diff --git a/.editorconfig b/.editorconfig index 5c189821..cfcc52ab 100644 --- a/.editorconfig +++ b/.editorconfig @@ -67,3 +67,9 @@ trim_trailing_whitespace = unset indent_style = unset indent_size = unset generated_code = true + +[/internal/events/**/*.schema.json] +insert_final_newline = unset + +[/pact-contracts/pacts/**/*.json] +insert_final_newline = unset diff --git a/.env.template b/.env.template new file mode 100644 index 00000000..380990a6 --- /dev/null +++ b/.env.template @@ -0,0 +1,5 @@ +ENVIRONMENT=$ENV_NAME +API_KEY= +HEADERAUTH= +PR_NUMBER=prxx # remove if needs to run against main +NHSD_APIM_TOKEN= diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 304adfcf..a8d42367 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -5,7 +5,10 @@ /.github/ @NHSDigital/nhs-notify-supplier-api-admins *.code-workspace @NHSDigital/nhs-notify-supplier-api-admins /docs/ @NHSDigital/nhs-notify-supplier-api -/infrastructure/terraform/ @NHSDigital/nhs-notify-platform +/infrastructure/terraform/ @NHSDigital/nhs-notify-platform @NHSDigital/nhs-notify-supplier-api-admins + +# Root level AGENTS.md owned by platform. +AGENTS.md @NHSDigital/nhs-notify-platform # Codeowners must be final check /.github/CODEOWNERS @NHSDigital/nhs-notify-code-owners diff --git a/.github/actions/acceptance-tests/action.yaml b/.github/actions/acceptance-tests/action.yaml new file mode 100644 index 00000000..bf6148cd --- /dev/null +++ b/.github/actions/acceptance-tests/action.yaml @@ -0,0 +1,51 @@ +name: Acceptance tests +description: "Run acceptance tests for this repo" + +inputs: + testType: + description: Type of test to run + required: true + + targetEnvironment: + description: Name of the environment under test + required: true + + targetAccountGroup: + description: Name of the account group under test + default: nhs-notify-template-management-dev + required: true + + targetComponent: + description: Name of the component under test + required: true + +runs: + using: "composite" + + steps: + - name: Fetch terraform output + uses: actions/download-artifact@v5 + with: + name: terraform-output-${{ inputs.targetComponent }} + + - name: Get Node version + id: nodejs_version + shell: bash + run: | + echo "nodejs_version=$(grep "^nodejs\s" .tool-versions | cut -f2 -d' ')" >> $GITHUB_OUTPUT + + - name: "Repo setup" + uses: ./.github/actions/node-install + with: + node-version: ${{ steps.nodejs_version.outputs.nodejs_version }} + GITHUB_TOKEN: ${{ env.GITHUB_TOKEN }} + + - name: "Set PR NUMBER" + shell: bash + run: | + echo "PR_NUMBER=${{ inputs.targetEnvironment }}" >> $GITHUB_ENV + + - name: Run test - ${{ inputs.testType }} + shell: bash + run: | + make test-${{ inputs.testType }} diff --git a/.github/actions/build-docs/action.yml b/.github/actions/build-docs/action.yml index 9bdd189c..8b887bf3 100644 --- a/.github/actions/build-docs/action.yml +++ b/.github/actions/build-docs/action.yml @@ -4,6 +4,9 @@ inputs: version: description: "Version number" required: true + NODE_AUTH_TOKEN: + description: "Token for access to github package registry" + required: true runs: using: "composite" steps: @@ -11,9 +14,12 @@ runs: uses: actions/checkout@v4 - uses: actions/setup-node@v4 with: - node-version: 18 + node-version: 22 + registry-url: 'https://npm.pkg.github.com' - name: Npm cli install working-directory: . + env: + NODE_AUTH_TOKEN: ${{ inputs.NODE_AUTH_TOKEN }} run: npm ci shell: bash - name: Setup Ruby diff --git a/.github/actions/build-libraries/action.yml b/.github/actions/build-libraries/action.yml index 32f340a6..14ac5231 100644 --- a/.github/actions/build-libraries/action.yml +++ b/.github/actions/build-libraries/action.yml @@ -4,6 +4,9 @@ inputs: version: description: "Version number" required: true + NODE_AUTH_TOKEN: + description: "Token for access to github package registry" + required: true runs: using: "composite" steps: @@ -11,10 +14,13 @@ runs: uses: actions/checkout@v4 - uses: actions/setup-node@v4 with: - node-version: 24 + node-version: 22 + registry-url: 'https://npm.pkg.github.com' - name: Npm install working-directory: . + env: + NODE_AUTH_TOKEN: ${{ inputs.NODE_AUTH_TOKEN }} run: npm ci shell: bash diff --git a/.github/actions/build-proxies/action.yml b/.github/actions/build-proxies/action.yml index aa516f44..5dcb872d 100644 --- a/.github/actions/build-proxies/action.yml +++ b/.github/actions/build-proxies/action.yml @@ -25,6 +25,12 @@ inputs: description: "Name of the Component to deploy" required: true default: 'api' + nodejs_version: + description: "Node.js version, set by the CI/CD pipeline workflow" + required: true + NODE_AUTH_TOKEN: + description: "Token for access to github package registry" + required: true runs: using: composite @@ -34,10 +40,22 @@ runs: uses: actions/checkout@v4 - uses: actions/setup-node@v4 with: - node-version: 24 + node-version: ${{ inputs.nodejs_version }} + registry-url: 'https://npm.pkg.github.com' + + - name: "Cache node_modules" + uses: actions/cache@v4 + with: + path: | + **/node_modules + key: ${{ runner.os }}-node-${{ inputs.nodejs_version }}-${{ hashFiles('**/package-lock.json') }} + restore-keys: | + ${{ runner.os }}-node-${{ inputs.nodejs_version }}- - name: Npm install working-directory: . + env: + NODE_AUTH_TOKEN: ${{ inputs.NODE_AUTH_TOKEN }} run: npm ci shell: bash diff --git a/.github/actions/build-sandbox/action.yml b/.github/actions/build-sandbox/action.yml index efdaec61..5bcef84c 100644 --- a/.github/actions/build-sandbox/action.yml +++ b/.github/actions/build-sandbox/action.yml @@ -4,6 +4,10 @@ inputs: version: description: "Version number" required: true + + NODE_AUTH_TOKEN: + description: "Token for access to github package registry" + required: true runs: using: composite @@ -12,10 +16,13 @@ runs: uses: actions/checkout@v4 - uses: actions/setup-node@v4 with: - node-version: 24 + node-version: 22 + registry-url: 'https://npm.pkg.github.com' - name: Npm install working-directory: . + env: + NODE_AUTH_TOKEN: ${{ inputs.NODE_AUTH_TOKEN }} run: npm ci shell: bash diff --git a/.github/actions/build-sdk/action.yml b/.github/actions/build-sdk/action.yml index 22d46521..1231b2c2 100644 --- a/.github/actions/build-sdk/action.yml +++ b/.github/actions/build-sdk/action.yml @@ -4,6 +4,9 @@ inputs: version: description: "Version number" required: true + NODE_AUTH_TOKEN: + description: "Token for access to github package registry" + required: true runs: using: "composite" steps: @@ -11,10 +14,13 @@ runs: uses: actions/checkout@v4 - uses: actions/setup-node@v4 with: - node-version: 18 + node-version: 22 + registry-url: 'https://npm.pkg.github.com' - name: Npm install working-directory: . + env: + NODE_AUTH_TOKEN: ${{ inputs.NODE_AUTH_TOKEN }} run: npm ci shell: bash diff --git a/.github/actions/build-server/action.yml b/.github/actions/build-server/action.yml index 5179f0f4..c077fa3b 100644 --- a/.github/actions/build-server/action.yml +++ b/.github/actions/build-server/action.yml @@ -4,6 +4,9 @@ inputs: version: description: "Version number" required: true + NODE_AUTH_TOKEN: + description: "Token for access to github package registry" + required: true runs: using: "composite" steps: @@ -11,10 +14,13 @@ runs: uses: actions/checkout@v4 - uses: actions/setup-node@v4 with: - node-version: 24 + node-version: 22 + registry-url: 'https://npm.pkg.github.com' - name: Npm install working-directory: . + env: + NODE_AUTH_TOKEN: ${{ inputs.NODE_AUTH_TOKEN }} run: npm ci shell: bash diff --git a/.github/actions/node-install/action.yaml b/.github/actions/node-install/action.yaml new file mode 100644 index 00000000..534ebc09 --- /dev/null +++ b/.github/actions/node-install/action.yaml @@ -0,0 +1,27 @@ +name: 'npm install and setup' +description: 'Setup node, authenticate github package repository and perform clean npm install' + +inputs: + node-version: + description: 'Node.js version' + required: true + GITHUB_TOKEN: + description: "Token for access to github package registry" + required: true + +runs: + using: 'composite' + steps: + - name: 'Use Node.js' + uses: actions/setup-node@v6 + with: + node-version: '${{ inputs.node-version }}' + registry-url: 'https://npm.pkg.github.com' + scope: '@nhsdigital' + + - name: 'Install dependencies' + shell: bash + env: + NODE_AUTH_TOKEN: ${{ inputs.GITHUB_TOKEN }} + run: | + npm ci diff --git a/.github/actions/test-types.json b/.github/actions/test-types.json new file mode 100644 index 00000000..5530c31c --- /dev/null +++ b/.github/actions/test-types.json @@ -0,0 +1,3 @@ +[ + "component" +] diff --git a/.github/workflows/manual-proxy-environment-deploy.yaml b/.github/workflows/manual-proxy-environment-deploy.yaml index 4232e7e0..c8ca20fe 100644 --- a/.github/workflows/manual-proxy-environment-deploy.yaml +++ b/.github/workflows/manual-proxy-environment-deploy.yaml @@ -21,6 +21,7 @@ on: permissions: contents: read + packages: read jobs: deploy-environment: @@ -32,10 +33,12 @@ jobs: - uses: actions/setup-node@v6 with: - node-version: 24 + node-version: 22 - name: Npm install working-directory: . + env: + NODE_AUTH_TOKEN: ${{ secrets.GITHUB_TOKEN }} run: npm ci shell: bash @@ -87,3 +90,4 @@ jobs: runId: "${{ github.run_id }}" buildSandbox: ${{ inputs.build_sandbox }} releaseVersion: ${{ github.ref_name }} + NODE_AUTH_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/pr_closed.yaml b/.github/workflows/pr_closed.yaml index 4cc942e9..003cf976 100644 --- a/.github/workflows/pr_closed.yaml +++ b/.github/workflows/pr_closed.yaml @@ -62,3 +62,98 @@ jobs: --targetAccountGroup "nhs-notify-supplier-api-dev" \ --targetComponent "${{ matrix.component }}" \ --terraformAction "apply" + + check-event-schemas-version-change: + name: Check for event schemas package version change + needs: check-merge-or-workflow-dispatch + if: needs.check-merge-or-workflow-dispatch.outputs.deploy == 'true' + outputs: + version_changed: ${{ steps.check-version.outputs.version_changed }} + runs-on: ubuntu-latest + permissions: + contents: read + packages: read + steps: + - name: Checkout code + uses: actions/checkout@v5.0.0 + + - name: Setup NodeJS + uses: actions/setup-node@v4 + with: + node-version: ${{ inputs.nodejs_version }} + registry-url: 'https://npm.pkg.github.com' + + - name: check if local version differs from latest published version + id: check-version + env: + NODE_AUTH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + published_version=$(npm view @nhsdigital/nhs-notify-event-schemas-supplier-api --json 2>/dev/null | jq -r '.["dist-tags"].latest // "null"') + echo "Published version: $published_version" + + local_version=$(jq -r '.version' internal/events/package.json) + echo "Local version: $local_version" + + if [[ $local_version = $published_version ]]; then + echo "Local version is the same as the latest published version - skipping publish" + echo "version_changed=false" >> $GITHUB_OUTPUT + else + echo "Local version is different to the latest published version - publishing new version" + echo "version_changed=true" >> $GITHUB_OUTPUT + fi + + test-contract: + name: "Test contracts (provider)" + needs: check-event-schemas-version-change + if: needs.check-event-schemas-version-change.outputs.version_changed == 'true' + runs-on: ubuntu-latest + permissions: + contents: read + packages: read + steps: + - name: "Checkout code" + uses: actions/checkout@v5.0.0 + - name: Setup NodeJS + uses: actions/setup-node@v4 + with: + node-version: ${{ inputs.nodejs_version }} + registry-url: 'https://npm.pkg.github.com' + - name: "Install dependencies" + env: + NODE_AUTH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: npm ci + - name: "Run provider contract tests" + run: make test-contract + env: + GITHUB_PACKAGES_TOKEN: ${{ secrets.GITHUB_TOKEN }} + + publish-event-schemas: + name: Publish event schemas package to GitHub package registry + needs: + - check-event-schemas-version-change + - test-contract + if: needs.check-event-schemas-version-change.outputs.version_changed == 'true' + runs-on: ubuntu-latest + permissions: + contents: read + packages: write + + steps: + - name: Checkout code + uses: actions/checkout@v5.0.0 + + - name: Setup NodeJS + uses: actions/setup-node@v4 + with: + node-version: ${{ inputs.nodejs_version }} + registry-url: 'https://npm.pkg.github.com' + + - name: Install dependencies + env: + NODE_AUTH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: npm ci + + - name: Publish to GitHub Packages + run: npm publish --workspace internal/events + env: + NODE_AUTH_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/pr_create_dynamic_env.yaml b/.github/workflows/pr_create_dynamic_env.yaml deleted file mode 100644 index 997ca082..00000000 --- a/.github/workflows/pr_create_dynamic_env.yaml +++ /dev/null @@ -1,37 +0,0 @@ -name: PR Create Environment - -on: - pull_request: - types: [labeled, opened, synchronize, reopened, unlabeled, edited] - -concurrency: - group: ${{ github.workflow }}-${{ github.ref }} - cancel-in-progress: false - -permissions: - id-token: write - contents: read - -jobs: - create-dynamic-environment: - name: Create Dynamic Environment - runs-on: ubuntu-latest - - steps: - - uses: actions/checkout@v5 - - name: Trigger dynamic environment creation - env: - APP_CLIENT_ID: ${{ secrets.APP_CLIENT_ID }} - APP_PEM_FILE: ${{ secrets.APP_PEM_FILE }} - shell: bash - run: | - .github/scripts/dispatch_internal_repo_workflow.sh \ - --infraRepoName "$(echo ${{ github.repository }} | cut -d'/' -f2)" \ - --releaseVersion "${GITHUB_HEAD_REF:-${GITHUB_REF#refs/heads/}}" \ - --targetWorkflow "dispatch-deploy-dynamic-env.yaml" \ - --targetEnvironment "pr${{ github.event.number }}" \ - --targetComponent "api" \ - --targetAccountGroup "nhs-notify-supplier-api-dev" \ - --terraformAction "apply" \ - --overrideProjectName "nhs" \ - --overrideRoleName "nhs-main-acct-supplier-api-github-deploy" diff --git a/.github/workflows/pr_destroy_dynamic_env.yaml b/.github/workflows/pr_destroy_dynamic_env.yaml index 343279cf..66eb1a2b 100644 --- a/.github/workflows/pr_destroy_dynamic_env.yaml +++ b/.github/workflows/pr_destroy_dynamic_env.yaml @@ -3,13 +3,19 @@ name: PR Destroy Environment on: pull_request: types: [closed] + branches: + - '*' concurrency: group: ${{ github.workflow }}-${{ github.ref }} cancel-in-progress: false +permissions: + id-token: write + contents: read + jobs: - create-dynamic-environment: + destroy-dynamic-environment: name: Destroy Dynamic Environment runs-on: ubuntu-latest @@ -32,3 +38,25 @@ jobs: --terraformAction "destroy" \ --overrideProjectName "nhs" \ --overrideRoleName "nhs-main-acct-supplier-api-github-deploy" + + destroy-dynamic-proxy: + name: Destroy Dynamic Proxy + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v5 + + - name: Trigger dynamic proxy destruction + env: + APP_CLIENT_ID: ${{ secrets.APP_CLIENT_ID }} + APP_PEM_FILE: ${{ secrets.APP_PEM_FILE }} + shell: bash + run: | + .github/scripts/dispatch_internal_repo_workflow.sh \ + --infraRepoName "nhs-notify-supplier-api" \ + --releaseVersion "main" \ + --targetComponent "api" \ + --targetWorkflow "proxy-destroy.yaml" \ + --targetEnvironment "pr${{ github.event.number }}" \ + --apimEnvironment "internal-dev-sandbox" \ + --boundedContext "notify-supplier" diff --git a/.github/workflows/scorecard.yml b/.github/workflows/scorecard.yml index ddaaca33..bd547a16 100644 --- a/.github/workflows/scorecard.yml +++ b/.github/workflows/scorecard.yml @@ -68,6 +68,6 @@ jobs: # Upload the results to GitHub's code scanning dashboard (optional). # Commenting out will disable upload of results to your repo's Code Scanning dashboard - name: "Upload to code-scanning" - uses: github/codeql-action/upload-sarif@4e94bd11f71e507f7f87df81788dff88d1dacbfb # v4.31.0 + uses: github/codeql-action/upload-sarif@0499de31b99561a6d14a36a5f662c2a54f91beee # v4.31.2 with: sarif_file: results.sarif diff --git a/.github/workflows/stage-1-commit.yaml b/.github/workflows/stage-1-commit.yaml index a44c8871..6063d464 100644 --- a/.github/workflows/stage-1-commit.yaml +++ b/.github/workflows/stage-1-commit.yaml @@ -152,9 +152,16 @@ jobs: timeout-minutes: 10 needs: detect-terraform-changes if: needs.detect-terraform-changes.outputs.terraform_changed == 'true' + env: + NODE_AUTH_TOKEN: ${{ secrets.GITHUB_TOKEN }} steps: - name: "Checkout code" uses: actions/checkout@v5 + - name: Setup NodeJS + uses: actions/setup-node@v4 + with: + node-version: ${{ inputs.nodejs_version }} + registry-url: 'https://npm.pkg.github.com' - name: "Setup ASDF" uses: asdf-vm/actions/setup@v4 - name: "Perform Setup" @@ -199,3 +206,106 @@ jobs: idp_aws_report_upload_region: "${{ secrets.IDP_AWS_REPORT_UPLOAD_REGION }}" idp_aws_report_upload_role_name: "${{ secrets.IDP_AWS_REPORT_UPLOAD_ROLE_NAME }}" idp_aws_report_upload_bucket_endpoint: "${{ secrets.IDP_AWS_REPORT_UPLOAD_BUCKET_ENDPOINT }}" + + detect-event-schema-package-changes: + name: "Check for changes to event schema package compared to main branch" + runs-on: ubuntu-latest + permissions: + contents: read + outputs: + changed: ${{ steps.check.outputs.changed }} + main_version: ${{ steps.check.outputs.main_version }} + + steps: + - name: "Checkout code" + uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Detect package changes and current version + id: check + run: | + git fetch origin main + + if git diff --quiet origin/main...HEAD -- internal/events; then + echo "No changes in event schemas package" + echo "changed=false" >> $GITHUB_OUTPUT + else + echo "Changes detected in event schemas" + echo "changed=true" >> $GITHUB_OUTPUT + fi + + if content=$(git show origin/main:internal/events/package.json 2>/dev/null); then + version=$(jq -r .version <<< $content); + else + version=null; + fi + + echo "Detected package version $version in main branch" + echo "main_version=$version" >> $GITHUB_OUTPUT + + check-schema-version-change: + name: Check event schema version has been updated + needs: detect-event-schema-package-changes + if: needs.detect-event-schema-package-changes.outputs.changed == 'true' + runs-on: ubuntu-latest + permissions: + contents: read + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Check schema versions + run: | + source scripts/is_valid_increment.sh + + main_version="${{ needs.detect-event-schema-package-changes.outputs.main_version }}" + echo "Main version: ${{ needs.detect-event-schema-package-changes.outputs.main_version }}" + + local_version=$(jq -r '.version' internal/events/package.json) + echo "Local version: $local_version" + + if ! is_valid_increment "$main_version" "$local_version" ; then + echo "Error: Event Schema package has changed, but new version ($local_version) is not a valid increment from latest version on main branch ($main_version)." + exit 1 + fi + + check-event-schemas-version-change: + name: Check for event schemas package version change + needs: detect-event-schema-package-changes + if: needs.detect-event-schema-package-changes.outputs.changed == 'true' + outputs: + version_changed: ${{ steps.check-version.outputs.version_changed }} + runs-on: ubuntu-latest + permissions: + contents: read + packages: read + steps: + - name: Checkout code + uses: actions/checkout@v5.0.0 + + - name: Setup NodeJS + uses: actions/setup-node@v4 + with: + node-version: ${{ inputs.nodejs_version }} + registry-url: 'https://npm.pkg.github.com' + + - name: check if local version differs from latest published version + id: check-version + env: + NODE_AUTH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + published_version=$(npm view @nhsdigital/nhs-notify-event-schemas-supplier-api --json 2>/dev/null | jq -r '.["dist-tags"].latest // "null"') + echo "Published version: $published_version" + + local_version=$(jq -r '.version' internal/events/package.json) + echo "Local version: $local_version" + + if [[ $local_version = $published_version ]]; then + echo "ERROR: Local version is the same as the latest published version, but event schemas have changed" + echo "version_changed=false" >> $GITHUB_OUTPUT + exit 1 + else + echo "Local version is different to the latest published version - a new version will be published" + echo "version_changed=true" >> $GITHUB_OUTPUT + fi diff --git a/.github/workflows/stage-2-test.yaml b/.github/workflows/stage-2-test.yaml index d7e25d52..3b03118a 100644 --- a/.github/workflows/stage-2-test.yaml +++ b/.github/workflows/stage-2-test.yaml @@ -39,6 +39,7 @@ env: permissions: id-token: write # This is required for requesting the JWT contents: read # This is required for actions/checkout + packages: read # This is required for downloading from GitHub Package Registry jobs: check-generated-dependencies: @@ -48,9 +49,19 @@ jobs: steps: - name: "Checkout code" uses: actions/checkout@v5 + - name: "Cache node_modules" + uses: actions/cache@v4 + with: + path: | + **/node_modules + key: ${{ runner.os }}-node-${{ inputs.nodejs_version }}-${{ hashFiles('**/package-lock.json') }} + restore-keys: | + ${{ runner.os }}-node-${{ inputs.nodejs_version }}- - name: "Repo setup" - run: | - npm ci + uses: ./.github/actions/node-install + with: + node-version: ${{ inputs.nodejs_version }} + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - name: "Generate dependencies" run: | npm run generate-dependencies --workspaces --if-present @@ -62,9 +73,19 @@ jobs: steps: - name: "Checkout code" uses: actions/checkout@v5 + - name: "Cache node_modules" + uses: actions/cache@v4 + with: + path: | + **/node_modules + key: ${{ runner.os }}-node-${{ inputs.nodejs_version }}-${{ hashFiles('**/package-lock.json') }} + restore-keys: | + ${{ runner.os }}-node-${{ inputs.nodejs_version }}- - name: "Repo setup" - run: | - npm ci + uses: ./.github/actions/node-install + with: + node-version: ${{ inputs.nodejs_version }} + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - name: "Generate dependencies" run: | npm run generate-dependencies --workspaces --if-present @@ -83,6 +104,37 @@ jobs: with: name: code-coverage-report path: ".reports/lcov.info" + test-pact: + name: "Pact tests" + runs-on: ubuntu-latest + timeout-minutes: 5 + env: + NODE_AUTH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + permissions: + packages: write + contents: read + steps: + - name: "Checkout code" + uses: actions/checkout@v5 + - name: "Cache node_modules" + uses: actions/cache@v4 + with: + path: | + **/node_modules + key: ${{ runner.os }}-node-${{ inputs.nodejs_version }}-${{ hashFiles('**/package-lock.json') }} + restore-keys: | + ${{ runner.os }}-node-${{ inputs.nodejs_version }}- + - name: "Repo setup" + uses: ./.github/actions/node-install + with: + node-version: ${{ inputs.nodejs_version }} + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + - name: "Generate dependencies" + run: npm run generate-dependencies --workspaces --if-present + - name: "Run PACT tests" + run: npm run test:pact --workspace tests + - name: Publish Pact Contracts + run: ./scripts/publish-pact-contracts.sh test-lint: name: "Linting" runs-on: ubuntu-latest @@ -90,9 +142,19 @@ jobs: steps: - name: "Checkout code" uses: actions/checkout@v5 + - name: "Cache node_modules" + uses: actions/cache@v4 + with: + path: | + **/node_modules + key: ${{ runner.os }}-node-${{ inputs.nodejs_version }}-${{ hashFiles('**/package-lock.json') }} + restore-keys: | + ${{ runner.os }}-node-${{ inputs.nodejs_version }}- - name: "Repo setup" - run: | - npm ci + uses: ./.github/actions/node-install + with: + node-version: ${{ inputs.nodejs_version }} + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - name: "Generate dependencies" run: | npm run generate-dependencies --workspaces --if-present @@ -106,9 +168,19 @@ jobs: steps: - name: "Checkout code" uses: actions/checkout@v5 + - name: "Cache node_modules" + uses: actions/cache@v4 + with: + path: | + **/node_modules + key: ${{ runner.os }}-node-${{ inputs.nodejs_version }}-${{ hashFiles('**/package-lock.json') }} + restore-keys: | + ${{ runner.os }}-node-${{ inputs.nodejs_version }}- - name: "Repo setup" - run: | - npm ci + uses: ./.github/actions/node-install + with: + node-version: ${{ inputs.nodejs_version }} + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - name: "Generate dependencies" run: | npm run generate-dependencies --workspaces --if-present @@ -143,7 +215,7 @@ jobs: with: fetch-depth: 0 # Full history is needed to improving relevancy of reporting - name: "Download coverage report for SONAR" - uses: actions/download-artifact@v5 + uses: actions/download-artifact@v6 with: name: code-coverage-report - name: "Perform static analysis" diff --git a/.github/workflows/stage-3-build.yaml b/.github/workflows/stage-3-build.yaml index 73d74f4c..474b9094 100644 --- a/.github/workflows/stage-3-build.yaml +++ b/.github/workflows/stage-3-build.yaml @@ -39,6 +39,8 @@ on: permissions: id-token: write # This is required for requesting the JWT contents: read # This is required for actions/checkout + packages: read # This is required for downloading from GitHub Package Registry + jobs: artefact-jekyll-docs: name: "Build Docs" @@ -51,6 +53,7 @@ jobs: uses: ./.github/actions/build-docs with: version: "${{ inputs.version }}" + NODE_AUTH_TOKEN: ${{ secrets.GITHUB_TOKEN }} artefact-sdks: name: "Build SDKs" @@ -63,6 +66,7 @@ jobs: uses: ./.github/actions/build-sdk with: version: "${{ inputs.version }}" + NODE_AUTH_TOKEN: ${{ secrets.GITHUB_TOKEN }} # Take out for now - might add again in the future # artefact-servers: @@ -87,10 +91,33 @@ jobs: # uses: ./.github/actions/build-libraries # with: # version: "${{ inputs.version }}" + pr-create-dynamic-environment: + name: Create Dynamic Environment + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v5 + - name: Trigger dynamic environment creation + env: + APP_CLIENT_ID: ${{ secrets.APP_CLIENT_ID }} + APP_PEM_FILE: ${{ secrets.APP_PEM_FILE }} + PR_NUMBER: ${{ inputs.pr_number }} + shell: bash + run: | + .github/scripts/dispatch_internal_repo_workflow.sh \ + --infraRepoName "$(echo ${{ github.repository }} | cut -d'/' -f2)" \ + --releaseVersion ${{ github.head_ref || github.ref_name }} \ + --targetWorkflow "dispatch-deploy-dynamic-env.yaml" \ + --targetEnvironment "pr${PR_NUMBER}" \ + --targetComponent "api" \ + --targetAccountGroup "nhs-notify-supplier-api-dev" \ + --terraformAction "apply" \ + --overrideProjectName "nhs" \ + --overrideRoleName "nhs-main-acct-supplier-api-github-deploy" artefact-proxies: name: "Build proxies" runs-on: ubuntu-latest + needs: [pr-create-dynamic-environment] timeout-minutes: 10 env: PROXYGEN_API_NAME: nhs-notify-supplier @@ -109,3 +136,5 @@ jobs: runId: "${{ github.run_id }}" buildSandbox: true releaseVersion: ${{ github.head_ref || github.ref_name }} + nodejs_version: ${{ inputs.nodejs_version }} + NODE_AUTH_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/stage-4-acceptance.yaml b/.github/workflows/stage-4-acceptance.yaml index ee42fca8..7bd64b23 100644 --- a/.github/workflows/stage-4-acceptance.yaml +++ b/.github/workflows/stage-4-acceptance.yaml @@ -35,139 +35,152 @@ on: required: true type: string +permissions: + id-token: write + contents: read + jobs: - environment-set-up: - name: "Environment set up" - runs-on: ubuntu-latest - timeout-minutes: 5 - steps: - - name: "Checkout code" - uses: actions/checkout@v5 - - name: "Create infractructure" - run: | - echo "Creating infractructure..." - - name: "Update database" - run: | - echo "Updating database..." - - name: "Deploy application" - run: | - echo "Deploying application..." - test-contract: - name: "Contract test" - runs-on: ubuntu-latest - needs: environment-set-up - timeout-minutes: 10 - steps: - - name: "Checkout code" - uses: actions/checkout@v5 - - name: "Run contract test" +# environment-set-up: +# name: "Environment set up" +# runs-on: ubuntu-latest +# timeout-minutes: 5 +# steps: +# - name: "Checkout code" +# uses: actions/checkout@v5 +# - name: "Create infractructure" +# run: | +# echo "Creating infractructure..." +# - name: "Update database" +# run: | +# echo "Updating database..." +# - name: "Deploy application" +# run: | +# echo "Deploying application..." + + - name: 'Use Node.js' + uses: actions/setup-node@v6 + with: + node-version: '${{ inputs.nodejs_version }}' + registry-url: 'https://npm.pkg.github.com' + scope: '@nhsdigital' + + - name: Trigger Acceptance Tests + shell: bash + env: + APP_PEM_FILE: ${{ secrets.APP_PEM_FILE }} + APP_CLIENT_ID: ${{ secrets.APP_CLIENT_ID }} + NODE_AUTH_TOKEN: ${{ secrets.GITHUB_TOKEN }} run: | make test-contract + env: + GITHUB_PACKAGES_TOKEN: ${{ secrets.GITHUB_TOKEN }} - name: "Save result" run: | echo "Nothing to save" - test-security: - name: "Security test" - runs-on: ubuntu-latest - needs: environment-set-up - timeout-minutes: 10 - steps: - - name: "Checkout code" - uses: actions/checkout@v5 - - name: "Run security test" - run: | - make test-security - - name: "Save result" - run: | - echo "Nothing to save" - test-ui: - name: "UI test" - runs-on: ubuntu-latest - needs: environment-set-up - timeout-minutes: 10 - steps: - - name: "Checkout code" - uses: actions/checkout@v5 - - name: "Run UI test" - run: | - make test-ui - - name: "Save result" - run: | - echo "Nothing to save" - test-ui-performance: - name: "UI performance test" - runs-on: ubuntu-latest - needs: environment-set-up - timeout-minutes: 10 - steps: - - name: "Checkout code" - uses: actions/checkout@v5 - - name: "Run UI performance test" - run: | - make test-ui-performance - - name: "Save result" - run: | - echo "Nothing to save" - test-integration: - name: "Integration test" - runs-on: ubuntu-latest - needs: environment-set-up - timeout-minutes: 10 - steps: - - name: "Checkout code" - uses: actions/checkout@v5 - - name: "Run integration test" - run: | - make test-integration - - name: "Save result" - run: | - echo "Nothing to save" - test-accessibility: - name: "Accessibility test" - runs-on: ubuntu-latest - needs: environment-set-up - timeout-minutes: 10 - steps: - - name: "Checkout code" - uses: actions/checkout@v5 - - name: "Run accessibility test" - run: | - make test-accessibility - - name: "Save result" - run: | - echo "Nothing to save" - test-load: - name: "Load test" - runs-on: ubuntu-latest - needs: environment-set-up - timeout-minutes: 10 - steps: - - name: "Checkout code" - uses: actions/checkout@v5 - - name: "Run load tests" - run: | - make test-load - - name: "Save result" - run: | - echo "Nothing to save" - environment-tear-down: - name: "Environment tear down" - runs-on: ubuntu-latest - needs: - [ - test-accessibility, - test-contract, - test-integration, - test-load, - test-security, - test-ui-performance, - test-ui, - ] - if: always() - timeout-minutes: 5 - steps: - - name: "Checkout code" - uses: actions/checkout@v5 - - name: "Tear down environment" - run: | - echo "Tearing down environment..." + +# Environment-based tests should run from notify-internal repo, not in public repo +# test-security: +# name: "Security test" +# runs-on: ubuntu-latest +# needs: environment-set-up +# timeout-minutes: 10 +# steps: +# - name: "Checkout code" +# uses: actions/checkout@v5 +# - name: "Run security test" +# run: | +# make test-security +# - name: "Save result" +# run: | +# echo "Nothing to save" +# test-ui: +# name: "UI test" +# runs-on: ubuntu-latest +# needs: environment-set-up +# timeout-minutes: 10 +# steps: +# - name: "Checkout code" +# uses: actions/checkout@v5 +# - name: "Run UI test" +# run: | +# make test-ui +# - name: "Save result" +# run: | +# echo "Nothing to save" +# test-ui-performance: +# name: "UI performance test" +# runs-on: ubuntu-latest +# needs: environment-set-up +# timeout-minutes: 10 +# steps: +# - name: "Checkout code" +# uses: actions/checkout@v5 +# - name: "Run UI performance test" +# run: | +# make test-ui-performance +# - name: "Save result" +# run: | +# echo "Nothing to save" +# test-integration: +# name: "Integration test" +# runs-on: ubuntu-latest +# needs: environment-set-up +# timeout-minutes: 10 +# steps: +# - name: "Checkout code" +# uses: actions/checkout@v5 +# - name: "Run integration test" +# run: | +# make test-integration +# - name: "Save result" +# run: | +# echo "Nothing to save" +# test-accessibility: +# name: "Accessibility test" +# runs-on: ubuntu-latest +# needs: environment-set-up +# timeout-minutes: 10 +# steps: +# - name: "Checkout code" +# uses: actions/checkout@v5 +# - name: "Run accessibility test" +# run: | +# make test-accessibility +# - name: "Save result" +# run: | +# echo "Nothing to save" +# test-load: +# name: "Load test" +# runs-on: ubuntu-latest +# needs: environment-set-up +# timeout-minutes: 10 +# steps: +# - name: "Checkout code" +# uses: actions/checkout@v5 +# - name: "Run load tests" +# run: | +# make test-load +# - name: "Save result" +# run: | +# echo "Nothing to save" +# environment-tear-down: +# name: "Environment tear down" +# runs-on: ubuntu-latest +# needs: +# [ +# test-accessibility, +# test-contract, +# test-integration, +# test-load, +# test-security, +# test-ui-performance, +# test-ui, +# ] +# if: always() +# timeout-minutes: 5 +# steps: +# - name: "Checkout code" +# uses: actions/checkout@v5 +# - name: "Tear down environment" +# run: | +# echo "Tearing down environment..." diff --git a/.github/workflows/stage-5-publish.yaml b/.github/workflows/stage-5-publish.yaml index 1c7eb36f..1bf1ac45 100644 --- a/.github/workflows/stage-5-publish.yaml +++ b/.github/workflows/stage-5-publish.yaml @@ -46,56 +46,56 @@ jobs: uses: actions/checkout@v5 - name: "Get the artefacts 1" - uses: actions/download-artifact@v5 + uses: actions/download-artifact@v6 with: path: ./artifacts/jekyll-docs-${{ inputs.version }} name: jekyll-docs-${{ inputs.version }} - name: "Get the artefacts 2" - uses: actions/download-artifact@v5 + uses: actions/download-artifact@v6 with: path: ./artifacts/sdk-html-docs-${{ inputs.version }} name: sdk-html-docs-${{ inputs.version }} - name: "Get the artefacts 3" - uses: actions/download-artifact@v5 + uses: actions/download-artifact@v6 with: path: ./artifacts/sdk-swagger-docs-${{ inputs.version }} name: sdk-swagger-docs-${{ inputs.version }} - name: "Get the artefacts 4" - uses: actions/download-artifact@v5 + uses: actions/download-artifact@v6 with: path: ./artifacts/sdk-html-${{ inputs.version }} name: sdk-html-${{ inputs.version }} - name: "Get the artefacts 5" - uses: actions/download-artifact@v5 + uses: actions/download-artifact@v6 with: path: ./artifacts/sdk-ts-${{ inputs.version }} name: sdk-ts-${{ inputs.version }} - name: "Get the artefacts 6" - uses: actions/download-artifact@v5 + uses: actions/download-artifact@v6 with: path: ./artifacts/sdk-python-${{ inputs.version }} name: sdk-python-${{ inputs.version }} - name: "Get the artefacts 7" - uses: actions/download-artifact@v5 + uses: actions/download-artifact@v6 with: path: ./artifacts/sdk-csharp-${{ inputs.version }} name: sdk-csharp-${{ inputs.version }} - name: "Get the artefacts 8" - uses: actions/download-artifact@v5 + uses: actions/download-artifact@v6 with: path: ./artifacts/api-oas-specification-${{ inputs.version }} name: api-oas-specification-${{ inputs.version }} # Take out for now - might add again in the future # - name: "Get the artefacts 9" - # uses: actions/download-artifact@v5 + # uses: actions/download-artifact@v6 # with: # path: ./artifacts/server-csharp-${{ inputs.version }} # name: server-csharp-${{ inputs.version }} @@ -144,7 +144,7 @@ jobs: asset_content_type: "application/gzip" - name: "zip html release asset" - # Git hub pages needs a single tar called artifact inside the zip. + # GitHub pages needs a single tar called artifact inside the zip. working-directory: ./artifacts/sdk-html-${{ inputs.version }} run: zip -r ../sdk-html-${{ inputs.version }}.zip . shell: bash @@ -160,7 +160,7 @@ jobs: asset_content_type: "application/gzip" - name: "zip sdk ts release asset" - # Git hub pages needs a single tar called artifact inside the zip. + # GitHub pages needs a single tar called artifact inside the zip. working-directory: ./artifacts/sdk-ts-${{ inputs.version }} run: zip -r ../sdk-ts-${{ inputs.version }}.zip . shell: bash @@ -176,7 +176,7 @@ jobs: asset_content_type: "application/gzip" - name: "zip sdk python release asset" - # Git hub pages needs a single tar called artifact inside the zip. + # GitHub pages needs a single tar called artifact inside the zip. working-directory: ./artifacts/sdk-python-${{ inputs.version }} run: zip -r ../sdk-python-${{ inputs.version }}.zip . shell: bash @@ -192,7 +192,7 @@ jobs: asset_content_type: "application/gzip" - name: "zip sdk csharp release asset" - # Git hub pages needs a single tar called artifact inside the zip. + # GitHub pages needs a single tar called artifact inside the zip. working-directory: ./artifacts/sdk-csharp-${{ inputs.version }} run: zip -r ../sdk-csharp-${{ inputs.version }}.zip . shell: bash @@ -208,7 +208,7 @@ jobs: asset_content_type: "application/gzip" - name: "zip api OAS specification release asset" - # Git hub pages needs a single tar called artifact inside the zip. + # GitHub pages needs a single tar called artifact inside the zip. working-directory: ./artifacts/api-oas-specification-${{ inputs.version }} run: zip -r ../api-oas-specification-${{ inputs.version }}.zip . shell: bash @@ -225,7 +225,7 @@ jobs: # Take out for now - might add again in the future # - name: "zip csharp server release asset" - # # Git hub pages needs a single tar called artifact inside the zip. + # # GitHub pages needs a single tar called artifact inside the zip. # working-directory: ./artifacts/server-csharp-${{ inputs.version }} # run: zip -r ../server-csharp-${{ inputs.version }}.zip . # shell: bash @@ -252,12 +252,12 @@ jobs: # contents: read # steps: # - name: "Get the artefacts csharp docker" - # uses: actions/download-artifact@v5 + # uses: actions/download-artifact@v6 # with: # path: . # name: server-csharp-docker-${{ inputs.version }} # - name: "Get the artefacts csharp server" - # uses: actions/download-artifact@v5 + # uses: actions/download-artifact@v6 # with: # path: ./csharp-server # name: server-csharp-${{ inputs.version }} @@ -279,7 +279,7 @@ jobs: contents: read steps: - name: "Get the artefacts" - uses: actions/download-artifact@v5 + uses: actions/download-artifact@v6 with: path: . name: sdk-csharp-${{ inputs.version }} @@ -335,7 +335,7 @@ jobs: contents: read steps: - name: "Get the artefacts" - uses: actions/download-artifact@v5 + uses: actions/download-artifact@v6 with: path: . name: sdk-ts-${{ inputs.version }} @@ -439,7 +439,7 @@ jobs: # contents: read # steps: # - name: "Get the artefacts" - # uses: actions/download-artifact@v5 + # uses: actions/download-artifact@v6 # with: # path: . # name: libs-letter-${{ inputs.version }} diff --git a/.gitignore b/.gitignore index b0b64b4a..3130b2da 100644 --- a/.gitignore +++ b/.gitignore @@ -13,6 +13,7 @@ version.json # Please, add your custom content below! .idea .env +.envrc # dependencies node_modules @@ -27,3 +28,11 @@ dist /sandbox/*.log /sandbox-staging /specification/api/components/examples +/specification/api/components/x-nhsd-apim/x-nhsd-apim.yml +/specification/api/components/security-schemes/security-schemes.yml +/specification/api/components/security/security.yml +/specification/api/components/parameters/authorization/authorization.yml +/scripts/JWT/*.pem + +# ignore PACTS +.pacts diff --git a/.gitleaksignore b/.gitleaksignore index 996b4388..59b814d9 100644 --- a/.gitleaksignore +++ b/.gitleaksignore @@ -16,3 +16,6 @@ b1f85a7faf54eaf66074d7a6daa093aefe6b3ebe:sdk/python/pyproject.toml:ipv4:25 493e54b6baa390529aab08d9fd956837f7bb3f30:src/src.sln:ipv4:3 d8aaf7e033bf78fff491caa148897be266b60f67:src/src.sln:ipv4:3 e12407e09151898bfd8d049d57eee9db9977d56b:.github/copilot-instructions.md:generic-api-key:213 +4ad86108d4e08cd410061e8842dd3a2b3bee4867:scripts/JWT/README.md:generic-api-key:38 +504844c9838740c8c5235024919f0775ad817cde:pact-contracts/pacts/letter-rendering/supplier-api-letter-request-prepared.json:generic-api-key:10 +82cf3b2e89ea24b97c4ffc09e618700fb1b0aff3:pact-contracts/pacts/letter-rendering/supplier-api-letter-request-prepared.json:generic-api-key:10 diff --git a/.npmrc b/.npmrc new file mode 100644 index 00000000..15cc4735 --- /dev/null +++ b/.npmrc @@ -0,0 +1 @@ +@nhsdigital:registry=https://npm.pkg.github.com diff --git a/.vscode/settings.json b/.vscode/settings.json index d7c02400..8ad0e3c2 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,5 +1,8 @@ { "autoOpenWorkspace.enableAutoOpenIfSingleWorkspace": true, + "cSpell.words": [ + "envrc" + ], "files.exclude": { "**/.DS_Store": true, "**/.git": true, diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 00000000..c85e1afd --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,95 @@ +# AGENTS.md + + +## Scope + +This file is for **AI agents** working within NHS Notify repositories. +Humans should read `README.md` and the docs for how we actually work day to day. +Keep anything language or tool-specific in nested `AGENTS.md` files (for example under `infrastructure/terraform` or `lambdas`). + +## Repository Layout (high level) + +At a glance, the main areas are: + +- `infrastructure/terraform/` – Terraform components, and shared modules for AWS accounts and environments. +- `lambdas/` – TypeScript lambda projects (each with their own `package.json`, Jest config, etc.). Root level packages.json defines workspaces and scripts. Tests for the lambda are stored in `lambdas/{name}/src/__test`. +- `src/` and `utils/` – Shared code and utilities (for example `utils/logger`). +- `docs/` – Documentation site, ADRs, RFCS, and other long‑form docs. +- `.github/workflows/` and `.github/actions/` – GitHub Actions workflows and composite actions. +- `scripts/` – Helper scripts and tooling used by humans and workflows. +- `tests/` – Cross‑cutting tests and harnesses for the repo. + +Agents should look for a nested `AGENTS.md` in or near these areas before making non‑trivial changes. + +## Root package.json – role and usage + +The root `package.json` is the orchestration manifestgit co for this repo. It does not ship application code; it wires up shared dev tooling and delegates to workspace-level projects. + +- Workspaces: Declares the set of npm workspaces (e.g. under `lambdas/`, `utils/`, `tests/`, `scripts/`). Agents should add a new workspace path here when introducing a new npm project. +- Scripts: Provides top-level commands that fan out across workspaces using `--workspaces` (lint, typecheck, unit tests) and project-specific runners (e.g. `lambda-build`). +- Dev tool dependencies: Centralises Jest, TypeScript, ESLint configurations and plugins to keep versions consistent across workspaces. Workspace projects should rely on these unless a local override is strictly needed. +- Overrides/resolutions: Pins transitive dependencies (e.g. Jest/react-is) to avoid ecosystem conflicts. Agents must not remove overrides without verifying tests across all workspaces. + +Agent guidance: + +- Before adding or removing a workspace, update the root `workspaces` array and ensure CI scripts still succeed with `npm run lint`, `npm run typecheck`, and `npm run test:unit` at the repo root. +- When adding repo-wide scripts, keep names consistent with existing patterns (e.g. `lint`, `lint:fix`, `typecheck`, `test:unit`, `lambda-build`) and prefer `--workspaces` fan-out. +- Do not publish from the root. If adding a new workspace intended for publication, mark that workspace package as `private: false` and keep the root as private. +- Validate changes by running the repo pre-commit hooks: `make githooks-run`. + +Success criteria for changes affecting the root `package.json`: + +- `npm run lint`, `npm run typecheck`, and `npm run test:unit` pass at the repo root. +- Workspace discovery is correct (new projects appear under `npm run typecheck --workspaces`). +- No regression in lambda build tooling (`npm run lambda-build`). + +## What Agents Can / Can’t Do + +Agents **can**: + +- Propose changes to code, tests, GitHub workflows, Terraform, and docs. +- Suggest new scripts, Make targets, or composite actions by copying existing patterns. +- Run tests to validate proposed solutions. + +Agents **must not**: + +- Create, push, or merge branches or PRs. +- Introduce new technologies, providers, or big architectural patterns without clearly calling out that an ADR is needed. +- Invent secrets or hard‑code real credentials anywhere. + +## Working With This Repo + +- All dependencies can be setup using the command `make config` from the repository root. +- **Don’t guess commands.** Derive them from what’s already here or ask for guidance form the human user: + - Prefer `Makefile` targets, `scripts/`, `.github/workflows/`, and `.github/actions/`. +- For Terraform, follow `infrastructure/terraform/{components,modules}` and respect `versions.tf`. +- Keep diffs small and focused. Avoid mixing refactors with behaviour changes unless you explain why. + +## Quality Expectations + +When proposing a change, agents should: + +- Keep code formatted and idiomatic (Terraform, TypeScript, Bash, YAML). +- Stick to existing patterns where available (for example `utils/logger`, composite actions under `.github/actions`). +- Use available information on best practices within the specific area of the codebase. +- **Always** run local pre-commit hooks from the repo root with: + + ```sh + pre-commit run \ + --config scripts/config/pre-commit.yaml + ``` + + to catch formatting and basic lint issues. Domain specific checks will be defined in appropriate nested AGENTS.md files. + +- Suggest at least one extra validation step (for example `npm test` in a lambda, or triggering a specific workflow). +- Any required follow up activites which fall outside of the current task's scope should be clearly marked with a 'TODO: CCM-12345' comment. The human user should be prompted to create and provide a JIRA ticket ID to be added to the comment. + +## Security & Safety + +- All agent-generated changes **must** be reviewed and merged by a human. +- Provide a concise, clear summary of the proposed changes to make human review easier (what changed, why (refer directly to the guidance in relevant Agents.MD files when applicable), and how it was validated). It should be directly pastable into the PR description and make it clear that AI assistance was used. +- Never output real secrets or tokens. Use placeholders and rely on the GitHub/AWS secrets already wired into workflows. + +## Escalation / Blockers + +If you are blocked by an unavailable secret, unclear architectural constraint, missing upstream module, or failing tooling you cannot safely fix, stop and ask a single clear clarifying question rather than guessing. diff --git a/Makefile b/Makefile index 7f5186ec..a85bbc83 100644 --- a/Makefile +++ b/Makefile @@ -30,9 +30,9 @@ clean:: # Clean-up project resources (main) @Operations # (cd src/server && make clean) guard-%: - @ if [ "${${*}}" = "" ]; then \ + @if [ -z "$${$*}" ]; then \ echo "Variable $* not set"; \ - echo "Usage: make APIM_ENV=" + echo "Usage: make $*="; \ exit 1; \ fi serve: @@ -45,33 +45,33 @@ publish-oas: $(MAKE) copy-examples npm run publish-oas -set-target: guard-APIM_ENV - @ TARGET=target-$$APIM_ENV.yml \ - envsubst '$${TARGET}' \ - < specification/api/components/x-nhsd-apim/target-template.yml > specification/api/components/x-nhsd-apim/target.yml +set-authorization: guard-APIM_ENV + SPEC_DIR=./specification/api/components/environments + COMPONENT_DIR=./specification/api/components/parameters/authorization + ./scripts/build/substitute_build_env.sh $$COMPONENT_DIR/authorization-template.yml $$SPEC_DIR/$$APIM_ENV.env $$COMPONENT_DIR/authorization.yml -set-access: guard-APIM_ENV - @ ACCESS=access-$$APIM_ENV.yml \ - envsubst '$${ACCESS}' \ - < specification/api/components/x-nhsd-apim/access-template.yml > specification/api/components/x-nhsd-apim/access.yml +set-nhsd-apim: guard-APIM_ENV + SPEC_DIR=./specification/api/components/environments + COMPONENT_DIR=./specification/api/components/x-nhsd-apim + ./scripts/build/substitute_build_env.sh $$COMPONENT_DIR/x-nhsd-apim-template.yml $$SPEC_DIR/$$APIM_ENV.env $$COMPONENT_DIR/x-nhsd-apim.yml set-security: guard-APIM_ENV - @ SECURITY=security-$$APIM_ENV.yml \ - envsubst '$${SECURITY}' \ - < specification/api/components/security/security-template.yml > specification/api/components/security/security.yml + SPEC_DIR=./specification/api/components/environments + COMPONENT_DIR=./specification/api/components/security + ./scripts/build/substitute_build_env.sh $$COMPONENT_DIR/security-template.yml $$SPEC_DIR/$$APIM_ENV.env $$COMPONENT_DIR/security.yml + COMPONENT_DIR=./specification/api/components/security-schemes + ./scripts/build/substitute_build_env.sh $$COMPONENT_DIR/security-schemes-template.yml $$SPEC_DIR/$$APIM_ENV.env $$COMPONENT_DIR/security-schemes.yml + construct-spec: guard-APIM_ENV - $(MAKE) set-target APIM_ENV=$$APIM_ENV - $(MAKE) set-access APIM_ENV=$$APIM_ENV + $(MAKE) set-nhsd-apim APIM_ENV=$$APIM_ENV + $(MAKE) set-authorization APIM_ENV=$$APIM_ENV $(MAKE) set-security APIM_ENV=$$APIM_ENV - - build-json-oas-spec: guard-APIM_ENV $(MAKE) construct-spec APIM_ENV=$$APIM_ENV $(MAKE) publish-oas - build-yml-oas-spec: guard-APIM_ENV $(MAKE) construct-spec APIM_ENV=$$APIM_ENV $(MAKE) bundle-oas @@ -94,7 +94,7 @@ serve-swagger: npm run serve-swagger-docs copy-examples: - cp -r ./sandbox/data/examples/. ./specification/api/components/examples + @scripts/build/copy-examples.sh config:: _install-dependencies version # Configure development environment (main) @Configuration npm install diff --git a/VERSION b/VERSION index 502578ed..ed1dc891 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -0.2.0-${yyyy}${mm}${dd}.${HH}${MM}${SS}+${hash} +1.0.1-${yyyy}${mm}${dd}.${HH}${MM}${SS}+${hash} diff --git a/docs/_config.yml b/docs/_config.yml index 821aec59..ba494fa7 100644 --- a/docs/_config.yml +++ b/docs/_config.yml @@ -75,7 +75,7 @@ mermaid: version: "10.9.1" aux_links: - "NHS Notify Supplier API Template on GitHub": + "NHS Notify Supplier API on GitHub": - "//github.com/NHSDigital/nhs-notify-supplier-api" aux_links_new_tab: false diff --git a/docs/assets/diagrams/phase1-design.drawio b/docs/assets/diagrams/phase1-design.drawio deleted file mode 100644 index 90c30849..00000000 --- a/docs/assets/diagrams/phase1-design.drawio +++ /dev/null @@ -1,181 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/docs/assets/diagrams/target-architecture.drawio b/docs/assets/diagrams/target-architecture.drawio index 7c7e68b9..55cf49a4 100644 --- a/docs/assets/diagrams/target-architecture.drawio +++ b/docs/assets/diagrams/target-architecture.drawio @@ -1,577 +1,482 @@ - - - + + + - - - - + + + + - - + + - - + + - - - - + + - - + + - - + + - + - - + + - - + + + + + + + - - + + + + + + + - + - + - - + + - - + + - - + + + + + - + - - - + + + + - - - - - + + - + - - + + - - - - + + - - + + - - + + - - + + - + + - + + - - - - - - - - - - - + + - - + + - + - - - - - - - + + - - + + - - - - - + + - - - - + - + - - - - - + + - - - - + + - + - + + + - - + + - + - - + + + + - - - - - + + - - - - + - - - - - - - - - - - + + - - + + - + - - + - - - - + + - - + + - - - - + + - + + + - - + + - - - + + - - - - - - + + - - - - - - - + + - - + + - + - - + + - - + + - - + + - - + + - - - - + + - - - - + + - - + + - - + + - - + + - - + + - - - - - - - + + - - - - - - - - - + - - + + - - + + - + - + + - - + + - - + + - - + + - - + + + + + - - + + + + + - + - + - + - - - - - - - - - - - - - - - - - + + - - + + - - + + - + - - - - - + + + + - + + + - - + - - - - - - - - - - - + + + - - + + - - + + - - + + - - + + - - + + - - + + - + - - + + - + - - - - + - - - + + - + - - - - - + + - - + + - - - - + - - + + + + - - + + + + + - - + + + + - - + + - + - - + + - - + + + + + + - - + + + + - - + + - + - - + - - - - - + + - - + + - - - - + - + - - + + - - - + + + - - - - + - + + - - + + - - + + - + + + + + + - - + + - - - - - - - - - - - - - - - + + + + - - + + + + + - - + + + + - - + + - - + + - + - - - - - - - - - - - - - - - diff --git a/docs/assets/diagrams/types.md b/docs/assets/diagrams/types.md index 08e72d59..cef0f110 100644 --- a/docs/assets/diagrams/types.md +++ b/docs/assets/diagrams/types.md @@ -1,4 +1,4 @@ -## Data Store Schemas +# Data Store Schemas This document contains the mermaid diagrams for the data store schemas used in the application. @@ -10,10 +10,10 @@ The schemas are generated from Zod definitions and provide a visual representati erDiagram Letter { string id - string status "enum: PENDING, ACCEPTED, REJECTED, PRINTED, ENCLOSED, CANCELLED, DISPATCHED, FAILED, RETURNED, DESTROYED, FORWARDED, DELIVERED" + string status "enum: PENDING, ACCEPTED, REJECTED, PRINTED, ENCLOSED, CANCELLED, DISPATCHED, FAILED, RETURNED, FORWARDED, DELIVERED" string specificationId string groupId - number reasonCode + string reasonCode string reasonText string supplierId string url "url" @@ -31,16 +31,27 @@ erDiagram erDiagram MI { string id - string supplierId "ref: Supplier" - string specificationId - string groupId string lineItem + string timestamp number quantity + string specificationId + string groupId number stockRemaining + string supplierId string createdAt string updatedAt + number ttl "min: -9007199254740991, max: 9007199254740991" } +``` + +## Supplier schema + +```mermaid +erDiagram Supplier { + string id + string name + string apimId + string status "enum: ENABLED, DISABLED" } - MI }o--|| Supplier : "supplierId" ``` diff --git a/docs/collections/_consumers/acceptance.md b/docs/collections/_consumers/acceptance.md index dc05cadd..2d38ae9f 100644 --- a/docs/collections/_consumers/acceptance.md +++ b/docs/collections/_consumers/acceptance.md @@ -35,15 +35,21 @@ NHS Notify will seed your Integration (INT) environment with a sample of 2,500 l The seeded dataset provides a mix of standard and exceptional cases to validate your system's ability to process, identify, and report on different letter types and outcomes. -The sample data will include: +We use the specification to define the format in which letters should be produced.  An Example of the full range of specifications is: - Standard letters (English) - Core test cases representing typical production letters in standard format and layout - Accessible format letters - Letters designed for accessibility (e.g., large print, braille, or audio) to ensure your system correctly identifies and routes alternative format requests -- Letters without PDFs - Records that reference missing or unavailable PDF files, used to test your system's error handling and reporting for incomplete data -- Incorrect letter specifications - Letters containing invalid or incomplete metadata to validate error detection and response handling -- Non-English letters – Right-to-Left (RTL) - Examples in languages that read from right to left (e.g., Arabic, Urdu) to test layout handling +- Letters without PDFs - Records that reference missing or unavailable PDF files, used to test your system’s error handling and reporting for incomplete data +- Incorrect letter specifications - Letters containing invalid or incomplete metadata  to validate error detection and response handling +- Non-English letters – Right-to-Left (RTL) -  Examples in languages that read from right to left (e.g., Arabic, Urdu) to test layout handling - Non-English letters – Left-to-Right (LTR) - Examples in other supported non-English languages +As part of the seeded test data will provide multiple specifications to simulate the above scenarios (Note: the linked PDF data may be a standard English test document): + +integration-specification-english +integration-specification-braille +integration-specification-arabic + ### Purpose of the Test Data This data allows you to: @@ -79,14 +85,14 @@ Validate that your system can functionally establish a secure, authenticated con | Criteria | Description | |---|---| -| **Steps** | 1. Authenticate with APIM
2. Send a request to GET /_status to confirm service availability
3. Call GET /letters to verify that the connection is working correctly
4. Confirm the API returns a successful response | +| **Steps** | 1. Authenticate with APIM
2. Send a successful request to GET /_status or any of the endpoints to confirm service availability
3. Call GET /letters to verify that the connection is working correctly
4. Confirm the API returns a successful response | | **Acceptance** | - API connection successfully established for both endpoints
- No authentication or connectivity errors observed. | | **Evidence** | Screenshot or API log showing successful responses from both endpoints | | **Business value** | Demonstrates that your organisation can connect securely to NHS Notify and that authentication is correctly implemented before processing any real letter data. Ensures compliance with NHS Digital security standards and protects patient information from unauthorised access. | ### AT2 - Receive and prepare letters for production -This test validates that your system can receive and prepare all allocated letters each day by successfully calling the Notify API, fetching multiple pages of letter requests and confirming that all letters are retrieved without omission or duplication. +This test validates that your system can receive and prepare all allocated letters each day by successfully calling the Notify API and confirming that all letters are retrieved without omission or duplication. **Business outcome:** @@ -112,22 +118,25 @@ Validate your system's ability to: | Criteria | Description | |---|---| | **Steps** | 1. Call GET /letters to query a list of letters that is ready to be printed. Parameter to use: limit = 2500
2. Record total count and confirm 2,500 unique letter IDs are retrieved. | -| **Acceptance** | - 2,500 unique letter IDs retrieved.
- No missing or duplicated IDs.
- All pages are fetched. | +| **Acceptance** | - 2,500 unique letter IDs retrieved.
- No missing or duplicated IDs.
- Letters are retrieved in a single call | | **Evidence** | API logs showing successful acknowledgement with total count of 2,500 unique letter IDs. | | **Business value** | Confirms your production systems can begin each print run with all allocated letters, ensuring no delays or missed communications. | -### AT3 - Retrieve letter list twice without duplicate processing +### AT3 - Process letter list twice + +This test validates that your system can process the same list of allocated letters multiple times without creating duplicate jobs, re-importing data or triggering re-prints. It is designed to prove that your integration correctly handles repeated API calls and that your processing logic is idempotent. -This test validates that your system can retrieve the same list of allocated letters multiple times via the Notify API without triggering duplicate processing or re-queuing. +Please note that the steps described below are part of the test scenario and they are not meant to describe a business workflow. You may demonstrate this behaviour in any suitable way, but the steps below describe our recommended approach. **Business outcome:** -Your system safely retrieves the daily list of allocated letters more than once and correctly identifies previously processed items, preventing any duplication in production. +Your system can safely process the same daily letter list more than once without generating duplicate imports, print jobs, or downstream updates. **Preconditions:** - Supplier's INT tenant is seeded with 2,500 letters - Supplier has valid API credentials and access to GET /letters endpoint in the integration (INT) environment - All letters are in PENDING status +- No status updates or acknowledgements performed between calls **Endpoints:** @@ -137,17 +146,16 @@ Your system safely retrieves the daily list of allocated letters more than once Demonstrate that your system can: -- Retrieve the full list of allocated letters repeatedly -- Recognise and ignore letters already retrieved -- Prevent duplicate print, acceptance, or downstream processing -- Maintain consistent ordering across repeated calls +- Process the full list of allocated letters more than once without duplication +- Detect previously processed letter IDs and safely ignore duplicates +- Maintain consistent data handling repeated calls | Criteria | Description | |---|---| -| **Steps** | 1. Call GET /letters?limit=2500 to retrieve the full list of allocated letters
2. Call GET /letters again immediately to retrieve the same list of letters
3. Compare the second list to the first
4. Confirm that:
 a) The same 2,500 unique IDs are returned.
 b) No new processing jobs are triggered for already seen letters
 c) Your system ignores duplicates. | +| **Steps** | 1. Call GET /letters?limit=2500 to retrieve the full list of allocated letters
2. Call GET /letters again immediately to retrieve the same list of letters without updating any statuses
3. Compare the second list to the first
4. Confirm that:
 a) The same 2,500 unique IDs are returned.
 b) No new processing jobs are triggered for already seen letters
 c) Your system ignores duplicates. | | **Acceptance** | - Each letter is processed once.
- Duplicate retrievals do not cause re-printing, re-queuing, or duplicate API updates. | | **Evidence** | - Retrieval logs showing both API calls and ID comparisons
- Processing logs showing no duplicate triggers
- Summary of total unique IDs vs. total retrieved records. | -| **Business value** | Confirms your system can safely retry or re-fetch letter lists without duplicate processing, ensuring data integrity and resilience to transient network or API errors. | +| **Business value** | Confirms your system is idempotent — repeated API calls do not cause duplication, ensuring operational stability and preventing wasted print runs. | ### AT4 – Acknowledge that you have accepted a list of letters @@ -171,7 +179,7 @@ Confirm that you can mark letters as ACCEPTED for production readiness. | Criteria | Description | |---|---| -| **Steps** | 1. Call GET /letters?limit= 1000
2. Send POST /letters request with a single bulk payload
3. Verify via GET /letters/{id} that status updated to ACCEPTED.
4. Fetch another page of letters via GET /letters?limit=1000 and ensure that the letters are no longer in the pending queue. | +| **Steps** | 1. Call GET /letters?limit= 1000
2. Send POST /letters request with a single bulk payload
3. Verify via GET /letters/{id} that status updated to ACCEPTED.
4. Call GET /letters?limit=1000 and ensure that the letters are no longer in the pending queue. | | **Acceptance** | - Targeted letters successfully updated from 'PENDING' status to 'ACCEPTED' status
- No letter is skipped
- API returns a successful response. | | **Evidence** | - Include before and after status
- Include total update count. | | **Business value** | Confirms that your system can inform NHS Notify of production readiness for each allocated letter. | @@ -242,6 +250,8 @@ Demonstrate that your system can record the completion of envelope-insertion and Validate that your system can accurately record the handover of letters to delivery partners. This ensures full traceability beyond internal processing, confirming that all letters leaving your system are properly marked as DISPATCHED. +Please note that the use of GET/ letters/{id} endpoint in this scenario is for verification and evidence purposes only and it is not part of your normal operational process. + **Business outcome:** Your system records the postal hand-off of each letter. @@ -264,7 +274,7 @@ Validate that your system promptly reports all dispatched letters by updating th | Criteria | Description | |---|---| -| **Steps** | 1. Identify letters with status 'ACCEPTED', 'PRINTED', 'ENCLOSED'
2. Send a PATCH /letters/{id} request to update targeted letters to DISPATCHED or send a POST /letters for a bulk update
3. Verify via GET /letters/{id} that status updated to DISPATCHED. | +| **Steps** | 1. Identify letters with status 'ACCEPTED', 'PRINTED', 'ENCLOSED'
2. Send a PATCH /letters/{id} request to update targeted letters to DISPATCHED or send a POST /letters for a bulk update
3. Verify via GET /letters/{id} that status updated to DISPATCHED (This step is for acceptance testing and evidence, not a production requirement) | | **Acceptance** | - Only letters currently in ACCEPTED, PRINTED, or ENCLOSED state are targeted
- All targeted letters are updated to DISPATCHED
- API returns a successful response. | | **Evidence** | API responses and before/after samples show correct transition. | | **Business value** | Demonstrates postal hand-off tracking. | @@ -389,8 +399,14 @@ Demonstrate that returned mail is correctly: This test confirms that your system can process cancellation requests received from NHS Notify via the NHS Notify team and correctly update the associated letters to CANCELLED, ensuring they are immediately excluded from production, print, and dispatch workflows. +Currently the business process if we need letters to be cancelled is that we will contact you and request for cancellation. You will need to confirm whether those letters can be cancelled and once you have completed the cancellation task, to update their status through the API. + +Please note, this test focuses on your ability to report cancellations through the API — not on performing the entire business cancellation workflow automatically. +In future, we expect to automate this process for closer technical integration. +For now, the test simply ensures you can mark letters as CANCELLED once you’ve completed your internal cancellation steps. + **Business outcome:** -Your system successfully handles cancellation notifications, removes the affected letters from the active production flow, and records a CANCELLED status. +You can receive manual cancellation instructions from NHS Notify, act on them in your system, and then inform NHS Notify via the API that the affected letters were successfully cancelled. **Preconditions:** @@ -419,15 +435,17 @@ Demonstrate that your system: | Criteria | Description | |---|---| -| **Steps** | 1. Get the letter that needs to be cancelled (list provided by NHS Notify)
2. Send a PATCH /letters/{id} request to update targeted letters to CANCELLED or send a POST /letters for a bulk update
3. Verify via GET /letters/{id} that status updated to CANCELLED. | +| **Steps** | 1. Select list of letter IDs to be cancelled
2. Send a PATCH /letters/{id} request to update targeted letters to CANCELLED or send a POST /letters for a bulk update
3. Verify via GET /letters/{id} that status updated to CANCELLED. | | **Acceptance** | - Only letters not yet dispatched or delivered may be cancelled
- All targeted letters are successfully updated to CANCELLED
- API returns a successful response. | -| **Evidence** | API audit trail and before/after validation samples. | -| **Business value** | Prevents unnecessary printing and protects data integrity. | +| **Evidence** | API audit trail and before/after validation samples.
For this test, the key evidence is that you can notify us once your internal cancellation process is complete and that the API correctly reflects those cancellations. The test is not about the cancellation business process itself. | +| **Business value** | Confirms that suppliers can reliably report letter cancellations to NHS Notify once they have been actioned internally, supporting accurate lifecycle tracking and preparing the foundation for a future automated cancellation integration. | ### AT12 – Handle failures This test verifies that your system can flag letters as FAILED when an unrecoverable error occurs during production — for example, when a PDF is corrupted, an address is invalid, the letter has > 5 pages — and that it reports this back to NHS Notify clearly with failure reasons and codes. +A failure refers to a letter that cannot complete its production lifecycle due to a technical or operational issue within your print, packaging, or forwarding process — for example, a corrupted PDF, invalid address, or file structure error. + **Business outcome:** Your system reliably records production failures by updating the letter status to FAILED, including a failure code, reason, and timestamp. This ensures that Notify is aware of any items that could not be completed and can take appropriate action or monitoring steps. @@ -465,10 +483,16 @@ Demonstrate that your system: | **Evidence** | API logs showing state transitions, timestamps. | | **Business value** | Provides transparent incident reporting by informing of the reason why the letter cannot be processed. | -### AT13 – Reject invalid letters +### AT13 – Reject invalid letters (Optional Status) This test verifies that your system can detect and reject malformed letters (i.e: unrecognised specification id, missing PDFs etc) before they reach production by updating their status to REJECTED and providing appropriate reason codes and audit details. +A rejection means that your system has determined a letter cannot enter the printing workflow at all (for example, it fails structural validation or business-rule checks). +Unlike failures (AT12), rejected letters are never printed, queued, or handled operationally. + +Please note that REJECTED is only a valid status immediately after PENDING. It indicates that a letter will not be processed further — you are not attempting to print, enclose, or dispatch it. +This test ensures you can accurately detect and report such letters before production starts. + **Business outcome:** Invalid, malformed, or non-compliant letters are accurately identified and rejected early in the workflow. The rejection is logged with detailed diagnostic information and reported to NHS Notify, ensuring no non-compliant content enters production or consumes supplier quotas. @@ -499,22 +523,23 @@ Demonstrate that your system correctly: | Criteria | Description | |---|---| | **Steps** | 1. Identify bad letter requests
2. Send a PATCH /letters/{id} request to update targeted letters to REJECTED or send a POST /letters for a bulk update
3. Verify via GET /letters/{id} that status updated to REJECTED. | -| **Acceptance** | - All targeted letters are successfully updated to REJECTED
- API response confirms successful updates
- Rejected code and rejected reason are updated. | +| **Acceptance** | - All targeted letters are successfully updated to REJECTED
- API response confirms successful updates
- Rejected code and rejected reason are updated
- No rejected letter proceeds to ACCEPTED or any production status| | **Evidence** | API logs showing state transitions, timestamps. | | **Business value** | Ensures non-compliant inputs don't enter production.
Ensure NHS Notify is informed when supplier quotas are exhausted | ### AT14 – Submit and reconcile management information (MI) +This test verifies that your system can submit accurate, complete MI that matches the letters you’ve processed and can reconcile this data with NHS Notify’s records. The goal is to confirm that your MI reporting is accurate, complete, and free from duplication or omissions. + **Business outcome:** -Your MI submissions accurately match the letters you've processed. +NHS Notify uses MI data for reconciliation, billing, and operational assurance. You must ensure each submission represents a unique data set and does not duplicate previous entries. + +A duplicate MI entry is identified when multiple submissions are received with the same combination of reporting date and specification reference for the same groupID. **Endpoints:** - POST /mi -**What this test proves:** -That you can submit MI data in the correct format and confirm it's recorded correctly. - | Criteria | Description | |---|---| | **Steps** | - Submit MI for a known number of processed letters. The MI should be grouped by specification and group ID
- Reconcile counts and timestamps with your local records. | @@ -522,13 +547,28 @@ That you can submit MI data in the correct format and confirm it's recorded corr | **Evidence** | MI payloads and responses. | | **Business value** | Shows that operational and billing data are aligned and complete. | +**Objectives**: + +- Submit MI for all processed letters accurately. +- Avoid submitting duplicate MI entries for the same supplier and reporting date. +- Reconcile MI data counts with your own internal production or dispatch logs. + ### AT15 – Performance testing - Retrieve letter list, letter specifications and PDFs -Validate that your system can efficiently retrieve a high volume of allocated letters, their specifications, and associated PDFs within strict performance limits. +Validate that your system can efficiently retrieve a high volume of allocated letters, their specifications, and associated PDFs within agreed performance limits. + +The purpose is to ensure your system performs reliably under expected daily load and can complete a full production data within an acceptable timeframe. **Business outcome:** Your system can retrieve in <=10 secs, 2,500 allocated letters, their specifications, and all associated PDFs. +The NHS Notify reference benchmark for this test is to complete the retrieval and download of 2,500 letters (including metadata and PDFs) within 10 seconds under normal test load. +This is our **_most rigorous performance target_**, used as a reference for optimal supplier performance. + +We recognise that not all suppliers will need or be able to meet this exact metric. +You will need to agree a realistic performance target with The Supplier API team , taking into account your infrastructure and print workflow. +The key requirement is that you can evidence consistent and reliable performance at the agreed level. + **Preconditions:** - Supplier's INT tenant seeded with 2,500 letters @@ -573,7 +613,23 @@ Once you pass this stage, we'll confirm your readiness for going live. ## Appendix -Possible Scenarios and Applicable Letter Statuses +### Letter Status Definitions + +| Status | Description | Initiated By | Mandatory/Optional +|---|---|---|---| +| PENDING | Initial state for all new letters. Indicates that the letter has been allocated to a supplier but not yet retrieved or accepted | NHS Notify | Mandatory Starting Status | +| REJECTED | Used when a supplier determines a letter cannot be processed Occurs immediately after PENDING , before any production begins | Supplier | Conditional | +| ACCEPTED | Letter has passed validation checks and is ready for production | Supplier | Mandatory (core workflow transition) | +| PRINTED | Letter has been physically printed and is awaiting enclosure or dispatch. | Supplier | Optional (if print tracking supported) | +| ENCLOSED | The printed letter and any relevant enclosures have been inserted into the mailing envelope | Supplier | Optional (if enclosure tracking supported) | +| DISPATCHED | Letter has left the supplier's facility and been handed over to a postal service | Supplier | Mandatory for all successfully sent letters | +| DELIVERED | Letter has been delivered to the patient | Supplier | Optional (depends on delivery reporting integration) | +| RETURNED | Letter was undeliverable or returned by the postal service | Supplier | Mandatory | +| FORWARDED | Letter requires re-direction to a specialist supplier (e.g. RNIB or another accessible-format partner) | Supplier | Conditional (for accessible-format workflows) | +| CANCELLED | Letter was cancelled following a request from the NHS Notify team | Supplier on Notify request | Conditional (only via NHS Notify instruction) | +| FAILED | Letter could not complete production due to an unrecoverable issue | Supplier | Conditional (on error detection) | + +### Possible Scenarios and Applicable Letter Statuses | Scenario | Description | Typical Status flow | |---|---|---| diff --git a/docs/collections/_consumers/getting-started.md b/docs/collections/_consumers/getting-started.md index f0e5435a..004a2479 100644 --- a/docs/collections/_consumers/getting-started.md +++ b/docs/collections/_consumers/getting-started.md @@ -30,7 +30,7 @@ If you have installed the local repository requirements or are utilising the dev ## Documentation -This documentation is maintained within the [/docs](/docs) folder of the repository and published to [GitHub pages](https://nhsdigital.github.io/nhs-notify-supplier-api/) on PR merge +This documentation is maintained within the [/docs](https://github.com/NHSDigital/nhs-notify-supplier-api/tree/main/docs) folder of the repository and published to [GitHub pages](https://nhsdigital.github.io/nhs-notify-supplier-api/) on PR merge ## SDK Assets @@ -54,7 +54,7 @@ It will provide limited, fixed responses and does not require authentication to There are two ways to access the sandbox: -- The public sandbox [https://sandbox.api.service.nhs.uk/notify-supplier](https://sandbox.api.service.nhs.uk/notify-supplier) +- The public sandbox [https://sandbox.api.service.nhs.uk/notify-supplier](https://sandbox.api.service.nhs.uk/nhs-notify-supplier) - Local Build You can build the sandbox locally by installing the repository requirements (or using the devcontainer) and running: diff --git a/docs/collections/_consumers/integration.md b/docs/collections/_consumers/integration.md index 14785e2e..fc75554b 100644 --- a/docs/collections/_consumers/integration.md +++ b/docs/collections/_consumers/integration.md @@ -34,7 +34,7 @@ You have 90 calendar days to complete onboarding activities and submit all requi --- -## Pre-onboarding & Conformance +## Pre-onboarding and Conformance Before you can begin technical onboarding, your organisation must: @@ -59,10 +59,8 @@ This triggers an event to make the letter available for you to retrieve. ### List of letters and downloads Retrieve a list of all available letters, including key details such as the letter ID and current status. -The list only contains the summary information - not the actual PDF files. -To access each full letter, download the PDF directly by making a request to the `/letters/{id}/data` endpoint. -This will redirect to a short-lived URL to download the file. -These links are temporary and expire after about one minute, so downloads should be completed right away or re-requested if the link has expired. +The list only contains the summary information — not the actual PDF files. +To access each full letter, download the PDF directly by making a request to the /the GET letters/{id}/data endpoint. This will redirect to a short-lived URL to download the file. These links are temporary and expire after about one minute, so downloads should be completed right away or re-requested if the link has expired. ### API access @@ -105,7 +103,7 @@ The mandatory statuses are: **ACCEPTED**, **REJECTED**, **FORWARDED**, **DISPATC - **CANCELLED:** The letter was cancelled following a request from the NHS Notify team **Optional statuses** - additional, non-mandatory updates that can provide greater operational insight. -The optional statuses are: **PRINTED**, **ENCLOSED**, **DELIVERED**, and **DESTROYED**. +The optional statuses are: **PRINTED**, **ENCLOSED**, and **DELIVERED**. These can be used if your internal workflow supports more granular reporting. - **PRINTED:** The letter has been printed. @@ -121,7 +119,6 @@ Refer to the Letter Status Lifecycle diagram below for the complete sequence of ## Prepare your integration You must prepare your integration before you can get access to the NHS Notify integration environment. -You’ll need technical or developer support to complete tasks in this step. ### To set up your APIM application @@ -146,7 +143,7 @@ How you build your integration to meet your needs is your responsibility. The API is defined using the [OpenAPI Specification (OAS)](https://spec.openapis.org/oas/latest.html) format. -You can find it on [GitHub](https://github.com/NHSDigital/nhs-notify-supplier-api/tree/main/specification/api) and on the [NHS England Developer Catalogue](https://digital.nhs.uk/developer/api-catalogue/Alphabet/N). +You can find it on [GitHub](https://github.com/NHSDigital/nhs-notify-supplier-api/tree/main/specification/api) and on the [NHS API Catalogue](https://digital.nhs.uk/developer/api-catalogue/nhs-notify-supplier). A Postman collection will also be available in [GitHub](https://github.com/NHSDigital/nhs-notify-supplier-api/tree/main/postman) to explore the requests within the API. @@ -206,7 +203,9 @@ You’ll need to submit both digital proofs (for content and layout verification Only once your proofs have been reviewed and approved will you receive confirmation that your system and outputs are ready to move forward into the live (production) environment. -Digital proof will need to be sent via e-mail using this address: and physical proof will need to be sent to: Mr Wayne Shirt (NHS Notify), 6th Floor, 7 & 8 Wellington Place, Leeds, West Yorkshire, LS1 4AP. +Digital proof will need to be sent via e-mail using this address: and physical proof will need to be sent to: + + Mr Wayne Shirt (NHS Notify), 6th Floor, 7 & 8 Wellington Place, Leeds, West Yorkshire, LS1 4AP. --- diff --git a/docs/collections/_developers/architecture-design/phase1-design.md b/docs/collections/_developers/architecture-design/phase1-design.md deleted file mode 100644 index 256ea12f..00000000 --- a/docs/collections/_developers/architecture-design/phase1-design.md +++ /dev/null @@ -1,13 +0,0 @@ ---- -title: Phase 1 Design -nav_order: 2 -has_children: false -has_toc: false -parent: Architecture & Design ---- - -The diagram below illustrates the phase 1 design of the Supplier API. This meets the MVP for the API functionality for receiving, issuing, and updating letters and other print media. - -This does not include the necessary changes to NHS Notify to allocate letters. - -{% drawio path="assets/diagrams/phase1-design.drawio" page_number=0 height=800 %} diff --git a/docs/package.json b/docs/package.json index d9d967f3..2f2d699b 100644 --- a/docs/package.json +++ b/docs/package.json @@ -1,7 +1,7 @@ { "author": "", "dependencies": { - "nhsuk-frontend": "^8.1.1" + "nhsuk-frontend": "^10.1.0" }, "description": "", "devDependencies": {}, @@ -14,7 +14,8 @@ "build": "JEKYLL_ENV=production bundle exec jekyll build --trace --config _config.yml,_config.version.yml", "debug": "JEKYLL_ENV=development BUNDLE_GEMFILE=Gemfile bundle exec jekyll serve --config _config.yml,_config.dev.yml,_config.version.yml --limit_posts 100 --trace", "generate-includes": "./generate-includes.sh", - "test:unit": "echo \"Documentation module has no unit tests\"" + "test:unit": "echo \"Documentation module has no unit tests\"", + "typecheck": "echo \"Documentation module has no typescript to typecheck\"" }, "version": "1.0.0" } diff --git a/docs/pages/index.md b/docs/pages/index.md index 0db75ab0..7bd40fa1 100644 --- a/docs/pages/index.md +++ b/docs/pages/index.md @@ -84,15 +84,15 @@ Released versions of the specification can be found within [latest releases](htt ## API Consumers -If you wish to integrate with the Supplier API refer to the [API Consumer](/consumers) pages and +If you wish to integrate with the Supplier API refer to the *API Consumer* pages and [Getting Started as an API Consumer](/consumers/getting-started) You may also find it helpful to refer to the [Specification](/specification) ## API Developers -For NHS Developers working on the Supplier API refer to the [API Developer](/developers) pages and -[Getting Started as an API Developer](/developers/getting-started.md) +For NHS Developers working on the Supplier API refer to the *API Developer* pages and +[Getting Started as an API Developer](/developers/getting-started) ### Contributing diff --git a/eslint.config.mjs b/eslint.config.mjs index 78955cab..7e4d1a87 100644 --- a/eslint.config.mjs +++ b/eslint.config.mjs @@ -12,7 +12,7 @@ import unicorn from 'eslint-plugin-unicorn'; import { defineConfig, globalIgnores } from 'eslint/config'; import js from '@eslint/js'; import html from 'eslint-plugin-html'; -import tseslint from '@typescript-eslint/parser'; +import tseslint from 'typescript-eslint'; // Replace plugin-only import with meta package that includes configs import sortDestructureKeys from 'eslint-plugin-sort-destructure-keys'; import { configs as airbnbConfigs, @@ -40,9 +40,15 @@ export default defineConfig([ '**/test-results', '**/playwright-report*', 'eslint.config.mjs', + // newly ignored generated/build artifacts + 'build/**', + 'sdk/**', + 'docs/_site/**', + 'docs/vendor/**', + 'docs/**/*.html', ]), - //imports + // imports importX.flatConfigs.recommended, { rules: { ...airbnbPlugins.importX.rules } }, @@ -66,21 +72,16 @@ export default defineConfig([ }, }, }, - - { - files: ['**/*.json'], - extends: [tseslint.configs.disableTypeChecked], - }, + { files: ['**/*.json'], extends: [tseslint.configs.disableTypeChecked] }, { settings: { 'import-x/resolver-next': [ eslintImportResolverTypescript.createTypeScriptImportResolver({ project: [ - 'frontend/tsconfig.json', 'lambdas/*/tsconfig.json', - 'tests/test-team/tsconfig.json', - 'utils/*/tsconfig.json', + 'tests/tsconfig.json', + 'internal/*/tsconfig.json', ], }), ], @@ -91,10 +92,7 @@ export default defineConfig([ rules: { '@typescript-eslint/no-unused-vars': [ 2, - { - argsIgnorePattern: '^_', - varsIgnorePattern: '^_', - }, + { argsIgnorePattern: '^_', varsIgnorePattern: '^_' }, ], '@typescript-eslint/consistent-type-definitions': 0, }, @@ -107,22 +105,13 @@ export default defineConfig([ 'unicorn/prevent-abbreviations': 0, 'unicorn/filename-case': [ 2, - { - case: 'kebabCase', - ignore: ['.tsx'], - }, + { case: 'kebabCase', ignore: ['.tsx'] }, ], 'unicorn/no-null': 0, 'unicorn/prefer-module': 0, 'unicorn/import-style': [ 2, - { - styles: { - path: { - named: true, - }, - }, - }, + { styles: { path: { named: true } } }, ], }, }, @@ -145,114 +134,107 @@ export default defineConfig([ // jsxA11y { files: ['**/*.{js,mjs,cjs,jsx,mjsx,ts,tsx,mtsx}'], - plugins: { - 'jsx-a11y': jsxA11y, - }, - languageOptions: { - parserOptions: { - ecmaFeatures: { - jsx: true, - }, - }, - }, + plugins: { 'jsx-a11y': jsxA11y }, + languageOptions: { parserOptions: { ecmaFeatures: { jsx: true } } }, }, // security security.configs.recommended, - // sonar sonarjs.configs.recommended, - // html - { - files: ['**/*.html'], - plugins: { html }, - }, - - // Next.js - ...compat.config({ - extends: ['next', 'next/core-web-vitals', 'next/typescript'], - settings: { - next: { - rootDir: 'frontend', - }, - }, - rules: { - // needed because next lint rules look for a pages directory - '@next/next/no-html-link-for-pages': 0, - }, - }), + { files: ['**/*.html'], plugins: { html } }, // json - { - files: ['**/*.json'], - ...json.configs['recommended'], - }, + { files: ['**/*.json'], ...json.configs['recommended'] }, // destructure sorting { name: 'eslint-plugin-sort-destructure-keys', - plugins: { - 'sort-destructure-keys': sortDestructureKeys, - }, - rules: { - 'sort-destructure-keys/sort-destructure-keys': 2, - }, + plugins: { 'sort-destructure-keys': sortDestructureKeys }, + rules: { 'sort-destructure-keys/sort-destructure-keys': 2 }, }, // imports { rules: { - 'sort-imports': [ - 2, - { - ignoreDeclarationSort: true, - }, - ], + 'sort-imports': [2, { ignoreDeclarationSort: true }], 'import-x/extensions': 0, }, }, { files: ['**/*.ts', '**/*.tsx'], - rules: { - 'import-x/no-unresolved': 0, // trust the typescript compiler to catch unresolved imports - }, + rules: { 'import-x/no-unresolved': 0 }, }, { - files: ['tests/test-team/**'], + files: ['tests/**'], rules: { 'import-x/no-extraneous-dependencies': [ 2, - { - devDependencies: true, - }, + { devDependencies: true }, ], }, }, { - files: ['**/utils/**', 'tests/test-team/**'], + files: ['**/utils/**', 'tests/**'], + rules: { 'import-x/prefer-default-export': 0 }, + }, + { + plugins: { 'no-relative-import-paths': noRelativeImportPaths }, + rules: { 'no-relative-import-paths/no-relative-import-paths': 2 }, + }, + { + files: ['scripts/**', '**/__test__/**/*.ts', '**/__tests__/**/*.ts'], rules: { - 'import-x/prefer-default-export': 0, + 'import-x/no-extraneous-dependencies': [ + 'error', + { devDependencies: true }, + ], }, }, + + // js parserOptions override to suppress project service warnings for loose JS files { - plugins: { - 'no-relative-import-paths': noRelativeImportPaths, + files: ['**/*.js', '**/*.cjs', '**/*.mjs'], + languageOptions: { + parserOptions: { + allowDefaultProject: true, + noWarnOnMultipleProjects: true, // suppress informational warning + }, }, + }, + // tests: relax package import restriction + { + files: ['**/__test__/**', '**/__tests__/**', '**/*.test.ts', '**/*.test.js'], rules: { - 'no-relative-import-paths/no-relative-import-paths': 2, + 'import-x/no-relative-packages': 0, + 'security/detect-non-literal-fs-filename': 0 }, }, + + // CLI tools { - files: ['scripts/**'], + files: ['**/cli/**'], rules: { - 'import-x/no-extraneous-dependencies': [ - 'error', - { devDependencies: true }, - ], + 'no-console': 0, + 'security/detect-non-literal-fs-filename': 0 }, }, + // No use before define relaxations + { + rules: { + "no-use-before-define": "off", + "@typescript-eslint/no-use-before-define": [ "error", {"functions": false}] + } + }, + + // Lambda specific relaxations + { + files: ['lambdas/**'], + rules: { 'import-x/prefer-default-export': 1 }, + }, + // misc rule overrides { rules: { @@ -260,7 +242,9 @@ export default defineConfig([ 'no-underscore-dangle': 0, 'no-await-in-loop': 0, 'no-plusplus': [2, { allowForLoopAfterthoughts: true }], - 'unicorn/prefer-top-level-await': 0, // top level await is not available in commonjs + 'unicorn/prefer-top-level-await': 0, + 'import-x/no-relative-packages': 0, + 'no-relative-import-paths/no-relative-import-paths': 0 }, }, ]); diff --git a/infrastructure/terraform/bin/terraform.sh b/infrastructure/terraform/bin/terraform.sh index b0791e5f..659b535c 100755 --- a/infrastructure/terraform/bin/terraform.sh +++ b/infrastructure/terraform/bin/terraform.sh @@ -599,7 +599,6 @@ readonly backend_config="terraform { region = \"${region}\" bucket = \"${bucket}\" key = \"${backend_key}\" - dynamodb_table = \"${bucket}\" use_lockfile = true } }"; diff --git a/infrastructure/terraform/components/api/README.md b/infrastructure/terraform/components/api/README.md index 1392cc33..db387d8b 100644 --- a/infrastructure/terraform/components/api/README.md +++ b/infrastructure/terraform/components/api/README.md @@ -11,10 +11,14 @@ No requirements. |------|-------------|------|---------|:--------:| | [aws\_account\_id](#input\_aws\_account\_id) | The AWS Account ID (numeric) | `string` | n/a | yes | | [ca\_pem\_filename](#input\_ca\_pem\_filename) | Filename for the CA truststore file within the s3 bucket | `string` | `null` | no | +| [commit\_id](#input\_commit\_id) | The commit to deploy. Must be in the tree for branch\_name | `string` | `"HEAD"` | no | | [component](#input\_component) | The variable encapsulating the name of this component | `string` | `"supapi"` | no | | [default\_tags](#input\_default\_tags) | A map of default tags to apply to all taggable resources within the component | `map(string)` | `{}` | no | +| [disable\_gateway\_execute\_endpoint](#input\_disable\_gateway\_execute\_endpoint) | Disable the execution endpoint for the API Gateway | `bool` | `true` | no | | [enable\_backups](#input\_enable\_backups) | Enable backups | `bool` | `false` | no | | [environment](#input\_environment) | The name of the tfscaffold environment | `string` | n/a | yes | +| [eventpub\_control\_plane\_bus\_arn](#input\_eventpub\_control\_plane\_bus\_arn) | ARN of the EventBridge control plane bus for eventpub | `string` | `""` | no | +| [eventpub\_data\_plane\_bus\_arn](#input\_eventpub\_data\_plane\_bus\_arn) | ARN of the EventBridge data plane bus for eventpub | `string` | `""` | no | | [force\_destroy](#input\_force\_destroy) | Flag to force deletion of S3 buckets | `bool` | `false` | no | | [force\_lambda\_code\_deploy](#input\_force\_lambda\_code\_deploy) | If the lambda package in s3 has the same commit id tag as the terraform build branch, the lambda will not update automatically. Set to True if making changes to Lambda code from on the same commit for example during development | `bool` | `false` | no | | [group](#input\_group) | The group variables are being inherited from (often synonmous with account short-name) | `string` | n/a | yes | @@ -32,22 +36,32 @@ No requirements. | Name | Source | Version | |------|--------|---------| -| [authorizer\_lambda](#module\_authorizer\_lambda) | https://github.com/NHSDigital/nhs-notify-shared-modules/releases/download/v2.0.24/terraform-lambda.zip | n/a | -| [domain\_truststore](#module\_domain\_truststore) | https://github.com/NHSDigital/nhs-notify-shared-modules/releases/download/v2.0.20/terraform-s3bucket.zip | n/a | -| [get\_letter](#module\_get\_letter) | https://github.com/NHSDigital/nhs-notify-shared-modules/releases/download/v2.0.24/terraform-lambda.zip | n/a | -| [get\_letter\_data](#module\_get\_letter\_data) | https://github.com/NHSDigital/nhs-notify-shared-modules/releases/download/v2.0.24/terraform-lambda.zip | n/a | -| [get\_letters](#module\_get\_letters) | https://github.com/NHSDigital/nhs-notify-shared-modules/releases/download/v2.0.24/terraform-lambda.zip | n/a | -| [kms](#module\_kms) | https://github.com/NHSDigital/nhs-notify-shared-modules/releases/download/v2.0.20/terraform-kms.zip | n/a | -| [logging\_bucket](#module\_logging\_bucket) | https://github.com/NHSDigital/nhs-notify-shared-modules/releases/download/v2.0.20/terraform-s3bucket.zip | n/a | -| [patch\_letter](#module\_patch\_letter) | https://github.com/NHSDigital/nhs-notify-shared-modules/releases/download/v2.0.24/terraform-lambda.zip | n/a | -| [post\_mi](#module\_post\_mi) | https://github.com/NHSDigital/nhs-notify-shared-modules/releases/download/v2.0.24/terraform-lambda.zip | n/a | -| [s3bucket\_test\_letters](#module\_s3bucket\_test\_letters) | https://github.com/NHSDigital/nhs-notify-shared-modules/releases/download/v2.0.20/terraform-s3bucket.zip | n/a | -| [supplier\_ssl](#module\_supplier\_ssl) | https://github.com/NHSDigital/nhs-notify-shared-modules/releases/download/v2.0.20/terraform-ssl.zip | n/a | +| [authorizer\_lambda](#module\_authorizer\_lambda) | https://github.com/NHSDigital/nhs-notify-shared-modules/releases/download/v2.0.26/terraform-lambda.zip | n/a | +| [domain\_truststore](#module\_domain\_truststore) | https://github.com/NHSDigital/nhs-notify-shared-modules/releases/download/v2.0.26/terraform-s3bucket.zip | n/a | +| [eventpub](#module\_eventpub) | https://github.com/NHSDigital/nhs-notify-shared-modules/releases/download/v2.0.26/terraform-eventpub.zip | n/a | +| [eventsub](#module\_eventsub) | ../../modules/eventsub | n/a | +| [get\_letter](#module\_get\_letter) | https://github.com/NHSDigital/nhs-notify-shared-modules/releases/download/v2.0.26/terraform-lambda.zip | n/a | +| [get\_letter\_data](#module\_get\_letter\_data) | https://github.com/NHSDigital/nhs-notify-shared-modules/releases/download/v2.0.26/terraform-lambda.zip | n/a | +| [get\_letters](#module\_get\_letters) | https://github.com/NHSDigital/nhs-notify-shared-modules/releases/download/v2.0.26/terraform-lambda.zip | n/a | +| [get\_status](#module\_get\_status) | https://github.com/NHSDigital/nhs-notify-shared-modules/releases/download/v2.0.24/terraform-lambda.zip | n/a | +| [kms](#module\_kms) | https://github.com/NHSDigital/nhs-notify-shared-modules/releases/download/v2.0.26/terraform-kms.zip | n/a | +| [letter\_status\_update](#module\_letter\_status\_update) | https://github.com/NHSDigital/nhs-notify-shared-modules/releases/download/v2.0.24/terraform-lambda.zip | n/a | +| [letter\_status\_updates\_queue](#module\_letter\_status\_updates\_queue) | https://github.com/NHSDigital/nhs-notify-shared-modules/releases/download/v2.0.24/terraform-sqs.zip | n/a | +| [letter\_updates\_transformer](#module\_letter\_updates\_transformer) | https://github.com/NHSDigital/nhs-notify-shared-modules/releases/download/v2.0.26/terraform-lambda.zip | n/a | +| [logging\_bucket](#module\_logging\_bucket) | https://github.com/NHSDigital/nhs-notify-shared-modules/releases/download/v2.0.26/terraform-s3bucket.zip | n/a | +| [patch\_letter](#module\_patch\_letter) | https://github.com/NHSDigital/nhs-notify-shared-modules/releases/download/v2.0.26/terraform-lambda.zip | n/a | +| [post\_letters](#module\_post\_letters) | https://github.com/NHSDigital/nhs-notify-shared-modules/releases/download/v2.0.24/terraform-lambda.zip | n/a | +| [post\_mi](#module\_post\_mi) | https://github.com/NHSDigital/nhs-notify-shared-modules/releases/download/v2.0.26/terraform-lambda.zip | n/a | +| [s3bucket\_test\_letters](#module\_s3bucket\_test\_letters) | https://github.com/NHSDigital/nhs-notify-shared-modules/releases/download/v2.0.26/terraform-s3bucket.zip | n/a | +| [sqs\_letter\_updates](#module\_sqs\_letter\_updates) | https://github.com/NHSDigital/nhs-notify-shared-modules/releases/download/v2.0.26/terraform-sqs.zip | n/a | +| [supplier\_ssl](#module\_supplier\_ssl) | https://github.com/NHSDigital/nhs-notify-shared-modules/releases/download/v2.0.26/terraform-ssl.zip | n/a | +| [upsert\_letter](#module\_upsert\_letter) | https://github.com/NHSDigital/nhs-notify-shared-modules/releases/download/v2.0.26/terraform-lambda.zip | n/a | ## Outputs | Name | Description | |------|-------------| | [api\_urll](#output\_api\_urll) | n/a | +| [deployment](#output\_deployment) | Deployment details used for post-deployment scripts | diff --git a/infrastructure/terraform/components/api/api_gateway_rest_api.tf b/infrastructure/terraform/components/api/api_gateway_rest_api.tf index 349e4b98..aadbc0be 100644 --- a/infrastructure/terraform/components/api/api_gateway_rest_api.tf +++ b/infrastructure/terraform/components/api/api_gateway_rest_api.tf @@ -2,5 +2,5 @@ resource "aws_api_gateway_rest_api" "main" { name = local.csi body = local.openapi_spec description = "Suppliers API" - disable_execute_api_endpoint = true + disable_execute_api_endpoint = var.disable_gateway_execute_endpoint } diff --git a/infrastructure/terraform/components/api/cloudwatch_metric_alarm_apim_auth_cert_expirty.tf b/infrastructure/terraform/components/api/cloudwatch_metric_alarm_apim_auth_cert_expirty.tf new file mode 100644 index 00000000..f7e957f5 --- /dev/null +++ b/infrastructure/terraform/components/api/cloudwatch_metric_alarm_apim_auth_cert_expirty.tf @@ -0,0 +1,22 @@ +resource "aws_cloudwatch_metric_alarm" "alm-apim-client-certificate-near-expiry" { + alarm_name = "${local.csi}-alm-apim-client-certificate-near-expiry" + alarm_description = "RELIABILITY: An APIM client certificate is due to expire soon" + + metric_name = "apim-client-certificate-near-expiry" + namespace = "comms-apim-authorizer" + + dimensions = { + Environment = var.environment + } + + period = 60 * 60 * 4 //4 hours + comparison_operator = "GreaterThanThreshold" + threshold = "0" + evaluation_periods = "1" + statistic = "Sum" + treat_missing_data = "notBreaching" + + actions_enabled = "false" + alarm_actions = [] + ok_actions = [] +} diff --git a/infrastructure/terraform/components/api/ddb_table_suppliers.tf b/infrastructure/terraform/components/api/ddb_table_suppliers.tf new file mode 100644 index 00000000..24a79fb6 --- /dev/null +++ b/infrastructure/terraform/components/api/ddb_table_suppliers.tf @@ -0,0 +1,34 @@ +resource "aws_dynamodb_table" "suppliers" { + name = "${local.csi}-suppliers" + billing_mode = "PAY_PER_REQUEST" + + hash_key = "id" + range_key = "apimId" + + ttl { + attribute_name = "ttl" + enabled = false + } + + global_secondary_index { + name = "supplier-apim-index" + hash_key = "apimId" + projection_type = "ALL" + } + + attribute { + name = "id" + type = "S" + } + + attribute { + name = "apimId" + type = "S" + } + + point_in_time_recovery { + enabled = true + } + + tags = var.default_tags +} diff --git a/infrastructure/terraform/components/api/event_source_mapping_letter_updates.tf b/infrastructure/terraform/components/api/event_source_mapping_letter_updates.tf new file mode 100644 index 00000000..219b01ad --- /dev/null +++ b/infrastructure/terraform/components/api/event_source_mapping_letter_updates.tf @@ -0,0 +1,11 @@ +resource "aws_lambda_event_source_mapping" "letter_updates_transformer_kinesis" { + event_source_arn = aws_kinesis_stream.letter_change_stream.arn + function_name = module.letter_updates_transformer.function_arn + starting_position = "LATEST" + batch_size = 10 + maximum_batching_window_in_seconds = 1 + + depends_on = [ + module.letter_updates_transformer # ensures updates transformer exists + ] +} diff --git a/infrastructure/terraform/components/api/event_source_mapping_status_updates_to_handler.tf b/infrastructure/terraform/components/api/event_source_mapping_status_updates_to_handler.tf new file mode 100644 index 00000000..ab3634c4 --- /dev/null +++ b/infrastructure/terraform/components/api/event_source_mapping_status_updates_to_handler.tf @@ -0,0 +1,12 @@ +resource "aws_lambda_event_source_mapping" "status_updates_sqs_to_status_update_handler" { + event_source_arn = module.letter_status_updates_queue.sqs_queue_arn + function_name = module.letter_status_update.function_arn + batch_size = 10 + maximum_batching_window_in_seconds = 1 + scaling_config { maximum_concurrency = 10 } + + depends_on = [ + module.letter_status_updates_queue, # ensures queue exists + module.letter_status_update # ensures update handler exists + ] +} diff --git a/infrastructure/terraform/components/api/iam_role_api_gateway_execution_role.tf b/infrastructure/terraform/components/api/iam_role_api_gateway_execution_role.tf index 2e90b260..8acebc8b 100644 --- a/infrastructure/terraform/components/api/iam_role_api_gateway_execution_role.tf +++ b/infrastructure/terraform/components/api/iam_role_api_gateway_execution_role.tf @@ -53,6 +53,8 @@ data "aws_iam_policy_document" "api_gateway_execution_policy" { module.get_letter_data.function_arn, module.get_letters.function_arn, module.patch_letter.function_arn, + module.post_letters.function_arn, + module.get_status.function_arn, module.post_mi.function_arn ] } diff --git a/infrastructure/terraform/components/api/kinesis_letter_change_stream.tf b/infrastructure/terraform/components/api/kinesis_letter_change_stream.tf new file mode 100644 index 00000000..8a6d9d58 --- /dev/null +++ b/infrastructure/terraform/components/api/kinesis_letter_change_stream.tf @@ -0,0 +1,11 @@ +resource "aws_kinesis_stream" "letter_change_stream" { + name = "${local.csi}-letter-change-stream" + shard_count = 1 + retention_period = 24 +} + +resource "aws_dynamodb_kinesis_streaming_destination" "letter_streaming_destination" { + stream_arn = aws_kinesis_stream.letter_change_stream.arn + table_name = aws_dynamodb_table.letters.name + approximate_creation_date_time_precision = "MILLISECOND" +} diff --git a/infrastructure/terraform/components/api/lambda_event_source_mapping_upsert_letter.tf b/infrastructure/terraform/components/api/lambda_event_source_mapping_upsert_letter.tf new file mode 100644 index 00000000..a592ea9e --- /dev/null +++ b/infrastructure/terraform/components/api/lambda_event_source_mapping_upsert_letter.tf @@ -0,0 +1,9 @@ +resource "aws_lambda_event_source_mapping" "upsert_letter" { + event_source_arn = module.sqs_letter_updates.sqs_queue_arn + function_name = module.upsert_letter.function_name + batch_size = 10 + maximum_batching_window_in_seconds = 5 + function_response_types = [ + "ReportBatchItemFailures" + ] +} diff --git a/infrastructure/terraform/components/api/locals.tf b/infrastructure/terraform/components/api/locals.tf index 513e8da1..bfadfd30 100644 --- a/infrastructure/terraform/components/api/locals.tf +++ b/infrastructure/terraform/components/api/locals.tf @@ -5,14 +5,16 @@ locals { root_domain_nameservers = local.acct.route53_zone_nameservers["supplier-api"] openapi_spec = templatefile("${path.module}/resources/spec.tmpl.json", { - APIG_EXECUTION_ROLE_ARN = aws_iam_role.api_gateway_execution_role.arn - AWS_REGION = var.region - AUTHORIZER_LAMBDA_ARN = module.authorizer_lambda.function_arn - GET_LETTER_LAMBDA_ARN = module.get_letter.function_arn - GET_LETTERS_LAMBDA_ARN = module.get_letters.function_arn - GET_LETTER_DATA_LAMBDA_ARN = module.get_letter_data.function_arn - PATCH_LETTER_LAMBDA_ARN = module.patch_letter.function_arn - POST_MI_LAMBDA_ARN = module.post_mi.function_arn + APIG_EXECUTION_ROLE_ARN = aws_iam_role.api_gateway_execution_role.arn + AWS_REGION = var.region + AUTHORIZER_LAMBDA_ARN = module.authorizer_lambda.function_arn + GET_LETTER_LAMBDA_ARN = module.get_letter.function_arn + GET_LETTERS_LAMBDA_ARN = module.get_letters.function_arn + GET_LETTER_DATA_LAMBDA_ARN = module.get_letter_data.function_arn + GET_STATUS_LAMBDA_ARN = module.get_status.function_arn + PATCH_LETTER_LAMBDA_ARN = module.patch_letter.function_arn + POST_LETTERS_LAMBDA_ARN = module.post_letters.function_arn + POST_MI_LAMBDA_ARN = module.post_mi.function_arn }) destination_arn = "arn:aws:logs:${var.region}:${var.shared_infra_account_id}:destination:nhs-main-obs-firehose-logs" @@ -21,7 +23,7 @@ locals { LETTERS_TABLE_NAME = aws_dynamodb_table.letters.name, MI_TABLE_NAME = aws_dynamodb_table.mi.name, LETTER_TTL_HOURS = 12960, # 18 months * 30 days * 24 hours - MI_TTL_HOURS = 2160 # 90 days * 24 hours + MI_TTL_HOURS = 2160 # 90 days * 24 hours SUPPLIER_ID_HEADER = "nhsd-supplier-id", APIM_CORRELATION_HEADER = "nhsd-correlation-id", DOWNLOAD_URL_TTL_SECONDS = 60 diff --git a/infrastructure/terraform/components/api/module_authorizer_lambda.tf b/infrastructure/terraform/components/api/module_authorizer_lambda.tf index 9d56c950..a3ab42a0 100644 --- a/infrastructure/terraform/components/api/module_authorizer_lambda.tf +++ b/infrastructure/terraform/components/api/module_authorizer_lambda.tf @@ -1,5 +1,5 @@ module "authorizer_lambda" { - source = "https://github.com/NHSDigital/nhs-notify-shared-modules/releases/download/v2.0.24/terraform-lambda.zip" + source = "https://github.com/NHSDigital/nhs-notify-shared-modules/releases/download/v2.0.26/terraform-lambda.zip" aws_account_id = var.aws_account_id component = var.component @@ -11,6 +11,10 @@ module "authorizer_lambda" { log_retention_in_days = var.log_retention_in_days kms_key_arn = module.kms.key_arn + iam_policy_document = { + body = data.aws_iam_policy_document.authorizer_lambda.json + } + function_name = "authorizer" description = "Authorizer for Suppliers API" @@ -30,4 +34,40 @@ module "authorizer_lambda" { send_to_firehose = true log_destination_arn = local.destination_arn log_subscription_role_arn = local.acct.log_subscription_role_arn + + lambda_env_vars = { + CLOUDWATCH_NAMESPACE = "/aws/api-gateway/supplier/alarms", + CLIENT_CERTIFICATE_EXPIRATION_ALERT_DAYS = 14, + APIM_SUPPLIER_ID_HEADER = "NHSD-Supplier-ID", + SUPPLIERS_TABLE_NAME = aws_dynamodb_table.suppliers.name + } +} + +data "aws_iam_policy_document" "authorizer_lambda" { + statement { + sid = "AllowPutMetricData" + effect = "Allow" + + actions = [ + "cloudwatch:PutMetricData" + ] + + resources = [ + "*" + ] + } + + statement { + sid = "AllowDynamoDBAccess" + effect = "Allow" + + actions = [ + "dynamodb:Query" + ] + + resources = [ + aws_dynamodb_table.suppliers.arn, + "${aws_dynamodb_table.suppliers.arn}/index/supplier-apim-index" + ] + } } diff --git a/infrastructure/terraform/components/api/module_domain_truststore.tf b/infrastructure/terraform/components/api/module_domain_truststore.tf index 75ba5d44..cd15c58d 100644 --- a/infrastructure/terraform/components/api/module_domain_truststore.tf +++ b/infrastructure/terraform/components/api/module_domain_truststore.tf @@ -1,5 +1,5 @@ module "domain_truststore" { - source = "https://github.com/NHSDigital/nhs-notify-shared-modules/releases/download/v2.0.20/terraform-s3bucket.zip" + source = "https://github.com/NHSDigital/nhs-notify-shared-modules/releases/download/v2.0.26/terraform-s3bucket.zip" name = "truststore" aws_account_id = var.aws_account_id diff --git a/infrastructure/terraform/components/api/module_kms.tf b/infrastructure/terraform/components/api/module_kms.tf index f899fd94..3b3e47c4 100644 --- a/infrastructure/terraform/components/api/module_kms.tf +++ b/infrastructure/terraform/components/api/module_kms.tf @@ -1,5 +1,5 @@ module "kms" { - source = "https://github.com/NHSDigital/nhs-notify-shared-modules/releases/download/v2.0.20/terraform-kms.zip" + source = "https://github.com/NHSDigital/nhs-notify-shared-modules/releases/download/v2.0.26/terraform-kms.zip" providers = { aws = aws @@ -31,6 +31,7 @@ data "aws_iam_policy_document" "kms" { type = "Service" identifiers = [ + "sns.amazonaws.com", "logs.${var.region}.amazonaws.com", ] } @@ -46,4 +47,24 @@ data "aws_iam_policy_document" "kms" { "*", ] } + + statement { + sid = "AllowEventsFromSharedInfraAccount" + effect = "Allow" + + principals { + type = "AWS" + identifiers = ["arn:aws:iam::${var.shared_infra_account_id}:root"] + } + + actions = [ + "kms:Encrypt", + "kms:Decrypt", + "kms:GenerateDataKey" + ] + + resources = [ + "*", + ] + } } diff --git a/infrastructure/terraform/components/api/module_lambda_get_letter.tf b/infrastructure/terraform/components/api/module_lambda_get_letter.tf index a92a6051..8b311ad0 100644 --- a/infrastructure/terraform/components/api/module_lambda_get_letter.tf +++ b/infrastructure/terraform/components/api/module_lambda_get_letter.tf @@ -1,5 +1,5 @@ module "get_letter" { - source = "https://github.com/NHSDigital/nhs-notify-shared-modules/releases/download/v2.0.24/terraform-lambda.zip" + source = "https://github.com/NHSDigital/nhs-notify-shared-modules/releases/download/v2.0.26/terraform-lambda.zip" function_name = "get_letter" description = "Get letter status" @@ -25,7 +25,7 @@ module "get_letter" { handler_function_name = "getLetter" runtime = "nodejs22.x" memory = 128 - timeout = 5 + timeout = 29 log_level = var.log_level force_lambda_code_deploy = var.force_lambda_code_deploy diff --git a/infrastructure/terraform/components/api/module_lambda_get_letter_data.tf b/infrastructure/terraform/components/api/module_lambda_get_letter_data.tf index 5bf0597d..a1ac8ef7 100644 --- a/infrastructure/terraform/components/api/module_lambda_get_letter_data.tf +++ b/infrastructure/terraform/components/api/module_lambda_get_letter_data.tf @@ -1,5 +1,5 @@ module "get_letter_data" { - source = "https://github.com/NHSDigital/nhs-notify-shared-modules/releases/download/v2.0.24/terraform-lambda.zip" + source = "https://github.com/NHSDigital/nhs-notify-shared-modules/releases/download/v2.0.26/terraform-lambda.zip" function_name = "get_letter_data" description = "Get the letter data" @@ -25,7 +25,7 @@ module "get_letter_data" { handler_function_name = "getLetterData" runtime = "nodejs22.x" memory = 128 - timeout = 5 + timeout = 29 log_level = var.log_level force_lambda_code_deploy = var.force_lambda_code_deploy @@ -69,10 +69,10 @@ data "aws_iam_policy_document" "get_letter_data_lambda" { } statement { - sid = "S3GetObjectForPresign" - actions = [ + sid = "S3GetObjectForPresign" + actions = [ "s3:GetObject", - "s3:ListBucket"] # allows 404 response instead of 403 if object missing + "s3:ListBucket"] # allows 404 response instead of 403 if object missing resources = ["${module.s3bucket_test_letters.arn}/*"] } } diff --git a/infrastructure/terraform/components/api/module_lambda_get_letters.tf b/infrastructure/terraform/components/api/module_lambda_get_letters.tf index fa2369c7..318734ad 100644 --- a/infrastructure/terraform/components/api/module_lambda_get_letters.tf +++ b/infrastructure/terraform/components/api/module_lambda_get_letters.tf @@ -1,5 +1,5 @@ module "get_letters" { - source = "https://github.com/NHSDigital/nhs-notify-shared-modules/releases/download/v2.0.24/terraform-lambda.zip" + source = "https://github.com/NHSDigital/nhs-notify-shared-modules/releases/download/v2.0.26/terraform-lambda.zip" function_name = "get_letters" description = "Get paginated letter ids" @@ -25,7 +25,7 @@ module "get_letters" { handler_function_name = "getLetters" runtime = "nodejs22.x" memory = 128 - timeout = 5 + timeout = 29 log_level = var.log_level force_lambda_code_deploy = var.force_lambda_code_deploy diff --git a/infrastructure/terraform/components/api/module_lambda_get_status.tf b/infrastructure/terraform/components/api/module_lambda_get_status.tf new file mode 100644 index 00000000..c0d166ca --- /dev/null +++ b/infrastructure/terraform/components/api/module_lambda_get_status.tf @@ -0,0 +1,76 @@ +module "get_status" { + source = "https://github.com/NHSDigital/nhs-notify-shared-modules/releases/download/v2.0.24/terraform-lambda.zip" + + function_name = "get_status" + description = "Healthcheck for service" + + aws_account_id = var.aws_account_id + component = var.component + environment = var.environment + project = var.project + region = var.region + group = var.group + + log_retention_in_days = var.log_retention_in_days + kms_key_arn = module.kms.key_arn + + iam_policy_document = { + body = data.aws_iam_policy_document.get_status_lambda.json + } + + function_s3_bucket = local.acct.s3_buckets["lambda_function_artefacts"]["id"] + function_code_base_path = local.aws_lambda_functions_dir_path + function_code_dir = "api-handler/dist" + function_include_common = true + handler_function_name = "getStatus" + runtime = "nodejs22.x" + memory = 128 + timeout = 29 + log_level = var.log_level + + force_lambda_code_deploy = var.force_lambda_code_deploy + enable_lambda_insights = false + + send_to_firehose = true + log_destination_arn = local.destination_arn + log_subscription_role_arn = local.acct.log_subscription_role_arn + + lambda_env_vars = merge(local.common_lambda_env_vars, {}) +} + +data "aws_iam_policy_document" "get_status_lambda" { + statement { + sid = "KMSPermissions" + effect = "Allow" + + actions = [ + "kms:Decrypt", + "kms:GenerateDataKey", + ] + + resources = [ + module.kms.key_arn, ## Requires shared kms module + ] + } + + statement { + sid = "AllowDynamoDBAccess" + effect = "Allow" + + actions = [ + "dynamodb:DescribeTable" + ] + + resources = [ + aws_dynamodb_table.letters.arn, + "${aws_dynamodb_table.letters.arn}/index/supplierStatus-index" + ] + } + + + statement { + sid = "S3ListAllMyBuckets" + actions = ["s3:ListAllMyBuckets"] + resources = ["arn:aws:s3:::*"] + } +} diff --git a/infrastructure/terraform/components/api/module_lambda_letter_status_update.tf b/infrastructure/terraform/components/api/module_lambda_letter_status_update.tf new file mode 100644 index 00000000..7539d1a6 --- /dev/null +++ b/infrastructure/terraform/components/api/module_lambda_letter_status_update.tf @@ -0,0 +1,86 @@ +module "letter_status_update" { + source = "https://github.com/NHSDigital/nhs-notify-shared-modules/releases/download/v2.0.24/terraform-lambda.zip" + + function_name = "letter_status_update" + description = "Processes letter status updates" + + aws_account_id = var.aws_account_id + component = var.component + environment = var.environment + project = var.project + region = var.region + group = var.group + + log_retention_in_days = var.log_retention_in_days + kms_key_arn = module.kms.key_arn + + iam_policy_document = { + body = data.aws_iam_policy_document.letter_status_update.json + } + + function_s3_bucket = local.acct.s3_buckets["lambda_function_artefacts"]["id"] + function_code_base_path = local.aws_lambda_functions_dir_path + function_code_dir = "api-handler/dist" + function_include_common = true + handler_function_name = "letterStatusUpdate" + runtime = "nodejs22.x" + memory = 128 + timeout = 29 + log_level = var.log_level + + force_lambda_code_deploy = var.force_lambda_code_deploy + enable_lambda_insights = false + + send_to_firehose = true + log_destination_arn = local.destination_arn + log_subscription_role_arn = local.acct.log_subscription_role_arn + + lambda_env_vars = merge(local.common_lambda_env_vars, {}) +} + +data "aws_iam_policy_document" "letter_status_update" { + statement { + sid = "KMSPermissions" + effect = "Allow" + + actions = [ + "kms:Decrypt", + "kms:GenerateDataKey", + ] + + resources = [ + module.kms.key_arn, ## Requires shared kms module + ] + } + + statement { + sid = "AllowDynamoDBAccess" + effect = "Allow" + + actions = [ + "dynamodb:GetItem", + "dynamodb:Query", + "dynamodb:UpdateItem", + ] + + resources = [ + aws_dynamodb_table.letters.arn, + ] + } + + statement { + sid = "AllowQueueAccess" + effect = "Allow" + + actions = [ + "sqs:ReceiveMessage", + "sqs:DeleteMessage", + "sqs:GetQueueAttributes", + "sqs:ChangeMessageVisibility" + ] + + resources = [ + module.letter_status_updates_queue.sqs_queue_arn + ] + } +} diff --git a/infrastructure/terraform/components/api/module_lambda_letter_updates_transformer.tf b/infrastructure/terraform/components/api/module_lambda_letter_updates_transformer.tf new file mode 100644 index 00000000..7cf6853c --- /dev/null +++ b/infrastructure/terraform/components/api/module_lambda_letter_updates_transformer.tf @@ -0,0 +1,74 @@ +module "letter_updates_transformer" { + source = "https://github.com/NHSDigital/nhs-notify-shared-modules/releases/download/v2.0.26/terraform-lambda.zip" + + function_name = "letter-updates-transformer" + description = "Letter Update Filter/Producer" + + aws_account_id = var.aws_account_id + component = var.component + environment = var.environment + project = var.project + region = var.region + group = var.group + + log_retention_in_days = var.log_retention_in_days + kms_key_arn = module.kms.key_arn + + iam_policy_document = { + body = data.aws_iam_policy_document.letter_updates_transformer_lambda.json + } + + function_s3_bucket = local.acct.s3_buckets["lambda_function_artefacts"]["id"] + function_code_base_path = local.aws_lambda_functions_dir_path + function_code_dir = "letter-updates-transformer/dist" + function_include_common = true + handler_function_name = "handler" + runtime = "nodejs22.x" + memory = 128 + timeout = 29 + log_level = var.log_level + + force_lambda_code_deploy = var.force_lambda_code_deploy + enable_lambda_insights = false + + send_to_firehose = true + log_destination_arn = local.destination_arn + log_subscription_role_arn = local.acct.log_subscription_role_arn + + lambda_env_vars = merge(local.common_lambda_env_vars, { + EVENTPUB_SNS_TOPIC_ARN = "${module.eventpub.sns_topic.arn}" + }) +} + +data "aws_iam_policy_document" "letter_updates_transformer_lambda" { + statement { + sid = "AllowSNSPublish" + effect = "Allow" + + actions = [ + "sns:Publish" + ] + + resources = [ + module.eventpub.sns_topic.arn + ] + } + + statement { + sid = "AllowKinesisGet" + effect = "Allow" + + actions = [ + "kinesis:GetRecords", + "kinesis:GetShardIterator", + "kinesis:DescribeStream", + "kinesis:DescribeStreamSummary", + "kinesis:ListShards", + "kinesis:ListStreams", + ] + + resources = [ + aws_kinesis_stream.letter_change_stream.arn + ] + } +} diff --git a/infrastructure/terraform/components/api/module_lambda_patch_letter.tf b/infrastructure/terraform/components/api/module_lambda_patch_letter.tf index 942335b5..dfc496e9 100644 --- a/infrastructure/terraform/components/api/module_lambda_patch_letter.tf +++ b/infrastructure/terraform/components/api/module_lambda_patch_letter.tf @@ -1,5 +1,5 @@ module "patch_letter" { - source = "https://github.com/NHSDigital/nhs-notify-shared-modules/releases/download/v2.0.24/terraform-lambda.zip" + source = "https://github.com/NHSDigital/nhs-notify-shared-modules/releases/download/v2.0.26/terraform-lambda.zip" function_name = "patch_letter" description = "Update the status of a letter" @@ -25,7 +25,7 @@ module "patch_letter" { handler_function_name = "patchLetter" runtime = "nodejs22.x" memory = 128 - timeout = 5 + timeout = 29 log_level = var.log_level force_lambda_code_deploy = var.force_lambda_code_deploy @@ -35,7 +35,9 @@ module "patch_letter" { log_destination_arn = local.destination_arn log_subscription_role_arn = local.acct.log_subscription_role_arn - lambda_env_vars = merge(local.common_lambda_env_vars, {}) + lambda_env_vars = merge(local.common_lambda_env_vars, { + QUEUE_URL = module.letter_status_updates_queue.sqs_queue_url + }) } data "aws_iam_policy_document" "patch_letter_lambda" { @@ -54,21 +56,16 @@ data "aws_iam_policy_document" "patch_letter_lambda" { } statement { - sid = "AllowDynamoDBAccess" + sid = "AllowQueueAccess" effect = "Allow" actions = [ - "dynamodb:BatchGetItem", - "dynamodb:BatchWriteItem", - "dynamodb:GetItem", - "dynamodb:PutItem", - "dynamodb:Query", - "dynamodb:Scan", - "dynamodb:UpdateItem", + "sqs:SendMessage", + "sqs:GetQueueAttributes", ] resources = [ - aws_dynamodb_table.letters.arn, + module.letter_status_updates_queue.sqs_queue_arn ] } } diff --git a/infrastructure/terraform/components/api/module_lambda_post_letters.tf b/infrastructure/terraform/components/api/module_lambda_post_letters.tf new file mode 100644 index 00000000..8d60266f --- /dev/null +++ b/infrastructure/terraform/components/api/module_lambda_post_letters.tf @@ -0,0 +1,72 @@ +module "post_letters" { + source = "https://github.com/NHSDigital/nhs-notify-shared-modules/releases/download/v2.0.24/terraform-lambda.zip" + + function_name = "post_letters" + description = "Receives and accepts collection of letters to update" + + aws_account_id = var.aws_account_id + component = var.component + environment = var.environment + project = var.project + region = var.region + group = var.group + + log_retention_in_days = var.log_retention_in_days + kms_key_arn = module.kms.key_arn + + iam_policy_document = { + body = data.aws_iam_policy_document.post_letters.json + } + + function_s3_bucket = local.acct.s3_buckets["lambda_function_artefacts"]["id"] + function_code_base_path = local.aws_lambda_functions_dir_path + function_code_dir = "api-handler/dist" + function_include_common = true + handler_function_name = "postLetters" + runtime = "nodejs22.x" + memory = 128 + timeout = 29 + log_level = var.log_level + + force_lambda_code_deploy = var.force_lambda_code_deploy + enable_lambda_insights = false + + send_to_firehose = true + log_destination_arn = local.destination_arn + log_subscription_role_arn = local.acct.log_subscription_role_arn + + lambda_env_vars = merge(local.common_lambda_env_vars, { + QUEUE_URL = module.letter_status_updates_queue.sqs_queue_url, + MAX_LIMIT = var.max_get_limit + }) +} + +data "aws_iam_policy_document" "post_letters" { + statement { + sid = "KMSPermissions" + effect = "Allow" + + actions = [ + "kms:Decrypt", + "kms:GenerateDataKey", + ] + + resources = [ + module.kms.key_arn, ## Requires shared kms module + ] + } + + statement { + sid = "AllowQueueAccess" + effect = "Allow" + + actions = [ + "sqs:SendMessage", + "sqs:GetQueueAttributes", + ] + + resources = [ + module.letter_status_updates_queue.sqs_queue_arn + ] + } +} diff --git a/infrastructure/terraform/components/api/module_lambda_post_mi.tf b/infrastructure/terraform/components/api/module_lambda_post_mi.tf index 9398a3cf..f1d631fd 100644 --- a/infrastructure/terraform/components/api/module_lambda_post_mi.tf +++ b/infrastructure/terraform/components/api/module_lambda_post_mi.tf @@ -1,5 +1,5 @@ module "post_mi" { - source = "https://github.com/NHSDigital/nhs-notify-shared-modules/releases/download/v2.0.24/terraform-lambda.zip" + source = "https://github.com/NHSDigital/nhs-notify-shared-modules/releases/download/v2.0.26/terraform-lambda.zip" function_name = "post_mi" description = "Add management information" @@ -25,7 +25,7 @@ module "post_mi" { handler_function_name = "postMI" runtime = "nodejs22.x" memory = 128 - timeout = 5 + timeout = 29 log_level = var.log_level force_lambda_code_deploy = var.force_lambda_code_deploy diff --git a/infrastructure/terraform/components/api/module_lambda_upsert_letter.tf b/infrastructure/terraform/components/api/module_lambda_upsert_letter.tf new file mode 100644 index 00000000..65f5be98 --- /dev/null +++ b/infrastructure/terraform/components/api/module_lambda_upsert_letter.tf @@ -0,0 +1,84 @@ +module "upsert_letter" { + source = "https://github.com/NHSDigital/nhs-notify-shared-modules/releases/download/v2.0.26/terraform-lambda.zip" + + function_name = "upsert-letter" + description = "Update or Insert the letter data in the letters table" + + aws_account_id = var.aws_account_id + component = var.component + environment = var.environment + project = var.project + region = var.region + group = var.group + + log_retention_in_days = var.log_retention_in_days + kms_key_arn = module.kms.key_arn + + iam_policy_document = { + body = data.aws_iam_policy_document.upsert_letter_lambda.json + } + + function_s3_bucket = local.acct.s3_buckets["lambda_function_artefacts"]["id"] + function_code_base_path = local.aws_lambda_functions_dir_path + function_code_dir = "upsert-letter/dist" + function_include_common = true + handler_function_name = "handler" + runtime = "nodejs22.x" + memory = 128 + timeout = 29 + log_level = var.log_level + + force_lambda_code_deploy = var.force_lambda_code_deploy + enable_lambda_insights = false + + send_to_firehose = true + log_destination_arn = local.destination_arn + log_subscription_role_arn = local.acct.log_subscription_role_arn + + lambda_env_vars = merge(local.common_lambda_env_vars, {}) +} + +data "aws_iam_policy_document" "upsert_letter_lambda" { + statement { + sid = "KMSPermissions" + effect = "Allow" + + actions = [ + "kms:Decrypt", + "kms:GenerateDataKey", + ] + + resources = [ + module.kms.key_arn, + ] + } + + statement { + sid = "AllowDynamoDBWrite" + effect = "Allow" + + actions = [ + "dynamodb:PutItem" + ] + + resources = [ + aws_dynamodb_table.letters.arn, + "${aws_dynamodb_table.letters.arn}/index/supplierStatus-index" + ] + } + + statement { + sid = "AllowSQSRead" + effect = "Allow" + + actions = [ + "sqs:ReceiveMessage", + "sqs:DeleteMessage", + "sqs:GetQueueAttributes" + ] + + resources = [ + module.sqs_letter_updates.sqs_queue_arn + ] + } +} diff --git a/infrastructure/terraform/components/api/module_logging_bucket.tf b/infrastructure/terraform/components/api/module_logging_bucket.tf index da4bfba1..ace1eac8 100644 --- a/infrastructure/terraform/components/api/module_logging_bucket.tf +++ b/infrastructure/terraform/components/api/module_logging_bucket.tf @@ -1,5 +1,5 @@ module "logging_bucket" { - source = "https://github.com/NHSDigital/nhs-notify-shared-modules/releases/download/v2.0.20/terraform-s3bucket.zip" + source = "https://github.com/NHSDigital/nhs-notify-shared-modules/releases/download/v2.0.26/terraform-s3bucket.zip" name = "bucket-logs" aws_account_id = var.aws_account_id diff --git a/infrastructure/terraform/components/api/module_sqs_letter_status_updates.tf b/infrastructure/terraform/components/api/module_sqs_letter_status_updates.tf new file mode 100644 index 00000000..a604faaf --- /dev/null +++ b/infrastructure/terraform/components/api/module_sqs_letter_status_updates.tf @@ -0,0 +1,16 @@ +# Queue to transport update letter status messages +module "letter_status_updates_queue" { + source = "https://github.com/NHSDigital/nhs-notify-shared-modules/releases/download/v2.0.24/terraform-sqs.zip" + + name = "letter_status_updates_queue" + + aws_account_id = var.aws_account_id + component = var.component + environment = var.environment + project = var.project + region = var.region + + sqs_kms_key_arn = module.kms.key_arn + + create_dlq = true +} diff --git a/infrastructure/terraform/components/api/module_sqs_letter_updates.tf b/infrastructure/terraform/components/api/module_sqs_letter_updates.tf new file mode 100644 index 00000000..472afb81 --- /dev/null +++ b/infrastructure/terraform/components/api/module_sqs_letter_updates.tf @@ -0,0 +1,71 @@ +module "sqs_letter_updates" { + source = "https://github.com/NHSDigital/nhs-notify-shared-modules/releases/download/v2.0.26/terraform-sqs.zip" + + aws_account_id = var.aws_account_id + component = var.component + environment = var.environment + project = var.project + region = var.region + name = "letter-updates" + + sqs_kms_key_arn = module.kms.key_arn + + visibility_timeout_seconds = 60 + + create_dlq = true + sqs_policy_overload = data.aws_iam_policy_document.letter_updates_queue_policy.json +} + +data "aws_iam_policy_document" "letter_updates_queue_policy" { + version = "2012-10-17" + statement { + sid = "AllowSNSToSendMessage" + effect = "Allow" + + principals { + type = "Service" + identifiers = ["sns.amazonaws.com"] + } + + actions = [ + "sqs:SendMessage" + ] + + resources = [ + "arn:aws:sqs:${var.region}:${var.aws_account_id}:${var.project}-${var.environment}-${var.component}-letter-updates-queue" + ] + + condition { + test = "ArnEquals" + variable = "aws:SourceArn" + values = [module.eventsub.sns_topic.arn] + } + } + + statement { + sid = "AllowSNSPermissions" + effect = "Allow" + + principals { + type = "Service" + identifiers = ["sns.amazonaws.com"] + } + + actions = [ + "sqs:SendMessage", + "sqs:ListQueueTags", + "sqs:GetQueueUrl", + "sqs:GetQueueAttributes", + ] + + resources = [ + "arn:aws:sqs:${var.region}:${var.aws_account_id}:${var.project}-${var.environment}-${var.component}-letter-updates-queue" + ] + + condition { + test = "ArnEquals" + variable = "aws:SourceArn" + values = [module.eventsub.sns_topic.arn] + } + } +} diff --git a/infrastructure/terraform/components/api/module_supplier_ssl.tf b/infrastructure/terraform/components/api/module_supplier_ssl.tf index 63535da0..9ea26cb3 100644 --- a/infrastructure/terraform/components/api/module_supplier_ssl.tf +++ b/infrastructure/terraform/components/api/module_supplier_ssl.tf @@ -1,7 +1,7 @@ module "supplier_ssl" { count = var.manually_configure_mtls_truststore ? 0 : 1 - source = "https://github.com/NHSDigital/nhs-notify-shared-modules/releases/download/v2.0.20/terraform-ssl.zip" + source = "https://github.com/NHSDigital/nhs-notify-shared-modules/releases/download/v2.0.26/terraform-ssl.zip" name = "sapi_trust" aws_account_id = var.aws_account_id diff --git a/infrastructure/terraform/components/api/modules_eventpub.tf b/infrastructure/terraform/components/api/modules_eventpub.tf new file mode 100644 index 00000000..ccd8988d --- /dev/null +++ b/infrastructure/terraform/components/api/modules_eventpub.tf @@ -0,0 +1,28 @@ +module "eventpub" { + source = "https://github.com/NHSDigital/nhs-notify-shared-modules/releases/download/v2.0.26/terraform-eventpub.zip" + + name = "eventpub" + + aws_account_id = var.aws_account_id + component = var.component + environment = var.environment + project = var.project + region = var.region + group = var.group + + default_tags = local.default_tags + + kms_key_arn = module.kms.key_arn + log_retention_in_days = var.log_retention_in_days + log_level = "INFO" + + event_cache_buffer_interval = 500 + enable_sns_delivery_logging = true + sns_success_logging_sample_percent = 0 + + event_cache_expiry_days = 30 + enable_event_cache = true + + data_plane_bus_arn = var.eventpub_data_plane_bus_arn + control_plane_bus_arn = var.eventpub_control_plane_bus_arn +} diff --git a/infrastructure/terraform/components/api/modules_eventsub.tf b/infrastructure/terraform/components/api/modules_eventsub.tf new file mode 100644 index 00000000..b5978a4d --- /dev/null +++ b/infrastructure/terraform/components/api/modules_eventsub.tf @@ -0,0 +1,26 @@ +module "eventsub" { + source = "../../modules/eventsub" + + name = "eventsub" + + aws_account_id = var.aws_account_id + component = var.component + environment = var.environment + project = var.project + region = var.region + group = var.group + + default_tags = local.default_tags + + kms_key_arn = module.kms.key_arn + log_retention_in_days = var.log_retention_in_days + log_level = "INFO" + + event_cache_buffer_interval = 500 + enable_sns_delivery_logging = true + sns_success_logging_sample_percent = 0 + + event_cache_expiry_days = 30 + enable_event_cache = true + shared_infra_account_id = var.shared_infra_account_id +} diff --git a/infrastructure/terraform/components/api/outputs.tf b/infrastructure/terraform/components/api/outputs.tf index d7f78904..d18419bc 100644 --- a/infrastructure/terraform/components/api/outputs.tf +++ b/infrastructure/terraform/components/api/outputs.tf @@ -1,3 +1,16 @@ output "api_urll" { value = aws_api_gateway_stage.main.invoke_url } + +output "deployment" { + description = "Deployment details used for post-deployment scripts" + value = { + aws_region = var.region + aws_account_id = var.aws_account_id + project = var.project + environment = var.environment + group = var.group + component = var.component + commit_id = var.commit_id + } +} diff --git a/infrastructure/terraform/components/api/pre.sh b/infrastructure/terraform/components/api/pre.sh index 6f3957ec..202da9b8 100755 --- a/infrastructure/terraform/components/api/pre.sh +++ b/infrastructure/terraform/components/api/pre.sh @@ -2,7 +2,17 @@ # # It ensures all Node.js dependencies are installed, generates any required dependencies, # # and builds all Lambda functions in the workspace before Terraform provisions infrastructure. -npm ci +echo "Running Pre.sh" + +ROOT_DIR="$(git rev-parse --show-toplevel)" + +echo "Running set-github-token.sh" + +$ROOT_DIR/scripts/set-github-token.sh + +echo "Completed." + +npm ci --loglevel verbose npm run generate-dependencies --workspaces --if-present diff --git a/infrastructure/terraform/components/api/resources/spec.tmpl.json b/infrastructure/terraform/components/api/resources/spec.tmpl.json index 4514619b..5d333780 100644 --- a/infrastructure/terraform/components/api/resources/spec.tmpl.json +++ b/infrastructure/terraform/components/api/resources/spec.tmpl.json @@ -23,6 +23,34 @@ }, "openapi": "3.0.1", "paths": { + "/_status": { + "get": { + "operationId": "getStatusId", + "responses": { + "200": { + "description": "OK" + }, + "500": { + "description": "Server error" + } + }, + "summary": "Healthcheck endpoint", + "x-amazon-apigateway-integration": { + "contentHandling": "CONVERT_TO_TEXT", + "credentials": "${APIG_EXECUTION_ROLE_ARN}", + "httpMethod": "POST", + "passthroughBehavior": "WHEN_NO_TEMPLATES", + "responses": { + ".*": { + "statusCode": "200" + } + }, + "timeoutInMillis": 29000, + "type": "AWS_PROXY", + "uri": "arn:aws:apigateway:${AWS_REGION}:lambda:path/2015-03-31/functions/${GET_STATUS_LAMBDA_ARN}/invocations" + } + } + }, "/letters": { "get": { "description": "Returns 200 OK with paginated letter ids.", @@ -51,6 +79,43 @@ "type": "AWS_PROXY", "uri": "arn:aws:apigateway:${AWS_REGION}:lambda:path/2015-03-31/functions/${GET_LETTERS_LAMBDA_ARN}/invocations" } + }, + "post": { + "description": "Update the status of a collection of letters.", + "operationId": "postLetters", + "requestBody": { + "required": true + }, + "responses": { + "202": { + "description": "Acknowledges letters will be updated" + }, + "400": { + "description": "Bad request, invalid input data" + }, + "500": { + "description": "Server error" + } + }, + "security": [ + { + "LambdaAuthorizer": [] + } + ], + "x-amazon-apigateway-integration": { + "contentHandling": "CONVERT_TO_TEXT", + "credentials": "${APIG_EXECUTION_ROLE_ARN}", + "httpMethod": "POST", + "passthroughBehavior": "WHEN_NO_TEMPLATES", + "responses": { + ".*": { + "statusCode": "200" + } + }, + "timeoutInMillis": 29000, + "type": "AWS_PROXY", + "uri": "arn:aws:apigateway:${AWS_REGION}:lambda:path/2015-03-31/functions/${POST_LETTERS_LAMBDA_ARN}/invocations" + } } }, "/letters/{id}": { diff --git a/infrastructure/terraform/components/api/s3_test_letters.tf b/infrastructure/terraform/components/api/s3_test_letters.tf index e6011094..f1ad78f1 100644 --- a/infrastructure/terraform/components/api/s3_test_letters.tf +++ b/infrastructure/terraform/components/api/s3_test_letters.tf @@ -1,5 +1,5 @@ module "s3bucket_test_letters" { - source = "https://github.com/NHSDigital/nhs-notify-shared-modules/releases/download/v2.0.20/terraform-s3bucket.zip" + source = "https://github.com/NHSDigital/nhs-notify-shared-modules/releases/download/v2.0.26/terraform-s3bucket.zip" name = "test-letters" diff --git a/infrastructure/terraform/components/api/sns_topic_subscription_eventsub_sqs_letter_updates.tf b/infrastructure/terraform/components/api/sns_topic_subscription_eventsub_sqs_letter_updates.tf new file mode 100644 index 00000000..9c232c14 --- /dev/null +++ b/infrastructure/terraform/components/api/sns_topic_subscription_eventsub_sqs_letter_updates.tf @@ -0,0 +1,5 @@ +resource "aws_sns_topic_subscription" "eventsub_sqs_letter_updates" { + topic_arn = module.eventsub.sns_topic.arn + protocol = "sqs" + endpoint = module.sqs_letter_updates.sqs_queue_arn +} diff --git a/infrastructure/terraform/components/api/variables.tf b/infrastructure/terraform/components/api/variables.tf index 379523fb..daa43cd4 100644 --- a/infrastructure/terraform/components/api/variables.tf +++ b/infrastructure/terraform/components/api/variables.tf @@ -51,16 +51,46 @@ variable "default_tags" { # Variables specific to the component ## +variable "ca_pem_filename" { + type = string + description = "Filename for the CA truststore file within the s3 bucket" + default = null +} + +variable "commit_id" { + type = string + description = "The commit to deploy. Must be in the tree for branch_name" + default = "HEAD" +} + +variable "enable_backups" { + type = bool + description = "Enable backups" + default = false +} + +variable "force_destroy" { + type = bool + description = "Flag to force deletion of S3 buckets" + default = false +} + +variable "force_lambda_code_deploy" { + type = bool + description = "If the lambda package in s3 has the same commit id tag as the terraform build branch, the lambda will not update automatically. Set to True if making changes to Lambda code from on the same commit for example during development" + default = false +} + variable "kms_deletion_window" { type = string description = "When a kms key is deleted, how long should it wait in the pending deletion state?" default = "30" } -variable "log_retention_in_days" { +variable "letter_table_ttl_hours" { type = number - description = "The retention period in days for the Cloudwatch Logs events to be retained, default of 0 is indefinite" - default = 0 + description = "Number of hours to set as TTL on letters table" + default = 24 } variable "log_level" { @@ -69,12 +99,24 @@ variable "log_level" { default = "INFO" } -variable "force_lambda_code_deploy" { +variable "log_retention_in_days" { + type = number + description = "The retention period in days for the Cloudwatch Logs events to be retained, default of 0 is indefinite" + default = 0 +} + +variable "manually_configure_mtls_truststore" { type = bool - description = "If the lambda package in s3 has the same commit id tag as the terraform build branch, the lambda will not update automatically. Set to True if making changes to Lambda code from on the same commit for example during development" + description = "Manually manage the truststore used for API Gateway mTLS (e.g. for prod environment)" default = false } +variable "max_get_limit" { + type = number + description = "Default limit to apply to GET requests that support pagination" + default = 2500 +} + variable "parent_acct_environment" { type = string description = "Name of the environment responsible for the acct resources used, affects things like DNS zone. Useful for named dev environments" @@ -87,38 +129,20 @@ variable "shared_infra_account_id" { default = "000000000000" } -variable "manually_configure_mtls_truststore" { - type = bool - description = "Manually manage the truststore used for API Gateway mTLS (e.g. for prod environment)" - default = false -} - -variable "enable_backups" { - type = bool - description = "Enable backups" - default = false +variable "eventpub_data_plane_bus_arn" { + type = string + description = "ARN of the EventBridge data plane bus for eventpub" + default = "" } -variable "ca_pem_filename" { +variable "eventpub_control_plane_bus_arn" { type = string - description = "Filename for the CA truststore file within the s3 bucket" - default = null + description = "ARN of the EventBridge control plane bus for eventpub" + default = "" } -variable "force_destroy" { +variable "disable_gateway_execute_endpoint" { type = bool - description = "Flag to force deletion of S3 buckets" - default = false -} - -variable "letter_table_ttl_hours" { - type = number - description = "Number of hours to set as TTL on letters table" - default = 24 -} - -variable "max_get_limit" { - type = number - description = "Default limit to apply to GET requests that support pagination" - default = 2500 + description = "Disable the execution endpoint for the API Gateway" + default = true } diff --git a/infrastructure/terraform/modules/eventsub/README.md b/infrastructure/terraform/modules/eventsub/README.md new file mode 100644 index 00000000..a5653fda --- /dev/null +++ b/infrastructure/terraform/modules/eventsub/README.md @@ -0,0 +1,46 @@ + + + + +## Requirements + +| Name | Version | +|------|---------| +| [terraform](#requirement\_terraform) | >= 1.9.0 | +## Inputs + +| Name | Description | Type | Default | Required | +|------|-------------|------|---------|:--------:| +| [aws\_account\_id](#input\_aws\_account\_id) | The AWS Account ID (numeric) | `string` | n/a | yes | +| [component](#input\_component) | The name of the terraformscaffold component calling this module | `string` | n/a | yes | +| [default\_tags](#input\_default\_tags) | Default tag map for application to all taggable resources in the module | `map(string)` | `{}` | no | +| [enable\_event\_cache](#input\_enable\_event\_cache) | Enable caching of events to an S3 bucket | `bool` | `false` | no | +| [enable\_firehose\_raw\_message\_delivery](#input\_enable\_firehose\_raw\_message\_delivery) | Enables raw message delivery on firehose subscription | `bool` | `false` | no | +| [enable\_sns\_delivery\_logging](#input\_enable\_sns\_delivery\_logging) | Enable SNS Delivery Failure Notifications | `bool` | `false` | no | +| [environment](#input\_environment) | The name of the terraformscaffold environment the module is called for | `string` | n/a | yes | +| [event\_cache\_buffer\_interval](#input\_event\_cache\_buffer\_interval) | The buffer interval for data firehose | `number` | `500` | no | +| [event\_cache\_expiry\_days](#input\_event\_cache\_expiry\_days) | s3 archiving expiry in days | `number` | `30` | no | +| [force\_destroy](#input\_force\_destroy) | When enabled will force destroy event-cache S3 bucket | `bool` | `false` | no | +| [group](#input\_group) | The name of the tfscaffold group | `string` | `null` | no | +| [kms\_key\_arn](#input\_kms\_key\_arn) | KMS key arn to use for this function | `string` | n/a | yes | +| [log\_level](#input\_log\_level) | The log level to be used in lambda functions within the component. Any log with a lower severity than the configured value will not be logged: https://docs.python.org/3/library/logging.html#levels | `string` | `"WARN"` | no | +| [log\_retention\_in\_days](#input\_log\_retention\_in\_days) | The retention period in days for the Cloudwatch Logs events generated by the lambda function | `number` | n/a | yes | +| [name](#input\_name) | A unique name to distinguish this module invocation from others within the same CSI scope | `string` | n/a | yes | +| [project](#input\_project) | The name of the terraformscaffold project calling the module | `string` | n/a | yes | +| [region](#input\_region) | The AWS Region | `string` | n/a | yes | +| [shared\_infra\_account\_id](#input\_shared\_infra\_account\_id) | The AWS Account ID of the shared infrastructure account | `string` | `"000000000000"` | no | +| [sns\_success\_logging\_sample\_percent](#input\_sns\_success\_logging\_sample\_percent) | Enable SNS Delivery Successful Sample Percentage | `number` | `0` | no | +## Modules + +| Name | Source | Version | +|------|--------|---------| +| [s3bucket\_event\_cache](#module\_s3bucket\_event\_cache) | git::https://github.com/NHSDigital/nhs-notify-shared-modules.git//infrastructure/modules/s3bucket | v1.0.8 | +## Outputs + +| Name | Description | +|------|-------------| +| [s3\_bucket\_event\_cache](#output\_s3\_bucket\_event\_cache) | S3 Bucket ARN and Name for event cache | +| [sns\_topic](#output\_sns\_topic) | SNS Topic ARN and Name | + + + diff --git a/infrastructure/terraform/modules/eventsub/cloudwatch_log_group_kinesis_data_firehose.tf b/infrastructure/terraform/modules/eventsub/cloudwatch_log_group_kinesis_data_firehose.tf new file mode 100644 index 00000000..952fe8b6 --- /dev/null +++ b/infrastructure/terraform/modules/eventsub/cloudwatch_log_group_kinesis_data_firehose.tf @@ -0,0 +1,14 @@ +resource "aws_cloudwatch_log_group" "kinesis_data_firehose" { + count = var.enable_event_cache ? 1 : 0 + + name = "/aws/firehose/${local.csi}" + kms_key_id = var.kms_key_arn + retention_in_days = var.log_retention_in_days +} + +resource "aws_cloudwatch_log_stream" "kinesis_data_firehose_extended_s3" { + count = var.enable_event_cache ? 1 : 0 + + name = "extended_s3" + log_group_name = aws_cloudwatch_log_group.kinesis_data_firehose[0].name +} diff --git a/infrastructure/terraform/modules/eventsub/cloudwatch_log_group_sns_delivery_logging_failure.tf b/infrastructure/terraform/modules/eventsub/cloudwatch_log_group_sns_delivery_logging_failure.tf new file mode 100644 index 00000000..28a7ecfb --- /dev/null +++ b/infrastructure/terraform/modules/eventsub/cloudwatch_log_group_sns_delivery_logging_failure.tf @@ -0,0 +1,9 @@ +resource "aws_cloudwatch_log_group" "sns_delivery_logging_failure" { + count = var.enable_sns_delivery_logging ? 1 : 0 + + # SNS doesn't allow specifying a log group and is derived as: sns/${region}/${account_id}/${name_of_sns_topic}/Failure + # (for failure logs) + name = "sns/${var.region}/${var.aws_account_id}/${local.csi}/Failure" + kms_key_id = var.kms_key_arn + retention_in_days = var.log_retention_in_days +} diff --git a/infrastructure/terraform/modules/eventsub/cloudwatch_log_group_sns_delivery_logging_success.tf b/infrastructure/terraform/modules/eventsub/cloudwatch_log_group_sns_delivery_logging_success.tf new file mode 100644 index 00000000..f760e856 --- /dev/null +++ b/infrastructure/terraform/modules/eventsub/cloudwatch_log_group_sns_delivery_logging_success.tf @@ -0,0 +1,9 @@ +resource "aws_cloudwatch_log_group" "sns_delivery_logging_success" { + count = var.enable_sns_delivery_logging ? 1 : 0 + + # SNS doesn't allow specifying a log group and is derived as: sns/${region}/${account_id}/${name_of_sns_topic} + # (for success logs) + name = "sns/${var.region}/${var.aws_account_id}/${local.csi}" + kms_key_id = var.kms_key_arn + retention_in_days = var.log_retention_in_days +} diff --git a/infrastructure/terraform/modules/eventsub/cloudwatch_metric_alarm_sns_delivery_failures.tf b/infrastructure/terraform/modules/eventsub/cloudwatch_metric_alarm_sns_delivery_failures.tf new file mode 100644 index 00000000..e8ef1249 --- /dev/null +++ b/infrastructure/terraform/modules/eventsub/cloudwatch_metric_alarm_sns_delivery_failures.tf @@ -0,0 +1,16 @@ +resource "aws_cloudwatch_metric_alarm" "sns_delivery_failures" { + alarm_name = "${local.csi}-sns-delivery-failures" + alarm_description = "RELIABILITY: Alarm for SNS topic delivery failures" + comparison_operator = "GreaterThanThreshold" + evaluation_periods = 1 + metric_name = "NumberOfNotificationsFailed" + namespace = "AWS/SNS" + period = 300 + statistic = "Sum" + threshold = 0 + treat_missing_data = "notBreaching" + + dimensions = { + TopicName = aws_sns_topic.main.name + } +} diff --git a/infrastructure/terraform/modules/eventsub/iam_policy_sns_delivery_logging_cloudwatch.tf b/infrastructure/terraform/modules/eventsub/iam_policy_sns_delivery_logging_cloudwatch.tf new file mode 100644 index 00000000..d296da2d --- /dev/null +++ b/infrastructure/terraform/modules/eventsub/iam_policy_sns_delivery_logging_cloudwatch.tf @@ -0,0 +1,44 @@ +resource "aws_iam_policy" "sns_delivery_logging_cloudwatch" { + count = var.enable_sns_delivery_logging ? 1 : 0 + + name = "${local.csi}-${var.name}-sns-delivery" + description = "Policy for ${local.csi}-${var.name} SNS Delivery Logging" + policy = data.aws_iam_policy_document.sns_delivery_logging_cloudwatch[0].json +} + +data "aws_iam_policy_document" "sns_delivery_logging_cloudwatch" { + count = var.enable_sns_delivery_logging ? 1 : 0 + + statement { + sid = "KMSCloudwatchKeyAccess" + effect = "Allow" + + actions = [ + "kms:GenerateDataKey", + "kms:Decrypt", + ] + + resources = [ + var.kms_key_arn + ] + } + + statement { + sid = "AllowSNSDeliveryNotifications" + effect = "Allow" + + actions = [ + "logs:CreateLogStream", + "logs:PutLogEvents", + "logs:PutMetricFilter", + "logs:PutRetentionPolicy", + ] + + resources = [ + aws_cloudwatch_log_group.sns_delivery_logging_success[0].arn, + "${aws_cloudwatch_log_group.sns_delivery_logging_success[0].arn}:log-stream:*", + aws_cloudwatch_log_group.sns_delivery_logging_failure[0].arn, + "${aws_cloudwatch_log_group.sns_delivery_logging_failure[0].arn}:log-stream:*", + ] + } +} diff --git a/infrastructure/terraform/modules/eventsub/iam_role_firehose_role.tf b/infrastructure/terraform/modules/eventsub/iam_role_firehose_role.tf new file mode 100644 index 00000000..69bf7618 --- /dev/null +++ b/infrastructure/terraform/modules/eventsub/iam_role_firehose_role.tf @@ -0,0 +1,59 @@ +resource "aws_iam_role" "firehose_role" { + count = var.enable_event_cache ? 1 : 0 + + name = "${local.csi}-firehose-role" + assume_role_policy = data.aws_iam_policy_document.firehose_assume_role[0].json +} + +data "aws_iam_policy_document" "firehose_assume_role" { + count = var.enable_event_cache ? 1 : 0 + + statement { + effect = "Allow" + + principals { + type = "Service" + identifiers = ["firehose.amazonaws.com"] + } + + actions = ["sts:AssumeRole"] + } +} + +resource "aws_iam_role_policy_attachment" "s3_write_object" { + count = var.enable_event_cache ? 1 : 0 + + role = aws_iam_role.firehose_role[0].name + policy_arn = aws_iam_policy.s3_write_object[0].arn +} + +resource "aws_iam_policy" "s3_write_object" { + count = var.enable_event_cache ? 1 : 0 + + name = "${local.csi}-${var.name}-s3-write-object" + description = "S3 Put Object policy for ${local.csi}-${var.name} Firehose" + policy = data.aws_iam_policy_document.s3_write_object[0].json +} + +data "aws_iam_policy_document" "s3_write_object" { + count = var.enable_event_cache ? 1 : 0 + + statement { + sid = "AllowWriteObject" + effect = "Allow" + + actions = [ + "s3:AbortMultipartUpload", + "s3:GetBucketLocation", + "s3:GetObject", + "s3:ListBucket", + "s3:ListBucketMultipartUploads", + "s3:PutObject", + "s3:PutObject", + ] + + resources = [ + "${module.s3bucket_event_cache[0].arn}/*", + ] + } +} diff --git a/infrastructure/terraform/modules/eventsub/iam_role_sns.tf b/infrastructure/terraform/modules/eventsub/iam_role_sns.tf new file mode 100644 index 00000000..d88bea7a --- /dev/null +++ b/infrastructure/terraform/modules/eventsub/iam_role_sns.tf @@ -0,0 +1,51 @@ +resource "aws_iam_role" "sns_role" { + name = "${local.csi}-sns-role" + assume_role_policy = data.aws_iam_policy_document.sns_assume_role.json +} + +resource "aws_iam_policy" "firehose_delivery" { + count = var.enable_event_cache ? 1 : 0 + + name = "${local.csi}-${var.name}-firehose-delivery" + description = "Delivery Policy for ${local.csi}-${var.name} Firehose" + policy = data.aws_iam_policy_document.firehose_delivery[0].json +} + +resource "aws_iam_role_policy_attachment" "firehose_delivery" { + count = var.enable_event_cache ? 1 : 0 + + role = aws_iam_role.sns_role.name + policy_arn = aws_iam_policy.firehose_delivery[0].arn +} + + +data "aws_iam_policy_document" "sns_assume_role" { + statement { + effect = "Allow" + + principals { + type = "Service" + identifiers = ["sns.amazonaws.com"] + } + + actions = ["sts:AssumeRole"] + } +} + +data "aws_iam_policy_document" "firehose_delivery" { + count = var.enable_event_cache ? 1 : 0 + + statement { + sid = "AllowFirehoseDelivery" + effect = "Allow" + + actions = [ + "firehose:PutRecord", + "firehose:PutRecordBatch" + ] + + resources = [ + "${aws_kinesis_firehose_delivery_stream.main[0].arn}", + ] + } +} diff --git a/infrastructure/terraform/modules/eventsub/iam_role_sns_delivery_logging.tf b/infrastructure/terraform/modules/eventsub/iam_role_sns_delivery_logging.tf new file mode 100644 index 00000000..3bd25e06 --- /dev/null +++ b/infrastructure/terraform/modules/eventsub/iam_role_sns_delivery_logging.tf @@ -0,0 +1,21 @@ +resource "aws_iam_role" "sns_delivery_logging_role" { + count = var.enable_sns_delivery_logging ? 1 : 0 + + name = "${local.csi}-sns-delivery-logging" + assume_role_policy = data.aws_iam_policy_document.sns_delivery_logging_assume_role[0].json +} + +data "aws_iam_policy_document" "sns_delivery_logging_assume_role" { + count = var.enable_sns_delivery_logging ? 1 : 0 + + statement { + effect = "Allow" + + principals { + type = "Service" + identifiers = ["sns.amazonaws.com"] + } + + actions = ["sts:AssumeRole"] + } +} diff --git a/infrastructure/terraform/modules/eventsub/kinesis_firehose_delivery_stream.tf b/infrastructure/terraform/modules/eventsub/kinesis_firehose_delivery_stream.tf new file mode 100644 index 00000000..186372d8 --- /dev/null +++ b/infrastructure/terraform/modules/eventsub/kinesis_firehose_delivery_stream.tf @@ -0,0 +1,25 @@ +resource "aws_kinesis_firehose_delivery_stream" "main" { + count = var.enable_event_cache ? 1 : 0 + + name = local.csi + destination = "extended_s3" + + + server_side_encryption { + enabled = true + key_type = "CUSTOMER_MANAGED_CMK" + key_arn = var.kms_key_arn + } + + extended_s3_configuration { + role_arn = aws_iam_role.firehose_role[0].arn + bucket_arn = module.s3bucket_event_cache[0].arn + buffering_interval = var.event_cache_buffer_interval + + cloudwatch_logging_options { + enabled = true + log_group_name = aws_cloudwatch_log_group.kinesis_data_firehose[0].name + log_stream_name = aws_cloudwatch_log_stream.kinesis_data_firehose_extended_s3[0].name + } + } +} diff --git a/infrastructure/terraform/modules/eventsub/locals.tf b/infrastructure/terraform/modules/eventsub/locals.tf new file mode 100644 index 00000000..e421035a --- /dev/null +++ b/infrastructure/terraform/modules/eventsub/locals.tf @@ -0,0 +1,22 @@ +locals { + module = "eventsub" + + csi = replace( + format( + "%s-%s-%s-%s", + var.project, + var.environment, + var.component, + var.name, + ), + "_", + "", + ) + default_tags = merge( + var.default_tags, + { + Module = local.module + Name = local.csi + }, + ) +} diff --git a/infrastructure/terraform/modules/eventsub/module_s3bucket_event_cache.tf b/infrastructure/terraform/modules/eventsub/module_s3bucket_event_cache.tf new file mode 100644 index 00000000..0ef37512 --- /dev/null +++ b/infrastructure/terraform/modules/eventsub/module_s3bucket_event_cache.tf @@ -0,0 +1,128 @@ +module "s3bucket_event_cache" { + source = "git::https://github.com/NHSDigital/nhs-notify-shared-modules.git//infrastructure/modules/s3bucket?ref=v1.0.8" + + count = var.enable_event_cache ? 1 : 0 + + name = "eventsub_event_cache" + + aws_account_id = var.aws_account_id + region = var.region + project = var.project + environment = var.environment + component = var.component + + acl = "private" + force_destroy = var.force_destroy + versioning = true + + lifecycle_rules = [ + { + enabled = true + + noncurrent_version_transition = [ + { + noncurrent_days = "30" + storage_class = "STANDARD_IA" + } + ] + + noncurrent_version_expiration = { + noncurrent_days = "90" + } + + abort_incomplete_multipart_upload = { + days = "1" + } + } + ] + + policy_documents = [ + data.aws_iam_policy_document.s3bucket_event_cache[0].json + ] + + public_access = { + block_public_acls = true + block_public_policy = true + ignore_public_acls = true + restrict_public_buckets = true + } + + default_tags = { + Name = "Event Cache Storage" + } +} + +data "aws_iam_policy_document" "s3bucket_event_cache" { + count = var.enable_event_cache ? 1 : 0 + + statement { + sid = "DontAllowNonSecureConnection" + effect = "Deny" + + actions = [ + "s3:*", + ] + + resources = [ + module.s3bucket_event_cache[0].arn, + "${module.s3bucket_event_cache[0].arn}/*", + ] + + principals { + type = "AWS" + + identifiers = [ + "*", + ] + } + + condition { + test = "Bool" + variable = "aws:SecureTransport" + + values = [ + "false", + ] + } + } + + statement { + sid = "AllowManagedAccountsToList" + effect = "Allow" + + actions = [ + "s3:ListBucket", + ] + + resources = [ + module.s3bucket_event_cache[0].arn, + ] + + principals { + type = "AWS" + identifiers = [ + "arn:aws:iam::${var.aws_account_id}:root" + ] + } + } + + statement { + sid = "AllowManagedAccountsToGet" + effect = "Allow" + + actions = [ + "s3:GetObject", + ] + + resources = [ + "${module.s3bucket_event_cache[0].arn}/*", + ] + + principals { + type = "AWS" + identifiers = [ + "arn:aws:iam::${var.aws_account_id}:root" + ] + } + } +} diff --git a/infrastructure/terraform/modules/eventsub/outputs.tf b/infrastructure/terraform/modules/eventsub/outputs.tf new file mode 100644 index 00000000..e2ff3b38 --- /dev/null +++ b/infrastructure/terraform/modules/eventsub/outputs.tf @@ -0,0 +1,15 @@ +output "sns_topic" { + description = "SNS Topic ARN and Name" + value = { + arn = aws_sns_topic.main.arn + name = aws_sns_topic.main.name + } +} + +output "s3_bucket_event_cache" { + description = "S3 Bucket ARN and Name for event cache" + value = var.enable_event_cache ? { + arn = module.s3bucket_event_cache[0].arn + bucket = module.s3bucket_event_cache[0].bucket + } : {} +} diff --git a/infrastructure/terraform/modules/eventsub/sns_topic.tf b/infrastructure/terraform/modules/eventsub/sns_topic.tf new file mode 100644 index 00000000..cc30db15 --- /dev/null +++ b/infrastructure/terraform/modules/eventsub/sns_topic.tf @@ -0,0 +1,24 @@ +resource "aws_sns_topic" "main" { + name = local.csi + kms_master_key_id = var.kms_key_arn + + application_failure_feedback_role_arn = var.enable_sns_delivery_logging == true ? aws_iam_role.sns_delivery_logging_role[0].arn : null + application_success_feedback_role_arn = var.enable_sns_delivery_logging == true ? aws_iam_role.sns_delivery_logging_role[0].arn : null + application_success_feedback_sample_rate = var.enable_sns_delivery_logging == true ? var.sns_success_logging_sample_percent : null + + firehose_failure_feedback_role_arn = var.enable_sns_delivery_logging == true ? aws_iam_role.sns_delivery_logging_role[0].arn : null + firehose_success_feedback_role_arn = var.enable_sns_delivery_logging == true ? aws_iam_role.sns_delivery_logging_role[0].arn : null + firehose_success_feedback_sample_rate = var.enable_sns_delivery_logging == true ? var.sns_success_logging_sample_percent : null + + http_failure_feedback_role_arn = var.enable_sns_delivery_logging == true ? aws_iam_role.sns_delivery_logging_role[0].arn : null + http_success_feedback_role_arn = var.enable_sns_delivery_logging == true ? aws_iam_role.sns_delivery_logging_role[0].arn : null + http_success_feedback_sample_rate = var.enable_sns_delivery_logging == true ? var.sns_success_logging_sample_percent : null + + lambda_failure_feedback_role_arn = var.enable_sns_delivery_logging == true ? aws_iam_role.sns_delivery_logging_role[0].arn : null + lambda_success_feedback_role_arn = var.enable_sns_delivery_logging == true ? aws_iam_role.sns_delivery_logging_role[0].arn : null + lambda_success_feedback_sample_rate = var.enable_sns_delivery_logging == true ? var.sns_success_logging_sample_percent : null + + sqs_failure_feedback_role_arn = var.enable_sns_delivery_logging == true ? aws_iam_role.sns_delivery_logging_role[0].arn : null + sqs_success_feedback_role_arn = var.enable_sns_delivery_logging == true ? aws_iam_role.sns_delivery_logging_role[0].arn : null + sqs_success_feedback_sample_rate = var.enable_sns_delivery_logging == true ? var.sns_success_logging_sample_percent : null +} diff --git a/infrastructure/terraform/modules/eventsub/sns_topic_policy.tf b/infrastructure/terraform/modules/eventsub/sns_topic_policy.tf new file mode 100644 index 00000000..a772e9e7 --- /dev/null +++ b/infrastructure/terraform/modules/eventsub/sns_topic_policy.tf @@ -0,0 +1,63 @@ +resource "aws_sns_topic_policy" "main" { + arn = aws_sns_topic.main.arn + + policy = data.aws_iam_policy_document.sns_topic_policy.json +} + +data "aws_iam_policy_document" "sns_topic_policy" { + policy_id = "__default_policy_ID" + + statement { + sid = "AllowAllSNSActionsFromAccount" + effect = "Allow" + + principals { + type = "AWS" + identifiers = ["*"] + } + + actions = [ + "SNS:Subscribe", + "SNS:SetTopicAttributes", + "SNS:RemovePermission", + "SNS:Receive", + "SNS:Publish", + "SNS:ListSubscriptionsByTopic", + "SNS:GetTopicAttributes", + "SNS:DeleteTopic", + "SNS:AddPermission", + ] + + resources = [ + aws_sns_topic.main.arn, + ] + + condition { + test = "StringEquals" + variable = "AWS:SourceOwner" + + values = [ + var.aws_account_id, + ] + } + } + + statement { + sid = "AllowAllSNSActionsFromSharedAccount" + effect = "Allow" + actions = [ + "SNS:Publish", + ] + + principals { + type = "AWS" + identifiers = [ + "arn:aws:iam::${var.shared_infra_account_id}:root" + ] + } + + resources = [ + aws_sns_topic.main.arn, + ] + } +} diff --git a/infrastructure/terraform/modules/eventsub/sns_topic_subscription_firehose.tf b/infrastructure/terraform/modules/eventsub/sns_topic_subscription_firehose.tf new file mode 100644 index 00000000..42457f6d --- /dev/null +++ b/infrastructure/terraform/modules/eventsub/sns_topic_subscription_firehose.tf @@ -0,0 +1,9 @@ +resource "aws_sns_topic_subscription" "firehose" { + count = var.enable_event_cache ? 1 : 0 + + topic_arn = aws_sns_topic.main.arn + protocol = "firehose" + subscription_role_arn = aws_iam_role.sns_role.arn + endpoint = aws_kinesis_firehose_delivery_stream.main[0].arn + raw_message_delivery = var.enable_firehose_raw_message_delivery +} diff --git a/infrastructure/terraform/modules/eventsub/variables.tf b/infrastructure/terraform/modules/eventsub/variables.tf new file mode 100644 index 00000000..4b73d452 --- /dev/null +++ b/infrastructure/terraform/modules/eventsub/variables.tf @@ -0,0 +1,116 @@ +## +# Basic inherited variables for terraformscaffold modules +## + +variable "project" { + type = string + description = "The name of the terraformscaffold project calling the module" +} + +variable "environment" { + type = string + description = "The name of the terraformscaffold environment the module is called for" +} + +variable "component" { + type = string + description = "The name of the terraformscaffold component calling this module" +} + +variable "aws_account_id" { + type = string + description = "The AWS Account ID (numeric)" +} + +variable "group" { + type = string + description = "The name of the tfscaffold group" + default = null +} + +## +# Variable specific to the module +## + +# We presume this will always be specified. The default of {} will cause an error if a valid map is not specified. +# If we ever want to define this but allow it to not be specified, then we must provide a default tag keypair will be applied +# as the true default. In any other case default_tags should be removed from the module. +variable "default_tags" { + type = map(string) + description = "Default tag map for application to all taggable resources in the module" + default = {} +} + +variable "region" { + type = string + description = "The AWS Region" +} + +variable "name" { + type = string + description = "A unique name to distinguish this module invocation from others within the same CSI scope" +} + +variable "kms_key_arn" { + type = string + description = "KMS key arn to use for this function" +} + +variable "log_retention_in_days" { + type = number + description = "The retention period in days for the Cloudwatch Logs events generated by the lambda function" +} + +variable "event_cache_buffer_interval" { + type = number + description = "The buffer interval for data firehose" + default = 500 +} + +variable "enable_sns_delivery_logging" { + type = bool + description = "Enable SNS Delivery Failure Notifications" + default = false +} + +variable "sns_success_logging_sample_percent" { + type = number + description = "Enable SNS Delivery Successful Sample Percentage" + default = 0 +} + +variable "log_level" { + type = string + description = "The log level to be used in lambda functions within the component. Any log with a lower severity than the configured value will not be logged: https://docs.python.org/3/library/logging.html#levels" + default = "WARN" +} + +variable "event_cache_expiry_days" { + type = number + description = "s3 archiving expiry in days" + default = 30 +} + +variable "enable_event_cache" { + type = bool + description = "Enable caching of events to an S3 bucket" + default = false +} + +variable "enable_firehose_raw_message_delivery" { + type = bool + description = "Enables raw message delivery on firehose subscription" + default = false +} + +variable "force_destroy" { + type = bool + description = "When enabled will force destroy event-cache S3 bucket" + default = false +} + +variable "shared_infra_account_id" { + type = string + description = "The AWS Account ID of the shared infrastructure account" + default = "000000000000" +} diff --git a/infrastructure/terraform/modules/eventsub/versions.tf b/infrastructure/terraform/modules/eventsub/versions.tf new file mode 100644 index 00000000..f8dc86e9 --- /dev/null +++ b/infrastructure/terraform/modules/eventsub/versions.tf @@ -0,0 +1,9 @@ + +terraform { + required_providers { + aws = { + source = "hashicorp/aws" + } + } + required_version = ">= 1.9.0" +} diff --git a/internal/datastore/jest.config.ts b/internal/datastore/jest.config.ts index 129da6a6..1fb73e6d 100644 --- a/internal/datastore/jest.config.ts +++ b/internal/datastore/jest.config.ts @@ -1,7 +1,7 @@ -import type { Config } from 'jest'; +import type { Config } from "jest"; export const baseJestConfig: Config = { - preset: 'ts-jest', + preset: "ts-jest", // Automatically clear mock calls, instances, contexts and results before every test clearMocks: true, @@ -10,14 +10,14 @@ export const baseJestConfig: Config = { collectCoverage: true, // The directory where Jest should output its coverage files - coverageDirectory: './.reports/unit/coverage', + coverageDirectory: "./.reports/unit/coverage", // Indicates which provider should be used to instrument code for coverage - coverageProvider: 'babel', + coverageProvider: "babel", // Module name mapper to handle TypeScript path aliases moduleNameMapper: { - '^@internal/helpers$': '/../helpers/src' + "^@internal/helpers$": "/../helpers/src", }, coverageThreshold: { @@ -29,36 +29,36 @@ export const baseJestConfig: Config = { }, }, - coveragePathIgnorePatterns: ['/__tests__/'], - transform: { '^.+\\.ts$': 'ts-jest' }, - testPathIgnorePatterns: ['.build'], - testMatch: ['**/?(*.)+(spec|test).[jt]s?(x)'], + coveragePathIgnorePatterns: ["/__tests__/"], + transform: { "^.+\\.ts$": "ts-jest" }, + testPathIgnorePatterns: [".build"], + testMatch: ["**/?(*.)+(spec|test).[jt]s?(x)"], // Use this configuration option to add custom reporters to Jest reporters: [ - 'default', + "default", [ - 'jest-html-reporter', + "jest-html-reporter", { - pageTitle: 'Test Report', - outputPath: './.reports/unit/test-report.html', + pageTitle: "Test Report", + outputPath: "./.reports/unit/test-report.html", includeFailureMsg: true, }, ], ], // The test environment that will be used for testing - testEnvironment: 'jsdom', + testEnvironment: "jsdom", }; const utilsJestConfig = { ...baseJestConfig, - testEnvironment: 'node', + testEnvironment: "node", coveragePathIgnorePatterns: [ ...(baseJestConfig.coveragePathIgnorePatterns ?? []), - 'zod-validators.ts', + "zod-validators.ts", ], }; diff --git a/internal/datastore/package.json b/internal/datastore/package.json index 4d995c49..4afead43 100644 --- a/internal/datastore/package.json +++ b/internal/datastore/package.json @@ -4,7 +4,8 @@ "@aws-sdk/lib-dynamodb": "^3.858.0", "@internal/helpers": "*", "pino": "^9.7.0", - "zod": "^4.1.11" + "zod": "^4.1.11", + "zod-mermaid": "^1.0.9" }, "devDependencies": { "@stylistic/eslint-plugin": "^3.1.0", @@ -19,8 +20,7 @@ "testcontainers": "^11.4.0", "ts-jest": "^29.4.0", "ts-node": "^10.9.2", - "typescript": "^5.8.3", - "zod-mermaid": "^1.0.9" + "typescript": "^5.9.3" }, "license": "MIT", "main": "src/index.ts", diff --git a/internal/datastore/src/__test__/db.ts b/internal/datastore/src/__test__/db.ts index 85f89193..f3606b92 100644 --- a/internal/datastore/src/__test__/db.ts +++ b/internal/datastore/src/__test__/db.ts @@ -1,38 +1,39 @@ -import { GenericContainer } from 'testcontainers'; +import { GenericContainer } from "testcontainers"; import { CreateTableCommand, DeleteTableCommand, DynamoDBClient, - UpdateTimeToLiveCommand -} from '@aws-sdk/client-dynamodb'; -import { DynamoDBDocumentClient } from '@aws-sdk/lib-dynamodb'; -import { DatastoreConfig } from '../config'; + UpdateTimeToLiveCommand, +} from "@aws-sdk/client-dynamodb"; +import { DynamoDBDocumentClient } from "@aws-sdk/lib-dynamodb"; +import { DatastoreConfig } from "../config"; export async function setupDynamoDBContainer() { - const container = await new GenericContainer('amazon/dynamodb-local') + const container = await new GenericContainer("amazon/dynamodb-local") .withExposedPorts(8000) .start(); const endpoint = `http://${container.getHost()}:${container.getMappedPort(8000)}`; const ddbClient = new DynamoDBClient({ - region: 'us-west-2', + region: "us-west-2", endpoint, credentials: { - accessKeyId: 'fakeMyKeyId', - secretAccessKey: 'fakeSecretAccessKey' - } + accessKeyId: "fakeMyKeyId", + secretAccessKey: "fakeSecretAccessKey", + }, }); const docClient = DynamoDBDocumentClient.from(ddbClient); - const config : DatastoreConfig = { - region: 'us-west-2', + const config: DatastoreConfig = { + region: "us-west-2", endpoint, - lettersTableName: 'letters', - miTableName: 'management-info', + lettersTableName: "letters", + miTableName: "management-info", + suppliersTableName: "suppliers", lettersTtlHours: 1, - miTtlHours: 1 + miTtlHours: 1, }; return { @@ -40,60 +41,82 @@ export async function setupDynamoDBContainer() { ddbClient, docClient, endpoint, - config + config, }; } export type DBContext = Awaited>; const createLetterTableCommand = new CreateTableCommand({ - TableName: 'letters', - BillingMode: 'PAY_PER_REQUEST', - KeySchema: [ - { AttributeName: 'supplierId', KeyType: 'HASH' }, // Partition key - { AttributeName: 'id', KeyType: 'RANGE' } // Sort key - ], - GlobalSecondaryIndexes: [ - { - IndexName: 'supplierStatus-index', - KeySchema: [ - { AttributeName: 'supplierStatus', KeyType: 'HASH' }, // Partition key for GSI - { AttributeName: 'supplierStatusSk', KeyType: 'RANGE' } // Sort key for GSI - ], - Projection: { - ProjectionType: 'ALL' - } - } - ], - AttributeDefinitions: [ - { AttributeName: 'supplierId', AttributeType: 'S' }, - { AttributeName: 'id', AttributeType: 'S' }, - { AttributeName: 'supplierStatus', AttributeType: 'S' }, - { AttributeName: 'supplierStatusSk', AttributeType: 'S' }, - ] - }); + TableName: "letters", + BillingMode: "PAY_PER_REQUEST", + KeySchema: [ + { AttributeName: "supplierId", KeyType: "HASH" }, // Partition key + { AttributeName: "id", KeyType: "RANGE" }, // Sort key + ], + GlobalSecondaryIndexes: [ + { + IndexName: "supplierStatus-index", + KeySchema: [ + { AttributeName: "supplierStatus", KeyType: "HASH" }, // Partition key for GSI + { AttributeName: "supplierStatusSk", KeyType: "RANGE" }, // Sort key for GSI + ], + Projection: { + ProjectionType: "ALL", + }, + }, + ], + AttributeDefinitions: [ + { AttributeName: "supplierId", AttributeType: "S" }, + { AttributeName: "id", AttributeType: "S" }, + { AttributeName: "supplierStatus", AttributeType: "S" }, + { AttributeName: "supplierStatusSk", AttributeType: "S" }, + ], +}); const updateTimeToLiveCommand = new UpdateTimeToLiveCommand({ - TableName: 'letters', - TimeToLiveSpecification: { - AttributeName: 'ttl', - Enabled: true - } - }); + TableName: "letters", + TimeToLiveSpecification: { + AttributeName: "ttl", + Enabled: true, + }, +}); const createMITableCommand = new CreateTableCommand({ - TableName: 'management-info', - BillingMode: 'PAY_PER_REQUEST', - KeySchema: [ - { AttributeName: 'supplierId', KeyType: 'HASH' }, // Partition key - { AttributeName: 'id', KeyType: 'RANGE' } // Sort key - ], - AttributeDefinitions: [ - { AttributeName: 'supplierId', AttributeType: 'S' }, - { AttributeName: 'id', AttributeType: 'S' }, - ] - }); - + TableName: "management-info", + BillingMode: "PAY_PER_REQUEST", + KeySchema: [ + { AttributeName: "supplierId", KeyType: "HASH" }, // Partition key + { AttributeName: "id", KeyType: "RANGE" }, // Sort key + ], + AttributeDefinitions: [ + { AttributeName: "supplierId", AttributeType: "S" }, + { AttributeName: "id", AttributeType: "S" }, + ], +}); + +const createSupplierTableCommand = new CreateTableCommand({ + TableName: "suppliers", + BillingMode: "PAY_PER_REQUEST", + KeySchema: [ + { AttributeName: "id", KeyType: "HASH" }, // Partition key + ], + GlobalSecondaryIndexes: [ + { + IndexName: "supplier-apim-index", + KeySchema: [ + { AttributeName: "apimId", KeyType: "HASH" }, // Partition key for GSI + ], + Projection: { + ProjectionType: "ALL", + }, + }, + ], + AttributeDefinitions: [ + { AttributeName: "id", AttributeType: "S" }, + { AttributeName: "apimId", AttributeType: "S" }, + ], +}); export async function createTables(context: DBContext) { const { ddbClient } = context; @@ -102,17 +125,27 @@ export async function createTables(context: DBContext) { await ddbClient.send(updateTimeToLiveCommand); await ddbClient.send(createMITableCommand); + await ddbClient.send(createSupplierTableCommand); } - export async function deleteTables(context: DBContext) { const { ddbClient } = context; - await ddbClient.send(new DeleteTableCommand({ - TableName: 'letters' - })); - - await ddbClient.send(new DeleteTableCommand({ - TableName: 'management-info' - })); + await ddbClient.send( + new DeleteTableCommand({ + TableName: "letters", + }), + ); + + await ddbClient.send( + new DeleteTableCommand({ + TableName: "management-info", + }), + ); + + await ddbClient.send( + new DeleteTableCommand({ + TableName: "suppliers", + }), + ); } diff --git a/internal/datastore/src/__test__/heathcheck.test.ts b/internal/datastore/src/__test__/heathcheck.test.ts new file mode 100644 index 00000000..315144d7 --- /dev/null +++ b/internal/datastore/src/__test__/heathcheck.test.ts @@ -0,0 +1,43 @@ +import DBHealthcheck from "../healthcheck"; +import { + DBContext, + createTables, + deleteTables, + setupDynamoDBContainer, +} from "./db"; + +// Database tests can take longer, especially with setup and teardown +jest.setTimeout(30_000); + +describe("DBHealthcheck", () => { + let db: DBContext; + + beforeAll(async () => { + db = await setupDynamoDBContainer(); + }); + + beforeEach(async () => { + await createTables(db); + }); + + afterEach(async () => { + await deleteTables(db); + }); + + it("passes when the database is available", async () => { + const dbHealthCheck = new DBHealthcheck(db.docClient, db.config); + await expect(dbHealthCheck.check()).resolves.not.toThrow(); + }); + + it("fails when the database is unavailable", async () => { + const realFunction = db.docClient.send; + db.docClient.send = jest.fn().mockImplementation(() => { + throw new Error("Failed to send"); + }); + + const dbHealthCheck = new DBHealthcheck(db.docClient, db.config); + await expect(dbHealthCheck.check()).rejects.toThrow(); + + db.docClient.send = realFunction; + }); +}); diff --git a/internal/datastore/src/__test__/letter-repository.test.ts b/internal/datastore/src/__test__/letter-repository.test.ts index 6e336468..6b0c5294 100644 --- a/internal/datastore/src/__test__/letter-repository.test.ts +++ b/internal/datastore/src/__test__/letter-repository.test.ts @@ -1,29 +1,37 @@ -import { createTables, DBContext, deleteTables, setupDynamoDBContainer } from './db'; -import { LetterRepository } from '../letter-repository'; -import { Letter } from '../types'; -import { Logger } from 'pino'; -import { createTestLogger, LogStream } from './logs'; -import { PutCommand } from '@aws-sdk/lib-dynamodb'; -import { LetterDto } from '../../../../lambdas/api-handler/src/contracts/letters'; - -function createLetter(supplierId: string, letterId: string, status: Letter['status'] = 'PENDING'): Omit { +import { Logger } from "pino"; +import { PutCommand } from "@aws-sdk/lib-dynamodb"; +import { + DBContext, + createTables, + deleteTables, + setupDynamoDBContainer, +} from "./db"; +import { LetterRepository } from "../letter-repository"; +import { Letter } from "../types"; +import { LogStream, createTestLogger } from "./logs"; +import { LetterDto } from "../../../../lambdas/api-handler/src/contracts/letters"; + +function createLetter( + supplierId: string, + letterId: string, + status: Letter["status"] = "PENDING", +): Omit { return { id: letterId, - supplierId: supplierId, - specificationId: 'specification1', - groupId: 'group1', + supplierId, + specificationId: "specification1", + groupId: "group1", url: `s3://bucket/${letterId}.pdf`, - status: status, + status, createdAt: new Date().toISOString(), - updatedAt: new Date().toISOString() + updatedAt: new Date().toISOString(), }; } // Database tests can take longer, especially with setup and teardown -jest.setTimeout(30000); - -describe('LetterRepository', () => { +jest.setTimeout(30_000); +describe("LetterRepository", () => { let db: DBContext; let letterRepository: LetterRepository; let logStream: LogStream; @@ -35,9 +43,7 @@ describe('LetterRepository', () => { beforeEach(async () => { await createTables(db); - ( - { logStream, logger } = createTestLogger() - ); + ({ logStream, logger } = createTestLogger()); letterRepository = new LetterRepository(db.docClient, logger, db.config); }); @@ -51,14 +57,18 @@ describe('LetterRepository', () => { await db.container.stop(); }); - async function checkLetterStatus(supplierId: string, letterId: string, status: Letter['status']) { + async function checkLetterStatus( + supplierId: string, + letterId: string, + status: Letter["status"], + ) { const letter = await letterRepository.getLetterById(supplierId, letterId); expect(letter.status).toBe(status); } - test('adds a letter to the database', async () => { - const supplierId = 'supplier1'; - const letterId = 'letter1'; + test("adds a letter to the database", async () => { + const supplierId = "supplier1"; + const letterId = "letter1"; await letterRepository.putLetter(createLetter(supplierId, letterId)); @@ -70,191 +80,264 @@ describe('LetterRepository', () => { expect(letter.reasonText).toBeUndefined(); }); - test('fetches a letter by id', async () => { - await letterRepository.putLetter(createLetter('supplier1', 'letter1')); - const letter = await letterRepository.getLetterById('supplier1', 'letter1'); - expect(letter).toEqual(expect.objectContaining({ - id: 'letter1', - supplierId: 'supplier1', - specificationId: 'specification1', - groupId: 'group1', - status: 'PENDING' - })); + test("fetches a letter by id", async () => { + await letterRepository.putLetter(createLetter("supplier1", "letter1")); + const letter = await letterRepository.getLetterById("supplier1", "letter1"); + expect(letter).toEqual( + expect.objectContaining({ + id: "letter1", + supplierId: "supplier1", + specificationId: "specification1", + groupId: "group1", + status: "PENDING", + }), + ); }); - test('throws an error when fetching a letter that does not exist', async () => { - await expect(letterRepository.getLetterById('supplier1', 'letter1')) - .rejects.toThrow('Letter with id letter1 not found for supplier supplier1'); + test("throws an error when fetching a letter that does not exist", async () => { + await expect( + letterRepository.getLetterById("supplier1", "letter1"), + ).rejects.toThrow( + "Letter with id letter1 not found for supplier supplier1", + ); }); - test('throws an error when creating a letter which already exists', async () => { - await letterRepository.putLetter(createLetter('supplier1', 'letter1')); - await expect(letterRepository.putLetter(createLetter('supplier1', 'letter1'))) - .rejects.toThrow('Letter with id letter1 already exists for supplier supplier1'); + test("throws an error when creating a letter which already exists", async () => { + await letterRepository.putLetter(createLetter("supplier1", "letter1")); + await expect( + letterRepository.putLetter(createLetter("supplier1", "letter1")), + ).rejects.toThrow( + "Letter with id letter1 already exists for supplier supplier1", + ); }); - test('rethrows errors from DynamoDB when creating a letter', async () => { + test("rethrows errors from DynamoDB when creating a letter", async () => { const misconfiguredRepository = new LetterRepository(db.docClient, logger, { ...db.config, - lettersTableName: 'nonexistent-table' + lettersTableName: "nonexistent-table", }); - await expect(misconfiguredRepository.putLetter(createLetter('supplier1', 'letter1'))) - .rejects.toThrow('Cannot do operations on a non-existent table'); + await expect( + misconfiguredRepository.putLetter(createLetter("supplier1", "letter1")), + ).rejects.toThrow("Cannot do operations on a non-existent table"); }); - test('updates a letter\'s status in the database', async () => { - const letter = createLetter('supplier1', 'letter1', 'PENDING'); + test("updates a letter's status in the database", async () => { + const letter = createLetter("supplier1", "letter1", "PENDING"); await letterRepository.putLetter(letter); - await checkLetterStatus('supplier1', 'letter1', 'PENDING'); + await checkLetterStatus("supplier1", "letter1", "PENDING"); const letterDto: LetterDto = { - id: 'letter1', - supplierId: 'supplier1', - status: 'REJECTED', - reasonCode: 1, - reasonText: 'Reason text' + id: "letter1", + supplierId: "supplier1", + status: "REJECTED", + reasonCode: "R01", + reasonText: "Reason text", }; await letterRepository.updateLetterStatus(letterDto); - const updatedLetter = await letterRepository.getLetterById('supplier1', 'letter1'); - expect(updatedLetter.status).toBe('REJECTED'); - expect(updatedLetter.reasonCode).toBe(1); - expect(updatedLetter.reasonText).toBe('Reason text'); + const updatedLetter = await letterRepository.getLetterById( + "supplier1", + "letter1", + ); + expect(updatedLetter.status).toBe("REJECTED"); + expect(updatedLetter.reasonCode).toBe("R01"); + expect(updatedLetter.reasonText).toBe("Reason text"); }); - test('updates a letter\'s updatedAt date', async () => { + test("updates a letter's updatedAt date", async () => { jest.useFakeTimers(); jest.setSystemTime(new Date(2020, 1, 1)); - await letterRepository.putLetter(createLetter('supplier1', 'letter1', 'PENDING')); - const originalLetter = await letterRepository.getLetterById('supplier1', 'letter1'); - expect(originalLetter.updatedAt).toBe('2020-02-01T00:00:00.000Z'); + await letterRepository.putLetter( + createLetter("supplier1", "letter1", "PENDING"), + ); + const originalLetter = await letterRepository.getLetterById( + "supplier1", + "letter1", + ); + expect(originalLetter.updatedAt).toBe("2020-02-01T00:00:00.000Z"); // Month is zero-indexed in JavaScript Date // Day is one-indexed jest.setSystemTime(new Date(2020, 1, 2)); const letterDto: LetterDto = { - id: 'letter1', - supplierId: 'supplier1', - status: 'DELIVERED' + id: "letter1", + supplierId: "supplier1", + status: "DELIVERED", }; await letterRepository.updateLetterStatus(letterDto); - const updatedLetter = await letterRepository.getLetterById('supplier1', 'letter1'); + const updatedLetter = await letterRepository.getLetterById( + "supplier1", + "letter1", + ); - expect(updatedLetter.updatedAt).toBe('2020-02-02T00:00:00.000Z'); + expect(updatedLetter.updatedAt).toBe("2020-02-02T00:00:00.000Z"); }); - test('can\'t update a letter that does not exist', async () => { + test("can't update a letter that does not exist", async () => { const letterDto: LetterDto = { - id: 'letter1', - supplierId: 'supplier1', - status: 'DELIVERED' + id: "letter1", + supplierId: "supplier1", + status: "DELIVERED", }; - await expect(letterRepository.updateLetterStatus(letterDto)) - .rejects.toThrow('Letter with id letter1 not found for supplier supplier1'); + await expect( + letterRepository.updateLetterStatus(letterDto), + ).rejects.toThrow( + "Letter with id letter1 not found for supplier supplier1", + ); }); - test('update letter status rethrows errors from DynamoDB', async () => { + test("update letter status rethrows errors from DynamoDB", async () => { const misconfiguredRepository = new LetterRepository(db.docClient, logger, { ...db.config, - lettersTableName: 'nonexistent-table' + lettersTableName: "nonexistent-table", }); const letterDto: LetterDto = { - id: 'letter1', - supplierId: 'supplier1', - status: 'DELIVERED' + id: "letter1", + supplierId: "supplier1", + status: "DELIVERED", }; - await expect(misconfiguredRepository.updateLetterStatus(letterDto)) - .rejects.toThrow('Cannot do operations on a non-existent table'); + await expect( + misconfiguredRepository.updateLetterStatus(letterDto), + ).rejects.toThrow("Cannot do operations on a non-existent table"); }); - test('should return a list of letters matching status', async () => { - await letterRepository.putLetter(createLetter('supplier1', 'letter1')); - await letterRepository.putLetter(createLetter('supplier1', 'letter2')); - await letterRepository.putLetter(createLetter('supplier1', 'letter3', 'DELIVERED')); + test("should return a list of letters matching status", async () => { + await letterRepository.putLetter(createLetter("supplier1", "letter1")); + await letterRepository.putLetter(createLetter("supplier1", "letter2")); + await letterRepository.putLetter( + createLetter("supplier1", "letter3", "DELIVERED"), + ); - const pendingLetters = await letterRepository.getLettersByStatus('supplier1', 'PENDING'); + const pendingLetters = await letterRepository.getLettersByStatus( + "supplier1", + "PENDING", + ); expect(pendingLetters.letters).toHaveLength(2); - expect(pendingLetters.letters.map(l => l.id)).toEqual(['letter1', 'letter2']); + expect(pendingLetters.letters.map((l) => l.id)).toEqual([ + "letter1", + "letter2", + ]); - const deliveredLetters = await letterRepository.getLettersByStatus('supplier1', 'DELIVERED'); + const deliveredLetters = await letterRepository.getLettersByStatus( + "supplier1", + "DELIVERED", + ); expect(deliveredLetters.letters).toHaveLength(1); - expect(deliveredLetters.letters[0].id).toBe('letter3'); + expect(deliveredLetters.letters[0].id).toBe("letter3"); }); - test('letter list should change when letter status is updated', async () => { - await letterRepository.putLetter(createLetter('supplier1', 'letter1')); - await letterRepository.putLetter(createLetter('supplier1', 'letter2')); + test("letter list should change when letter status is updated", async () => { + await letterRepository.putLetter(createLetter("supplier1", "letter1")); + await letterRepository.putLetter(createLetter("supplier1", "letter2")); - const pendingLetters = await letterRepository.getLettersByStatus('supplier1', 'PENDING'); + const pendingLetters = await letterRepository.getLettersByStatus( + "supplier1", + "PENDING", + ); expect(pendingLetters.letters).toHaveLength(2); const letterDto: LetterDto = { - id: 'letter1', - supplierId: 'supplier1', - status: 'DELIVERED' + id: "letter1", + supplierId: "supplier1", + status: "DELIVERED", }; await letterRepository.updateLetterStatus(letterDto); - const remainingLetters = await letterRepository.getLettersByStatus('supplier1', 'PENDING'); + const remainingLetters = await letterRepository.getLettersByStatus( + "supplier1", + "PENDING", + ); expect(remainingLetters.letters).toHaveLength(1); - expect(remainingLetters.letters[0].id).toBe('letter2'); + expect(remainingLetters.letters[0].id).toBe("letter2"); - const updatedLetters = await letterRepository.getLettersByStatus('supplier1', 'DELIVERED'); + const updatedLetters = await letterRepository.getLettersByStatus( + "supplier1", + "DELIVERED", + ); expect(updatedLetters.letters).toHaveLength(1); - expect(updatedLetters.letters[0].id).toBe('letter1'); + expect(updatedLetters.letters[0].id).toBe("letter1"); }); - test('letter list should support pagination', async () => { + test("letter list should support pagination", async () => { for (let i = 1; i <= 99; i++) { - await letterRepository.putLetter(createLetter('supplier1', `letter${('000' + i).slice(-3)}`)); + const paddedId = i.toString().padStart(3, "0"); + await letterRepository.putLetter( + createLetter("supplier1", `letter${paddedId}`), + ); } - const firstPage = await letterRepository.getLettersByStatus('supplier1', 'PENDING'); + const firstPage = await letterRepository.getLettersByStatus( + "supplier1", + "PENDING", + ); expect(firstPage.letters).toHaveLength(50); // Default page size is 50 expect(firstPage.lastEvaluatedKey).toBeDefined(); - expect(firstPage.letters[0].id).toBe('letter001'); - expect(firstPage.letters[49].id).toBe('letter050'); + expect(firstPage.letters[0].id).toBe("letter001"); + expect(firstPage.letters[49].id).toBe("letter050"); - const secondPage = await letterRepository.getLettersByStatus('supplier1', 'PENDING', { - exclusiveStartKey: firstPage.lastEvaluatedKey - }); + const secondPage = await letterRepository.getLettersByStatus( + "supplier1", + "PENDING", + { + exclusiveStartKey: firstPage.lastEvaluatedKey, + }, + ); expect(secondPage.letters).toHaveLength(49); expect(secondPage.lastEvaluatedKey).toBeUndefined(); // No more pages - expect(secondPage.letters[0].id).toBe('letter051'); - expect(secondPage.letters[48].id).toBe('letter099'); + expect(secondPage.letters[0].id).toBe("letter051"); + expect(secondPage.letters[48].id).toBe("letter099"); }); - test('letter list should return empty when no letters match status', async () => { - await letterRepository.putLetter(createLetter('supplier1', 'letter1', 'ACCEPTED')); - const page = await letterRepository.getLettersByStatus('supplier1', 'PENDING'); + test("letter list should return empty when no letters match status", async () => { + await letterRepository.putLetter( + createLetter("supplier1", "letter1", "ACCEPTED"), + ); + const page = await letterRepository.getLettersByStatus( + "supplier1", + "PENDING", + ); expect(page.letters).toHaveLength(0); expect(page.lastEvaluatedKey).toBeUndefined(); }); - test('letter list should warn about invalid data', async () => { - await letterRepository.putLetter(createLetter('supplier1', 'letter1')); - await db.docClient.send(new PutCommand({ - TableName: db.config.lettersTableName, - Item: { - supplierId: 'supplier1', - id: 'invalid-letter', - // specificationId: 'specification1', // Missing required field - groupId: 'group1', - url: 's3://bucket/invalid-letter.pdf', - status: 'PENDING', - supplierStatus: 'supplier1#PENDING', - supplierStatusSk: Date.now().toString(), - createdAt: new Date().toISOString(), - updatedAt: new Date().toISOString() - } - })); - - const pendingLetters = await letterRepository.getLettersByStatus('supplier1', 'PENDING'); - expect(pendingLetters.letters).toHaveLength(1); - expect(pendingLetters.letters[0].id).toBe('letter1'); + test("letter list should warn about invalid data", async () => { + await letterRepository.putLetter(createLetter("supplier1", "letter1")); + await db.docClient.send( + new PutCommand({ + TableName: db.config.lettersTableName, + Item: { + supplierId: "supplier1", + id: "invalid-letter", + // specificationId: 'specification1', // Missing required field + groupId: "group1", + url: "s3://bucket/invalid-letter.pdf", + status: "PENDING", + supplierStatus: "supplier1#PENDING", + supplierStatusSk: Date.now().toString(), + createdAt: new Date().toISOString(), + updatedAt: new Date().toISOString(), + }, + }), + ); - expect(logStream.logs).toContainEqual(expect.stringMatching(/.*Invalid letter data:.*/)); - expect(logStream.logs).toContainEqual(expect.stringMatching(/.*specificationId.*Invalid input: expected string.*/)); + const pendingLetters = await letterRepository.getLettersByStatus( + "supplier1", + "PENDING", + ); + expect(pendingLetters.letters).toHaveLength(1); + expect(pendingLetters.letters[0].id).toBe("letter1"); + + expect( + logStream.logs.some((log) => log.includes("Invalid letter data:")), + ).toBe(true); + + expect( + logStream.logs.some( + (log) => + log.includes("specificationId") && + log.includes("Invalid input: expected string"), + ), + ).toBe(true); }); test("should return all letters for a supplier status", async () => { @@ -276,95 +359,102 @@ describe('LetterRepository', () => { { id: "letter1", specificationId: "specification1", - groupId: 'group1', + groupId: "group1", status: "PENDING", }, { id: "letter2", specificationId: "specification1", - groupId: 'group1', + groupId: "group1", status: "PENDING", }, { id: "letter3", specificationId: "specification1", - groupId: 'group1', + groupId: "group1", status: "PENDING", }, ]); }); - test('should return empty if no letters exist for a supplier', async () => { - await letterRepository.putLetter(createLetter('supplier1', 'letter1')); - await letterRepository.putLetter(createLetter('supplier1', 'letter2')); + test("should return empty if no letters exist for a supplier", async () => { + await letterRepository.putLetter(createLetter("supplier1", "letter1")); + await letterRepository.putLetter(createLetter("supplier1", "letter2")); - const letters = await letterRepository.getLettersBySupplier('supplier2', 'PENDING', 10); + const letters = await letterRepository.getLettersBySupplier( + "supplier2", + "PENDING", + 10, + ); expect(letters).toEqual([]); }); - test('should return empty if query result Items is null', async () => { - await letterRepository.putLetter(createLetter('supplier1', 'letter1')); + test("should return empty if query result Items is null", async () => { + await letterRepository.putLetter(createLetter("supplier1", "letter1")); const mockSend = jest.fn().mockResolvedValue({ Items: null }); const mockDdbClient = { send: mockSend } as any; - const repo = new LetterRepository(mockDdbClient, { debug: jest.fn() } as any, { lettersTableName: 'letters', lettersTtlHours: 1 }); + const repo = new LetterRepository( + mockDdbClient, + { debug: jest.fn() } as any, + { lettersTableName: "letters", lettersTtlHours: 1 }, + ); - const letters = await repo.getLettersBySupplier('supplier1', 'PENDING', 10); + const letters = await repo.getLettersBySupplier("supplier1", "PENDING", 10); expect(letters).toEqual([]); }); - test('should batch write letters to the database', async () => { + test("should batch write letters to the database", async () => { const letters = [ - createLetter('supplier1', 'letter1'), - createLetter('supplier1', 'letter2'), - createLetter('supplier1', 'letter3') + createLetter("supplier1", "letter1"), + createLetter("supplier1", "letter2"), + createLetter("supplier1", "letter3"), ]; await letterRepository.putLetterBatch(letters); - await checkLetterStatus('supplier1', 'letter1', 'PENDING'); - await checkLetterStatus('supplier1', 'letter2', 'PENDING'); - await checkLetterStatus('supplier1', 'letter3', 'PENDING'); + await checkLetterStatus("supplier1", "letter1", "PENDING"); + await checkLetterStatus("supplier1", "letter2", "PENDING"); + await checkLetterStatus("supplier1", "letter3", "PENDING"); }); - test('should batch in calls upto 25', async () => { - const letters = [] - for(let i=0; i<60; i++) { - letters.push(createLetter('supplier1', `letter${i}`)); + test("should batch in calls upto 25", async () => { + const letters = []; + for (let i = 0; i < 60; i++) { + letters.push(createLetter("supplier1", `letter${i}`)); } - const sendSpy = jest.spyOn(db.docClient, 'send'); + const sendSpy = jest.spyOn(db.docClient, "send"); await letterRepository.putLetterBatch(letters); expect(sendSpy).toHaveBeenCalledTimes(3); - await checkLetterStatus('supplier1', 'letter1', 'PENDING'); - await checkLetterStatus('supplier1', 'letter6', 'PENDING'); - await checkLetterStatus('supplier1', 'letter59', 'PENDING'); + await checkLetterStatus("supplier1", "letter1", "PENDING"); + await checkLetterStatus("supplier1", "letter6", "PENDING"); + await checkLetterStatus("supplier1", "letter59", "PENDING"); }); - test('should skip array gaps', async () => { - const letters = [ - createLetter('supplier1', 'letter1'), - createLetter('supplier1', 'letter2'), - createLetter('supplier1', 'letter3') - ]; - - delete letters[1]; + test("should skip array gaps", async () => { + const letters = []; + letters[0] = createLetter("supplier1", "letter1"); + letters[2] = createLetter("supplier1", "letter3"); await letterRepository.putLetterBatch(letters); - await checkLetterStatus('supplier1', 'letter1', 'PENDING'); - await checkLetterStatus('supplier1', 'letter3', 'PENDING'); + await checkLetterStatus("supplier1", "letter1", "PENDING"); + await checkLetterStatus("supplier1", "letter3", "PENDING"); }); - test('rethrows errors from DynamoDB when batch creating letter', async () => { + test("rethrows errors from DynamoDB when batch creating letter", async () => { const misconfiguredRepository = new LetterRepository(db.docClient, logger, { ...db.config, - lettersTableName: 'nonexistent-table' + lettersTableName: "nonexistent-table", }); - await expect(misconfiguredRepository.putLetterBatch([createLetter('supplier1', 'letter1')])) - .rejects.toThrow('Cannot do operations on a non-existent table'); + await expect( + misconfiguredRepository.putLetterBatch([ + createLetter("supplier1", "letter1"), + ]), + ).rejects.toThrow("Cannot do operations on a non-existent table"); }); }); diff --git a/internal/datastore/src/__test__/logs.ts b/internal/datastore/src/__test__/logs.ts index e9411de9..e96b639b 100644 --- a/internal/datastore/src/__test__/logs.ts +++ b/internal/datastore/src/__test__/logs.ts @@ -1,5 +1,5 @@ -import pino from 'pino'; -import { Writable } from 'stream'; +import pino from "pino"; +import { Writable } from "node:stream"; export class LogStream extends Writable { logs: string[] = []; @@ -11,9 +11,9 @@ export class LogStream extends Writable { } export function createTestLogger() { - let logStream = new LogStream(); + const logStream = new LogStream(); return { - logStream: logStream, - logger: pino(logStream) + logStream, + logger: pino(logStream), }; } diff --git a/internal/datastore/src/__test__/mi-repository.test.ts b/internal/datastore/src/__test__/mi-repository.test.ts index b0fa2e39..dd34e5d1 100644 --- a/internal/datastore/src/__test__/mi-repository.test.ts +++ b/internal/datastore/src/__test__/mi-repository.test.ts @@ -1,28 +1,28 @@ import { Logger } from "pino"; -import { setupDynamoDBContainer, createTables, DBContext, deleteTables } from "./db"; -import { createTestLogger, LogStream } from "./logs"; +import { + DBContext, + createTables, + deleteTables, + setupDynamoDBContainer, +} from "./db"; +import { createTestLogger } from "./logs"; import { MIRepository } from "../mi-repository"; // Database tests can take longer, especially with setup and teardown -jest.setTimeout(30000); +jest.setTimeout(30_000); - -describe('MiRepository', () => { +describe("MiRepository", () => { let db: DBContext; let miRepository: MIRepository; - let logStream: LogStream; let logger: Logger; - beforeAll(async () => { db = await setupDynamoDBContainer(); }); beforeEach(async () => { await createTables(db); - ( - { logStream, logger } = createTestLogger() - ); + ({ logger } = createTestLogger()); miRepository = new MIRepository(db.docClient, logger, db.config); }); @@ -36,32 +36,32 @@ describe('MiRepository', () => { await db.container.stop(); }); - describe('putMi', () => { - - it('creates a letter with id and timestamps', async () => { - + describe("putMi", () => { + it("creates a letter with id and timestamps", async () => { jest.useFakeTimers(); // Month is zero-indexed in JS Date jest.setSystemTime(new Date(2020, 1, 1)); const mi = { - specificationId: 'spec1', - supplierId: 'supplier1', - groupId:'group1', - lineItem: 'item1', + specificationId: "spec1", + supplierId: "supplier1", + groupId: "group1", + lineItem: "item1", quantity: 12, timestamp: new Date().toISOString(), - stockRemaining: 0 + stockRemaining: 0, }; - const persistedMi = await(miRepository.putMI(mi)); + const persistedMi = await miRepository.putMI(mi); - expect(persistedMi).toEqual(expect.objectContaining({ - id: expect.any(String), - createdAt: '2020-02-01T00:00:00.000Z', - updatedAt: '2020-02-01T00:00:00.000Z', - ttl: 1580518800, // 2020-02-01T00:01:00.000Z, seconds since epoch - ...mi - })); + expect(persistedMi).toEqual( + expect.objectContaining({ + id: expect.any(String), + createdAt: "2020-02-01T00:00:00.000Z", + updatedAt: "2020-02-01T00:00:00.000Z", + ttl: 1_580_518_800, // 2020-02-01T00:01:00.000Z, seconds since epoch + ...mi, + }), + ); }); }); }); diff --git a/internal/datastore/src/__test__/supplier-repository.test.ts b/internal/datastore/src/__test__/supplier-repository.test.ts new file mode 100644 index 00000000..23f98cdd --- /dev/null +++ b/internal/datastore/src/__test__/supplier-repository.test.ts @@ -0,0 +1,151 @@ +import { Logger } from "pino"; +import { randomUUID } from "node:crypto"; +import { + DBContext, + createTables, + deleteTables, + setupDynamoDBContainer, +} from "./db"; +import { createTestLogger } from "./logs"; +import { SupplierRepository } from "../supplier-repository"; +import { Supplier } from "../types"; + +function createSupplier( + status: "ENABLED" | "DISABLED", + apimId = randomUUID(), +): Omit { + return { + id: randomUUID(), + name: "Supplier One", + apimId, + status, + }; +} + +// Database tests can take longer, especially with setup and teardown +jest.setTimeout(30_000); + +describe("SupplierRepository", () => { + let db: DBContext; + let supplierRepository: SupplierRepository; + let logger: Logger; + + beforeAll(async () => { + db = await setupDynamoDBContainer(); + }); + + beforeEach(async () => { + await createTables(db); + ({ logger } = createTestLogger()); + + supplierRepository = new SupplierRepository( + db.docClient, + logger, + db.config, + ); + }); + + afterEach(async () => { + await deleteTables(db); + jest.useRealTimers(); + }); + + afterAll(async () => { + await db.container.stop(); + }); + + test("creates an enabled supplier with provided values and timestamps", async () => { + jest.useFakeTimers(); + // Month is zero-indexed in JS Date + jest.setSystemTime(new Date(2020, 1, 1)); + + const supplier = createSupplier("ENABLED"); + + const persistedSupplier = await supplierRepository.putSupplier(supplier); + + expect(persistedSupplier).toEqual( + expect.objectContaining({ + ...supplier, + updatedAt: "2020-02-01T00:00:00.000Z", + }), + ); + }); + + test("fetches a supplier by its ID", async () => { + const supplier = createSupplier("DISABLED"); + await supplierRepository.putSupplier(supplier); + + const fetched = await supplierRepository.getSupplierById(supplier.id); + + expect(fetched).toEqual( + expect.objectContaining({ + ...supplier, + }), + ); + }); + + test("throws an error fetching a supplier that does not exist", async () => { + await expect( + supplierRepository.getSupplierById("non-existent-id"), + ).rejects.toThrow("Supplier with id non-existent-id not found"); + }); + + test("overwrites an existing supplier entry", async () => { + const supplier = createSupplier("DISABLED"); + + const original = await supplierRepository.putSupplier(supplier); + expect(original.status).toBe("DISABLED"); + + supplier.status = "ENABLED"; + const updated = await supplierRepository.putSupplier(supplier); + expect(updated.status).toBe("ENABLED"); + }); + + test("rethrows errors from DynamoDB when creating a letter", async () => { + const misconfiguredRepository = new SupplierRepository( + db.docClient, + logger, + { + ...db.config, + suppliersTableName: "nonexistent-table", + }, + ); + await expect( + misconfiguredRepository.putSupplier(createSupplier("ENABLED")), + ).rejects.toThrow("Cannot do operations on a non-existent table"); + }); + + test("fetches a supplier by apimId", async () => { + const supplier = createSupplier("ENABLED"); + + await supplierRepository.putSupplier(supplier); + + const fetched = await supplierRepository.getSupplierByApimId( + supplier.apimId, + ); + expect(fetched).toEqual( + expect.objectContaining({ + ...supplier, + }), + ); + }); + + test("throws an error fetching a supplier by apimId that does not exist", async () => { + await expect( + supplierRepository.getSupplierByApimId("non-existent-apim-id"), + ).rejects.toThrow("Supplier with apimId non-existent-apim-id not found"); + }); + + test("throws an error fetching a supplier by apimId when multiple exist", async () => { + const apimId = randomUUID(); + const supplier1 = createSupplier("ENABLED", apimId); + const supplier2 = createSupplier("DISABLED", apimId); + + await supplierRepository.putSupplier(supplier1); + await supplierRepository.putSupplier(supplier2); + + await expect( + supplierRepository.getSupplierByApimId(apimId), + ).rejects.toThrow(`Multiple suppliers found with apimId ${apimId}`); + }); +}); diff --git a/internal/datastore/src/cli/diagrams.ts b/internal/datastore/src/cli/diagrams.ts index c9e8ad3c..d7667f6a 100644 --- a/internal/datastore/src/cli/diagrams.ts +++ b/internal/datastore/src/cli/diagrams.ts @@ -1,25 +1,39 @@ -import {LetterSchema, MISchema, SupplierSchema} from '../types'; -import { generateMermaidDiagram } from 'zod-mermaid'; -import * as fs from 'node:fs'; +import { generateMermaidDiagram } from "zod-mermaid"; +import * as fs from "node:fs"; +import { + LetterSchema, + MISchema, + SupplierSchema, +} from "internal/datastore/src/types"; -const out = fs.openSync('src/types.md', 'w'); +const out = fs.openSync("src/types.md", "w"); -fs.writeSync(out, `# Data Store Schemas +fs.writeSync( + out, + `# Data Store Schemas This document contains the mermaid diagrams for the data store schemas used in the application. The schemas are generated from Zod definitions and provide a visual representation of the data structure. -`); +`, +); -for (const [name, schema] of Object.entries({ Letter: [LetterSchema], MI: [MISchema], Supplier: [SupplierSchema] })) { +for (const [name, schema] of Object.entries({ + Letter: [LetterSchema], + MI: [MISchema], + Supplier: [SupplierSchema], +})) { const mermaid = generateMermaidDiagram(schema); - fs.writeSync(out, ` + fs.writeSync( + out, + ` ## ${name} schema \`\`\`mermaid ${mermaid} \`\`\` -`); +`, + ); } fs.closeSync(out); diff --git a/internal/datastore/src/config.ts b/internal/datastore/src/config.ts index 92081caf..6440942e 100644 --- a/internal/datastore/src/config.ts +++ b/internal/datastore/src/config.ts @@ -1,8 +1,9 @@ export type DatastoreConfig = { - region: string, - endpoint?: string, - lettersTableName: string, - miTableName: string, - lettersTtlHours: number, - miTtlHours: number -} + region: string; + endpoint?: string; + lettersTableName: string; + miTableName: string; + suppliersTableName: string; + lettersTtlHours: number; + miTtlHours: number; +}; diff --git a/internal/datastore/src/healthcheck.ts b/internal/datastore/src/healthcheck.ts new file mode 100644 index 00000000..62e1aa1b --- /dev/null +++ b/internal/datastore/src/healthcheck.ts @@ -0,0 +1,18 @@ +import { DescribeTableCommand } from "@aws-sdk/client-dynamodb"; +import { DynamoDBDocumentClient } from "@aws-sdk/lib-dynamodb"; +import { LetterRepositoryConfig } from "./letter-repository"; + +export default class DBHealthcheck { + constructor( + readonly ddbClient: DynamoDBDocumentClient, + readonly config: LetterRepositoryConfig, + ) {} + + async check(): Promise { + await this.ddbClient.send( + new DescribeTableCommand({ + TableName: this.config.lettersTableName, + }), + ); + } +} diff --git a/internal/datastore/src/index.ts b/internal/datastore/src/index.ts index 20f80e92..53d52cd6 100644 --- a/internal/datastore/src/index.ts +++ b/internal/datastore/src/index.ts @@ -1,4 +1,5 @@ -export * from './types'; -export * from './mi-repository'; -export * from './letter-repository'; -export * from './types'; +export * from "./types"; +export * from "./mi-repository"; +export * from "./letter-repository"; +export * from "./supplier-repository"; +export { default as DBHealthcheck } from "./healthcheck"; diff --git a/internal/datastore/src/letter-repository.ts b/internal/datastore/src/letter-repository.ts index 05bc0e3d..e9fd85d0 100644 --- a/internal/datastore/src/letter-repository.ts +++ b/internal/datastore/src/letter-repository.ts @@ -1,85 +1,98 @@ import { + BatchWriteCommand, DynamoDBDocumentClient, GetCommand, PutCommand, - BatchWriteCommand, QueryCommand, UpdateCommand, - UpdateCommandOutput -} from '@aws-sdk/lib-dynamodb'; -import { Letter, LetterBase, LetterSchema, LetterSchemaBase } from './types'; -import { Logger } from 'pino'; -import { z } from 'zod'; -import { LetterDto } from '../../../lambdas/api-handler/src/contracts/letters'; + UpdateCommandOutput, +} from "@aws-sdk/lib-dynamodb"; +import { Logger } from "pino"; +import { z } from "zod"; +import { Letter, LetterBase, LetterSchema, LetterSchemaBase } from "./types"; +import { LetterDto } from "../../../lambdas/api-handler/src/contracts/letters"; export type PagingOptions = Partial<{ - exclusiveStartKey: Record, - pageSize: number -}> + exclusiveStartKey: Record; + pageSize: number; +}>; const defaultPagingOptions = { - pageSize: 50 + pageSize: 50, }; export type LetterRepositoryConfig = { - lettersTableName: string, - lettersTtlHours: number -} + lettersTableName: string; + lettersTtlHours: number; +}; export class LetterRepository { - constructor(readonly ddbClient: DynamoDBDocumentClient, + constructor( + readonly ddbClient: DynamoDBDocumentClient, readonly log: Logger, - readonly config: LetterRepositoryConfig) { - } + readonly config: LetterRepositoryConfig, + ) {} - async putLetter(letter: Omit): Promise { + async putLetter( + letter: Omit, + ): Promise { const letterDb: Letter = { ...letter, supplierStatus: `${letter.supplierId}#${letter.status}`, supplierStatusSk: new Date().toISOString(), - ttl: Math.floor(Date.now() / 1000 + 60 * 60 * this.config.lettersTtlHours) + ttl: Math.floor( + Date.now() / 1000 + 60 * 60 * this.config.lettersTtlHours, + ), }; try { - await this.ddbClient.send(new PutCommand({ - TableName: this.config.lettersTableName, - Item: letterDb, - ConditionExpression: 'attribute_not_exists(id)', // Ensure id is unique - })); + await this.ddbClient.send( + new PutCommand({ + TableName: this.config.lettersTableName, + Item: letterDb, + ConditionExpression: "attribute_not_exists(id)", // Ensure id is unique + }), + ); } catch (error) { - if (error instanceof Error && error.name === 'ConditionalCheckFailedException') { - throw new Error(`Letter with id ${letter.id} already exists for supplier ${letter.supplierId}`); + if ( + error instanceof Error && + error.name === "ConditionalCheckFailedException" + ) { + throw new Error( + `Letter with id ${letter.id} already exists for supplier ${letter.supplierId}`, + ); } throw error; } return LetterSchema.parse(letterDb); } - async putLetterBatch(letters: Omit[]): Promise { + async putLetterBatch( + letters: Omit[], + ): Promise { let lettersDb: Letter[] = []; for (let i = 0; i < letters.length; i++) { - const letter = letters[i]; - if(!letter){ - continue; + if (letter) { + lettersDb.push({ + ...letter, + supplierStatus: `${letter.supplierId}#${letter.status}`, + supplierStatusSk: Date.now().toString(), + ttl: Math.floor( + Date.now() / 1000 + 60 * 60 * this.config.lettersTtlHours, + ), + }); } - lettersDb.push({ - ...letter, - supplierStatus: `${letter.supplierId}#${letter.status}`, - supplierStatusSk: Date.now().toString(), - ttl: Math.floor(Date.now() / 1000 + 60 * 60 * this.config.lettersTtlHours) - }); - if (lettersDb.length === 25 || i === letters.length - 1) { const input = { RequestItems: { [this.config.lettersTableName]: lettersDb.map((item: any) => ({ PutRequest: { - Item: item - } - })) - } + Item: item, + }, + })), + }, }; await this.ddbClient.send(new BatchWriteCommand(input)); @@ -90,115 +103,148 @@ export class LetterRepository { } async getLetterById(supplierId: string, letterId: string): Promise { - const result = await this.ddbClient.send(new GetCommand({ - TableName: this.config.lettersTableName, - Key: { - supplierId: supplierId, - id: letterId - } - })); + const result = await this.ddbClient.send( + new GetCommand({ + TableName: this.config.lettersTableName, + Key: { + supplierId, + id: letterId, + }, + }), + ); if (!result.Item) { - throw new Error(`Letter with id ${letterId} not found for supplier ${supplierId}`); + throw new Error( + `Letter with id ${letterId} not found for supplier ${supplierId}`, + ); } return LetterSchema.parse(result.Item); } - async getLettersByStatus(supplierId: string, status: Letter['status'], options?: PagingOptions): Promise<{ - letters: Letter[], - lastEvaluatedKey?: Record + async getLettersByStatus( + supplierId: string, + status: Letter["status"], + options?: PagingOptions, + ): Promise<{ + letters: Letter[]; + lastEvaluatedKey?: Record; }> { - const extendedOptions = { ...defaultPagingOptions, ...options }; - const result = await this.ddbClient.send(new QueryCommand({ - TableName: this.config.lettersTableName, - IndexName: 'supplierStatus-index', - KeyConditionExpression: 'supplierStatus = :supplierStatus', - ExpressionAttributeValues: { ':supplierStatus': `${supplierId}#${status}` }, - Limit: extendedOptions.pageSize, - ExclusiveStartKey: extendedOptions.exclusiveStartKey - })); + const result = await this.ddbClient.send( + new QueryCommand({ + TableName: this.config.lettersTableName, + IndexName: "supplierStatus-index", + KeyConditionExpression: "supplierStatus = :supplierStatus", + ExpressionAttributeValues: { + ":supplierStatus": `${supplierId}#${status}`, + }, + Limit: extendedOptions.pageSize, + ExclusiveStartKey: extendedOptions.exclusiveStartKey, + }), + ); + + // Items is an empty array if no items match the query + const letters = result + .Items!.map((item) => LetterSchema.safeParse(item)) + .filter((letterItem) => { + if (!letterItem.success) { + this.log.warn(`Invalid letter data: ${letterItem.error}`); + } + return letterItem.success; + }) + .map((successLetterItem) => successLetterItem.data); return { - // Items is an empty array if no items match the query - letters: result.Items!.map(item => LetterSchema.safeParse(item)) - .filter((result) => { - if (!result.success) { - this.log.warn(`Invalid letter data: ${result.error}`); - } - return result.success; - }) - .map(result => result.data), - lastEvaluatedKey: result.LastEvaluatedKey - } + letters, + lastEvaluatedKey: result.LastEvaluatedKey, + }; } async updateLetterStatus(letterToUpdate: LetterDto): Promise { - this.log.debug(`Updating letter ${letterToUpdate.id} to status ${letterToUpdate.status}`); + this.log.debug( + `Updating letter ${letterToUpdate.id} to status ${letterToUpdate.status}`, + ); let result: UpdateCommandOutput; try { - let updateExpression = 'set #status = :status, updatedAt = :updatedAt, supplierStatus = :supplierStatus, #ttl = :ttl'; - let expressionAttributeValues : Record = { - ':status': letterToUpdate.status, - ':updatedAt': new Date().toISOString(), - ':supplierStatus': `${letterToUpdate.supplierId}#${letterToUpdate.status}`, - ':ttl': Math.floor(Date.now() / 1000 + 60 * 60 * this.config.lettersTtlHours) + let updateExpression = + "set #status = :status, updatedAt = :updatedAt, supplierStatus = :supplierStatus, #ttl = :ttl"; + const expressionAttributeValues: Record = { + ":status": letterToUpdate.status, + ":updatedAt": new Date().toISOString(), + ":supplierStatus": `${letterToUpdate.supplierId}#${letterToUpdate.status}`, + ":ttl": Math.floor( + Date.now() / 1000 + 60 * 60 * this.config.lettersTtlHours, + ), }; - if (letterToUpdate.reasonCode) - { - updateExpression += ', reasonCode = :reasonCode'; - expressionAttributeValues[':reasonCode'] = letterToUpdate.reasonCode; + if (letterToUpdate.reasonCode) { + updateExpression += ", reasonCode = :reasonCode"; + expressionAttributeValues[":reasonCode"] = letterToUpdate.reasonCode; } - if (letterToUpdate.reasonText) - { - updateExpression += ', reasonText = :reasonText'; - expressionAttributeValues[':reasonText'] = letterToUpdate.reasonText; + if (letterToUpdate.reasonText) { + updateExpression += ", reasonText = :reasonText"; + expressionAttributeValues[":reasonText"] = letterToUpdate.reasonText; } - result = await this.ddbClient.send(new UpdateCommand({ - TableName: this.config.lettersTableName, - Key: { - supplierId: letterToUpdate.supplierId, - id: letterToUpdate.id - }, - UpdateExpression: updateExpression, - ConditionExpression: 'attribute_exists(id)', // Ensure letter exists - ExpressionAttributeNames: { - '#status': 'status', - '#ttl': 'ttl' - }, - ExpressionAttributeValues: expressionAttributeValues, - ReturnValues: 'ALL_NEW' - })); + result = await this.ddbClient.send( + new UpdateCommand({ + TableName: this.config.lettersTableName, + Key: { + supplierId: letterToUpdate.supplierId, + id: letterToUpdate.id, + }, + UpdateExpression: updateExpression, + ConditionExpression: "attribute_exists(id)", // Ensure letter exists + ExpressionAttributeNames: { + "#status": "status", + "#ttl": "ttl", + }, + ExpressionAttributeValues: expressionAttributeValues, + ReturnValues: "ALL_NEW", + }), + ); } catch (error) { - if (error instanceof Error && error.name === 'ConditionalCheckFailedException') { - throw new Error(`Letter with id ${letterToUpdate.id} not found for supplier ${letterToUpdate.supplierId}`); + if ( + error instanceof Error && + error.name === "ConditionalCheckFailedException" + ) { + throw new Error( + `Letter with id ${letterToUpdate.id} not found for supplier ${letterToUpdate.supplierId}`, + ); } throw error; } - this.log.debug(`Updated letter ${letterToUpdate.id} to status ${letterToUpdate.status}`); + this.log.debug( + `Updated letter ${letterToUpdate.id} to status ${letterToUpdate.status}`, + ); return LetterSchema.parse(result.Attributes); } - async getLettersBySupplier(supplierId: string, status: string, limit: number): Promise { + async getLettersBySupplier( + supplierId: string, + status: string, + limit: number, + ): Promise { const supplierStatus = `${supplierId}#${status}`; - const result = await this.ddbClient.send(new QueryCommand({ - TableName: this.config.lettersTableName, - IndexName: 'supplierStatus-index', - KeyConditionExpression: 'supplierStatus = :supplierStatus', - Limit: limit, - ExpressionAttributeNames: { - '#status': 'status' // reserved keyword - }, - ExpressionAttributeValues: { - ':supplierStatus': supplierStatus - }, - ProjectionExpression: 'id, #status, specificationId, groupId, reasonCode, reasonText' - })); + const result = await this.ddbClient.send( + new QueryCommand({ + TableName: this.config.lettersTableName, + IndexName: "supplierStatus-index", + KeyConditionExpression: "supplierStatus = :supplierStatus", + Limit: limit, + ExpressionAttributeNames: { + "#status": "status", // reserved keyword + }, + ExpressionAttributeValues: { + ":supplierStatus": supplierStatus, + }, + ProjectionExpression: + "id, #status, specificationId, groupId, reasonCode, reasonText", + }), + ); return z.array(LetterSchemaBase).parse(result.Items ?? []); } } diff --git a/internal/datastore/src/mi-repository.ts b/internal/datastore/src/mi-repository.ts index c3d1fd35..1a92fe71 100644 --- a/internal/datastore/src/mi-repository.ts +++ b/internal/datastore/src/mi-repository.ts @@ -1,38 +1,39 @@ -import { - DynamoDBDocumentClient, - PutCommand -} from '@aws-sdk/lib-dynamodb'; -import { MI, MISchema } from './types'; -import { Logger } from 'pino'; -import { v4 as uuidv4 } from 'uuid'; +import { DynamoDBDocumentClient, PutCommand } from "@aws-sdk/lib-dynamodb"; +import { Logger } from "pino"; +import { randomUUID } from "node:crypto"; +import { MI, MISchema } from "./types"; export type MIRepositoryConfig = { - miTableName: string, - miTtlHours: number + miTableName: string; + miTtlHours: number; }; export class MIRepository { - constructor(readonly ddbClient: DynamoDBDocumentClient, + constructor( + readonly ddbClient: DynamoDBDocumentClient, readonly log: Logger, - readonly config: MIRepositoryConfig) { - } - - async putMI(mi: Omit): Promise { + readonly config: MIRepositoryConfig, + ) {} + async putMI( + mi: Omit, + ): Promise { const now = new Date().toISOString(); const miDb = { ...mi, - id: uuidv4(), + id: randomUUID(), createdAt: now, updatedAt: now, - ttl: Math.floor(Date.now() / 1000 + 60 * 60 * this.config.miTtlHours) + ttl: Math.floor(Date.now() / 1000 + 60 * 60 * this.config.miTtlHours), }; - await this.ddbClient.send(new PutCommand({ - TableName: this.config.miTableName, - Item: miDb - })); + await this.ddbClient.send( + new PutCommand({ + TableName: this.config.miTableName, + Item: miDb, + }), + ); return MISchema.parse(miDb); } -}; +} diff --git a/internal/datastore/src/supplier-repository.ts b/internal/datastore/src/supplier-repository.ts new file mode 100644 index 00000000..8846a28b --- /dev/null +++ b/internal/datastore/src/supplier-repository.ts @@ -0,0 +1,77 @@ +import { + DynamoDBDocumentClient, + GetCommand, + PutCommand, + QueryCommand, +} from "@aws-sdk/lib-dynamodb"; +import { Logger } from "pino"; +import { Supplier, SupplierSchema } from "./types"; + +export type SupplierRepositoryConfig = { + suppliersTableName: string; +}; + +export class SupplierRepository { + constructor( + readonly ddbClient: DynamoDBDocumentClient, + readonly log: Logger, + readonly config: SupplierRepositoryConfig, + ) {} + + async putSupplier(supplier: Omit): Promise { + const now = new Date().toISOString(); + const supplierDb = { + ...supplier, + updatedAt: now, + }; + + await this.ddbClient.send( + new PutCommand({ + TableName: this.config.suppliersTableName, + Item: supplierDb, + }), + ); + + return SupplierSchema.parse(supplierDb); + } + + async getSupplierById(supplierId: string): Promise { + const result = await this.ddbClient.send( + new GetCommand({ + TableName: this.config.suppliersTableName, + Key: { + id: supplierId, + }, + }), + ); + + if (!result.Item) { + throw new Error(`Supplier with id ${supplierId} not found`); + } + + return SupplierSchema.parse(result.Item); + } + + async getSupplierByApimId(apimId: string): Promise { + const result = await this.ddbClient.send( + new QueryCommand({ + TableName: this.config.suppliersTableName, + IndexName: "supplier-apim-index", + KeyConditionExpression: "apimId = :apimId", + ExpressionAttributeValues: { + ":apimId": apimId, + }, + }), + ); + + if (result.Count && result.Count > 1) { + throw new Error(`Multiple suppliers found with apimId ${apimId}`); + } + + if (result.Count === 0 || !result.Items) { + throw new Error(`Supplier with apimId ${apimId} not found`); + } + + return SupplierSchema.parse(result.Items[0]); + } +} diff --git a/internal/datastore/src/types.md b/internal/datastore/src/types.md index 0ca598a8..89056843 100644 --- a/internal/datastore/src/types.md +++ b/internal/datastore/src/types.md @@ -10,10 +10,10 @@ The schemas are generated from Zod definitions and provide a visual representati erDiagram Letter { string id - string status "enum: PENDING, ACCEPTED, REJECTED, PRINTED, ENCLOSED, CANCELLED, DISPATCHED, FAILED, RETURNED, DESTROYED, FORWARDED, DELIVERED" + string status "enum: PENDING, ACCEPTED, REJECTED, PRINTED, ENCLOSED, CANCELLED, DISPATCHED, FAILED, RETURNED, FORWARDED, DELIVERED" string specificationId string groupId - number reasonCode + string reasonCode string reasonText string supplierId string url "url" @@ -31,14 +31,16 @@ erDiagram erDiagram MI { string id - string supplierId - string specificationId - string groupId string lineItem + string timestamp number quantity + string specificationId + string groupId number stockRemaining + string supplierId string createdAt string updatedAt + number ttl "min: -9007199254740991, max: 9007199254740991" } ``` @@ -51,5 +53,6 @@ erDiagram string name string apimId string status "enum: ENABLED, DISABLED" + string updatedAt } ``` diff --git a/internal/datastore/src/types.ts b/internal/datastore/src/types.ts index 1eb1e815..65b6df88 100644 --- a/internal/datastore/src/types.ts +++ b/internal/datastore/src/types.ts @@ -1,21 +1,33 @@ -import { z } from 'zod'; -import { idRef } from '@internal/helpers'; +import { z } from "zod"; +import { idRef } from "@internal/helpers"; -export const SupplerStatus = z.enum(['ENABLED', 'DISABLED']); +export const SupplierStatus = z.enum(["ENABLED", "DISABLED"]); -export const SupplierSchema = z.object({ - id: z.string(), - name: z.string(), - apimId: z.string(), - status: SupplerStatus -}).describe('Supplier'); +export const SupplierSchema = z + .object({ + id: z.string(), + name: z.string(), + apimId: z.string(), + status: SupplierStatus, + updatedAt: z.string(), + }) + .describe("Supplier"); export type Supplier = z.infer; export const LetterStatus = z.enum([ - 'PENDING', 'ACCEPTED', 'REJECTED', 'PRINTED', - 'ENCLOSED', 'CANCELLED', 'DISPATCHED', 'FAILED', - 'RETURNED', 'DESTROYED', 'FORWARDED', 'DELIVERED']); + "PENDING", + "ACCEPTED", + "REJECTED", + "PRINTED", + "ENCLOSED", + "CANCELLED", + "DISPATCHED", + "FAILED", + "RETURNED", + "FORWARDED", + "DELIVERED", +]); export type LetterStatusType = z.infer; @@ -24,19 +36,19 @@ export const LetterSchemaBase = z.object({ status: LetterStatus, specificationId: z.string(), groupId: z.string(), - reasonCode: z.number().optional(), - reasonText: z.string().optional() + reasonCode: z.string().optional(), + reasonText: z.string().optional(), }); export const LetterSchema = LetterSchemaBase.extend({ - supplierId: idRef(SupplierSchema, 'id'), + supplierId: idRef(SupplierSchema, "id"), url: z.url(), createdAt: z.string(), updatedAt: z.string(), - supplierStatus: z.string().describe('Secondary index PK'), - supplierStatusSk: z.string().describe('Secondary index SK'), + supplierStatus: z.string().describe("Secondary index PK"), + supplierStatusSk: z.string().describe("Secondary index SK"), ttl: z.int(), -}).describe('Letter'); +}).describe("Letter"); /** * Letter is the type used for storing letters in the database. @@ -53,15 +65,15 @@ export const MISchemaBase = z.object({ quantity: z.number(), specificationId: z.string().optional(), groupId: z.string().optional(), - stockRemaining: z.number().optional() + stockRemaining: z.number().optional(), }); export const MISchema = MISchemaBase.extend({ - supplierId: idRef(SupplierSchema, 'id'), + supplierId: idRef(SupplierSchema, "id"), createdAt: z.string(), updatedAt: z.string(), ttl: z.int(), -}).describe('MI'); +}).describe("MI"); export type MI = z.infer; export type MIBase = z.infer; diff --git a/internal/events/.spectral.yaml b/internal/events/.spectral.yaml new file mode 100644 index 00000000..63790cd0 --- /dev/null +++ b/internal/events/.spectral.yaml @@ -0,0 +1 @@ +extends: ["spectral:oas", "spectral:asyncapi", "spectral:arazzo"] diff --git a/internal/events/jest.config.ts b/internal/events/jest.config.ts new file mode 100644 index 00000000..84251001 --- /dev/null +++ b/internal/events/jest.config.ts @@ -0,0 +1,49 @@ +import type { Config } from "jest"; + +export const baseJestConfig: Config = { + preset: "ts-jest", + + // Automatically clear mock calls, instances, contexts and results before every test + clearMocks: true, + + // Indicates whether the coverage information should be collected while executing the test + collectCoverage: true, + + // The directory where Jest should output its coverage files + coverageDirectory: "./.reports/unit/coverage", + + // Indicates which provider should be used to instrument code for coverage + coverageProvider: "babel", + + coverageThreshold: { + global: { + branches: 100, + functions: 100, + lines: 100, + statements: -10, + }, + }, + + coveragePathIgnorePatterns: ["/__tests__/"], + transform: { "^.+\\.ts$": "ts-jest" }, + testPathIgnorePatterns: [".build"], + testMatch: ["**/?(*.)+(spec|test).[jt]s?(x)"], + + // Use this configuration option to add custom reporters to Jest + reporters: [ + "default", + [ + "jest-html-reporter", + { + pageTitle: "Test Report", + outputPath: "./.reports/unit/test-report.html", + includeFailureMsg: true, + }, + ], + ], + + // The test environment that will be used for testing + testEnvironment: "jsdom", +}; + +export default baseJestConfig; diff --git a/internal/events/package.json b/internal/events/package.json new file mode 100644 index 00000000..09e72bde --- /dev/null +++ b/internal/events/package.json @@ -0,0 +1,54 @@ +{ + "dependencies": { + "@asyncapi/bundler": "^0.6.4", + "zod": "^4.1.11" + }, + "description": "Schemas for NHS Notify Supplier API events", + "devDependencies": { + "@stoplight/spectral-cli": "^6.15.0", + "@stylistic/eslint-plugin": "^3.1.0", + "@tsconfig/node22": "^22.0.2", + "@types/jest": "^30.0.0", + "@typescript-eslint/eslint-plugin": "^8.27.0", + "@typescript-eslint/parser": "^8.27.0", + "eslint": "^9.27.0", + "eslint-plugin-jest": "^29.0.1", + "jest": "^30.2.0", + "ts-jest": "^29.4.0", + "ts-node": "^10.9.2", + "typescript": "^5.9.3" + }, + "license": "MIT", + "main": "dist/index.js", + "name": "@nhsdigital/nhs-notify-event-schemas-supplier-api", + "private": false, + "publishConfig": { + "registry": "https://npm.pkg.github.com" + }, + "repository": { + "type": "git", + "url": "git+ssh://git@github.com/NHSDigital/nhs-notify-supplier-api.git" + }, + "scripts": { + "build": "tsc -p ./tsconfig.build.json", + "dev": "ts-node src/index.ts", + "gen:asyncapi": "mkdir -p ./dist/asyncapi && ts-node src/cli/bundle-asyncapi.ts", + "gen:jsonschema": "ts-node src/cli/generate-json.ts", + "lint": "npm run lint:node && npm run lint:schema", + "lint:fix": "eslint . --fix", + "lint:node": "eslint .", + "lint:schema": "spectral lint client-config/client-config.yaml", + "prebuild": "rm -rf dist && npm run gen:asyncapi", + "pregen:asyncapi": "npm run gen:jsonschema", + "pregen:jsonschema": "rm -rf ./client-config/json", + "prelint:schema": "npm run gen:jsonschema", + "prepublishOnly": "npm run build", + "pretest:unit": "npm run gen:jsonschema", + "start": "node dist/index.js", + "test": "npm run test:unit", + "test:unit": "jest", + "typecheck": "tsc --noEmit" + }, + "types": "dist/index.d.ts", + "version": "1.0.6" +} diff --git a/internal/events/schemas/domain/.gitignore b/internal/events/schemas/domain/.gitignore new file mode 100644 index 00000000..a6c57f5f --- /dev/null +++ b/internal/events/schemas/domain/.gitignore @@ -0,0 +1 @@ +*.json diff --git a/internal/events/schemas/events/.gitignore b/internal/events/schemas/events/.gitignore new file mode 100644 index 00000000..a6c57f5f --- /dev/null +++ b/internal/events/schemas/events/.gitignore @@ -0,0 +1 @@ +*.json diff --git a/internal/events/schemas/examples/letter.ACCEPTED.json b/internal/events/schemas/examples/letter.ACCEPTED.json new file mode 100644 index 00000000..f9a1177c --- /dev/null +++ b/internal/events/schemas/examples/letter.ACCEPTED.json @@ -0,0 +1,26 @@ +{ + "data": { + "domainId": "f47ac10b-58cc-4372-a567-0e02b2c3d479", + "groupId": "client_template", + "origin": { + "domain": "letter-rendering", + "event": "f47ac10b-58cc-4372-a567-0e02b2c3d479", + "source": "/data-plane/letter-rendering/prod/render-pdf", + "subject": "client/00f3b388-bbe9-41c9-9e76-052d37ee8988/letter-request/0o5Fs0EELR0fUjHjbCnEtdUwQe4_0o5Fs0EELR0fUjHjbCnEtdUwQe5" + }, + "specificationId": "1y3q9v1zzzz", + "status": "ACCEPTED" + }, + "datacontenttype": "application/json", + "dataschema": "https://notify.nhs.uk/cloudevents/schemas/supplier-api/letter.ACCEPTED.1.0.0.schema.json", + "id": "23f1f09c-a555-4d9b-8405-0b33490bc920", + "recordedtime": "2025-08-28T08:45:00.000Z", + "severitynumber": 2, + "severitytext": "INFO", + "source": "/data-plane/supplier-api/prod/update-status", + "specversion": "1.0", + "subject": "letter-origin/letter-rendering/letter/f47ac10b-58cc-4372-a567-0e02b2c3d479", + "time": "2025-08-28T08:45:00.000Z", + "traceparent": "00-0af7651916cd43dd8448eb211c80319c-b7ad6b7169203331-01", + "type": "uk.nhs.notify.supplier-api.letter.ACCEPTED.v1" +} diff --git a/internal/events/schemas/examples/letter.FORWARDED.json b/internal/events/schemas/examples/letter.FORWARDED.json new file mode 100644 index 00000000..bf12ed69 --- /dev/null +++ b/internal/events/schemas/examples/letter.FORWARDED.json @@ -0,0 +1,28 @@ +{ + "data": { + "domainId": "f47ac10b-58cc-4372-a567-0e02b2c3d479", + "groupId": "client_template", + "origin": { + "domain": "letter-rendering", + "event": "f47ac10b-58cc-4372-a567-0e02b2c3d479", + "source": "/data-plane/letter-rendering/prod/render-pdf", + "subject": "client/00f3b388-bbe9-41c9-9e76-052d37ee8988/letter-request/0o5Fs0EELR0fUjHjbCnEtdUwQe4_0o5Fs0EELR0fUjHjbCnEtdUwQe5" + }, + "reasonCode": "RNIB", + "reasonText": "RNIB", + "specificationId": "1y3q9v1zzzz", + "status": "FORWARDED" + }, + "datacontenttype": "application/json", + "dataschema": "https://notify.nhs.uk/cloudevents/schemas/supplier-api/letter.FORWARDED.1.0.0.schema.json", + "id": "23f1f09c-a555-4d9b-8405-0b33490bc920", + "recordedtime": "2025-08-28T08:45:00.000Z", + "severitynumber": 2, + "severitytext": "INFO", + "source": "/data-plane/supplier-api/prod/update-status", + "specversion": "1.0", + "subject": "letter-origin/letter-rendering/letter/f47ac10b-58cc-4372-a567-0e02b2c3d479", + "time": "2025-08-28T08:45:00.000Z", + "traceparent": "00-0af7651916cd43dd8448eb211c80319c-b7ad6b7169203331-01", + "type": "uk.nhs.notify.supplier-api.letter.FORWARDED.v1" +} diff --git a/internal/events/schemas/examples/letter.RETURNED.json b/internal/events/schemas/examples/letter.RETURNED.json new file mode 100644 index 00000000..e273029d --- /dev/null +++ b/internal/events/schemas/examples/letter.RETURNED.json @@ -0,0 +1,28 @@ +{ + "data": { + "domainId": "f47ac10b-58cc-4372-a567-0e02b2c3d479", + "groupId": "client_template", + "origin": { + "domain": "letter-rendering", + "event": "f47ac10b-58cc-4372-a567-0e02b2c3d479", + "source": "/data-plane/letter-rendering/prod/render-pdf", + "subject": "client/00f3b388-bbe9-41c9-9e76-052d37ee8988/letter-request/0o5Fs0EELR0fUjHjbCnEtdUwQe4_0o5Fs0EELR0fUjHjbCnEtdUwQe5" + }, + "reasonCode": "R07", + "reasonText": "No such address", + "specificationId": "1y3q9v1zzzz", + "status": "RETURNED" + }, + "datacontenttype": "application/json", + "dataschema": "https://notify.nhs.uk/cloudevents/schemas/supplier-api/letter.RETURNED.1.0.0.schema.json", + "id": "23f1f09c-a555-4d9b-8405-0b33490bc920", + "recordedtime": "2025-08-28T08:45:00.000Z", + "severitynumber": 2, + "severitytext": "INFO", + "source": "/data-plane/supplier-api/prod/update-status", + "specversion": "1.0", + "subject": "letter-origin/letter-rendering/letter/f47ac10b-58cc-4372-a567-0e02b2c3d479", + "time": "2025-08-28T08:45:00.000Z", + "traceparent": "00-0af7651916cd43dd8448eb211c80319c-b7ad6b7169203331-01", + "type": "uk.nhs.notify.supplier-api.letter.RETURNED.v1" +} diff --git a/internal/events/schemas/supplier-api.yaml b/internal/events/schemas/supplier-api.yaml new file mode 100644 index 00000000..a87277dc --- /dev/null +++ b/internal/events/schemas/supplier-api.yaml @@ -0,0 +1,197 @@ +--- +asyncapi: '3.0.0' +info: + title: NHS Notify Supplier API Events + version: 1.x + description: Events generated by the supplier-api bounded context +defaultContentType: application/json + +channels: + data-plane: + title: Data Plane + description: Events relating to message delivery + messages: + letter-accepted: + $ref: '#/components/messages/letter-accepted' + letter-cancelled: + $ref: '#/components/messages/letter-cancelled' + letter-delivered: + $ref: '#/components/messages/letter-delivered' + letter-dispatched: + $ref: '#/components/messages/letter-dispatched' + letter-enclosed: + $ref: '#/components/messages/letter-enclosed' + letter-failed: + $ref: '#/components/messages/letter-failed' + letter-forwarded: + $ref: '#/components/messages/letter-forwarded' + letter-pending: + $ref: '#/components/messages/letter-pending' + letter-printed: + $ref: '#/components/messages/letter-printed' + letter-rejected: + $ref: '#/components/messages/letter-rejected' + letter-returned: + $ref: '#/components/messages/letter-returned' + letter-any: + $ref: '#/components/messages/letter-any' + mi-submitted: + $ref: '#/components/messages/mi-submitted' + +components: + messages: + letter-accepted: + name: letter-accepted + title: Letter Status Accepted + summary: | + A letter status has been updated to accepted. + + This indicates that a letter has been accepted for processing by a supplier. + contentType: application/json + payload: + $ref: './events/letter.ACCEPTED.schema.json' + examples: + - payload: + $ref: './examples/letter.ACCEPTED.json' + + letter-cancelled: + name: letter-cancelled + title: Letter Status Cancelled + summary: | + A letter status has been updated to cancelled. + + This indicates that a letter has been cancelled and will not be printed or dispatched. + contentType: application/json + payload: + $ref: './events/letter.CANCELLED.schema.json' + + letter-delivered: + name: letter-delivered + title: Letter Status Delivered + summary: | + A letter status has been updated to delivered. + + This indicates that a letter has been delivered to the recipient. + contentType: application/json + payload: + $ref: './events/letter.DELIVERED.schema.json' + + letter-dispatched: + name: letter-dispatched + title: Letter Status Dispatched + summary: | + A letter status has been updated to dispatched. + + This indicates that a letter has been dispatched from the supplier to a down-stream access (DSA) provider. + contentType: application/json + payload: + $ref: './events/letter.DISPATCHED.schema.json' + + letter-enclosed: + name: letter-enclosed + title: Letter Status Enclosed + summary: | + A letter status has been updated to enclosed. + + This indicates that a letter has been printed and packaged in an envelope. + contentType: application/json + payload: + $ref: './events/letter.ENCLOSED.schema.json' + + letter-failed: + name: letter-failed + title: Letter Status Failed + summary: | + A letter status has been updated to failed. + + This indicates that a letter has failed to be processed by a supplier. + contentType: application/json + payload: + $ref: './events/letter.FAILED.schema.json' + + letter-forwarded: + name: letter-forwarded + title: Letter Status Forwarded + summary: | + A letter status has been updated to forwarded. + + This indicates that a letter has been forwarded to another supplier for processing. + contentType: application/json + payload: + $ref: './events/letter.FORWARDED.schema.json' + examples: + - payload: + $ref: './examples/letter.FORWARDED.json' + + letter-pending: + name: letter-pending + title: Letter Status Pending + summary: | + A letter status has been updated to pending. + + This indicates that a letter has been queued for processing by a supplier. + contentType: application/json + payload: + $ref: './events/letter.PENDING.schema.json' + + letter-printed: + name: letter-printed + title: Letter Status Printed + summary: | + A letter status has been updated to printed. + + This indicates that a letter has been printed by the supplier. + contentType: application/json + payload: + $ref: './events/letter.PRINTED.schema.json' + + letter-rejected: + name: letter-rejected + title: Letter Status Rejected + summary: | + A letter status has been updated to rejected. + + This indicates that a letter has been rejected by the supplier and will not be processed. + contentType: application/json + payload: + $ref: './events/letter.REJECTED.schema.json' + + letter-returned: + name: letter-returned + title: Letter Status Returned + summary: | + A letter status has been updated to returned. + + This indicates that a letter has been returned to the supplier. + contentType: application/json + payload: + $ref: './events/letter.RETURNED.schema.json' + examples: + - payload: + $ref: './examples/letter.RETURNED.json' + + letter-any: + name: letter-any + title: Letter Status Changed + summary: | + A letter status has been updated. + + This is a generic schema which matches any status change. + contentType: application/json + payload: + $ref: './events/letter.any.schema.json' + + mi-submitted: + name: mi-submitted + title: MI Submitted + summary: | + This indicates that MI data has been submitted to the NHS Notify Supplier API. + contentType: application/json + payload: + $ref: './events/mi.SUBMITTED.schema.json' + + schemas: + letter: + $ref: './domain/letter.schema.json' + mi: + $ref: './domain/mi.schema.json' diff --git a/internal/events/src/cli/bundle-asyncapi.ts b/internal/events/src/cli/bundle-asyncapi.ts new file mode 100644 index 00000000..48a130ae --- /dev/null +++ b/internal/events/src/cli/bundle-asyncapi.ts @@ -0,0 +1,22 @@ +import { writeFileSync } from "node:fs"; +import bundle from "@asyncapi/bundler"; +import path from "node:path"; +import { version as packageVersion } from "@nhsdigital/nhs-notify-event-schemas-supplier-api/package.json"; + +async function main() { + const baseDir = path.resolve(process.cwd(), ".."); + const document = await bundle(["events/schemas/supplier-api.yaml"], { + baseDir, + xOrigin: true, + }); + const info = document.json()?.info; + if (info) { + info.version = packageVersion; + } + const bundledOutput = document.yml(); + if (bundledOutput) { + writeFileSync("dist/asyncapi/supplier-api.yaml", bundledOutput); // the complete bundled AsyncAPI document + } +} + +main().catch((error) => console.error(error)); diff --git a/internal/events/src/cli/generate-json.ts b/internal/events/src/cli/generate-json.ts new file mode 100644 index 00000000..a6d35d68 --- /dev/null +++ b/internal/events/src/cli/generate-json.ts @@ -0,0 +1,57 @@ +import { z } from "zod"; +import * as fs from "node:fs"; +import { $Letter } from "@nhsdigital/nhs-notify-event-schemas-supplier-api/src/domain/letter"; +import { + $LetterEvent, + letterEventMap, +} from "@nhsdigital/nhs-notify-event-schemas-supplier-api/src/events/letter-events"; +import { $MISubmittedEvent } from "@nhsdigital/nhs-notify-event-schemas-supplier-api/src/events/mi-events"; +import { $MI } from "@nhsdigital/nhs-notify-event-schemas-supplier-api/src/domain/mi"; + +for (const [key, schema] of Object.entries({ + letter: $Letter, + mi: $MI, +})) { + const json = z.toJSONSchema(schema, { + io: "input", + target: "openapi-3.0", + reused: "ref", + }); + fs.mkdirSync("schemas/domain", { recursive: true }); + const file = `schemas/domain/${key}.schema.json`; + fs.writeFileSync(file, JSON.stringify(json, null, 2)); + console.info(`Wrote JSON schema for ${key} to ${file}`); +} + +for (const [key, schema] of Object.entries(letterEventMap)) { + const json = z.toJSONSchema(schema, { + io: "input", + target: "openapi-3.0", + reused: "ref", + }); + fs.mkdirSync("schemas/events", { recursive: true }); + const file = `schemas/events/${key}.schema.json`; + fs.writeFileSync(file, JSON.stringify(json, null, 2)); + console.info(`Wrote JSON schema for ${key} to ${file}`); +} + +// Generic letter status change event schema +const json = z.toJSONSchema($LetterEvent, { + io: "input", + target: "openapi-3.0", + reused: "ref", +}); +fs.mkdirSync("schemas/events", { recursive: true }); +const letterJson = `schemas/events/letter.any.schema.json`; +fs.writeFileSync(letterJson, JSON.stringify(json, null, 2)); +console.info(`Wrote JSON schema for letter.any to ${letterJson}`); + +// MI Submitted Event +const miJson = z.toJSONSchema($MISubmittedEvent, { + io: "input", + target: "openapi-3.0", + reused: "ref", +}); +const miFile = `schemas/events/mi.SUBMITTED.schema.json`; +fs.writeFileSync(miFile, JSON.stringify(miJson, null, 2)); +console.info(`Wrote JSON schema for letter.any to ${miFile}`); diff --git a/internal/events/src/domain/__tests__/mi.test.ts b/internal/events/src/domain/__tests__/mi.test.ts new file mode 100644 index 00000000..111e3cf0 --- /dev/null +++ b/internal/events/src/domain/__tests__/mi.test.ts @@ -0,0 +1,165 @@ +import { + $MI, + MI, +} from "@nhsdigital/nhs-notify-event-schemas-supplier-api/src/domain/mi"; + +describe("MI schema validation", () => { + const validMIEvent: MI = { + id: "mi-001", + lineItem: "LETTER_PRINT_A4", + timestamp: "2025-11-16T10:30:00.000Z", + quantity: 150, + specificationId: "spec-123", + groupId: "group-456", + stockRemaining: 1000, + supplierId: "supplier-789", + createdAt: "2025-11-16T10:30:00.000Z", + updatedAt: "2025-11-16T10:30:00.000Z", + }; + + describe("basic validation", () => { + it("should validate a valid MI event with all fields", () => { + const result = $MI.safeParse(validMIEvent); + expect(result.success).toBe(true); + expect(result.error).toBeUndefined(); + expect(result.data).toEqual(validMIEvent); + }); + + it("should validate an MI event without optional fields", () => { + const minimalMI = { + id: "mi-002", + lineItem: "LETTER_PRINT_A5", + timestamp: "2025-11-16T11:00:00.000Z", + quantity: 75, + supplierId: "supplier-101", + createdAt: "2025-11-16T11:00:00.000Z", + updatedAt: "2025-11-16T11:00:00.000Z", + }; + + const result = $MI.safeParse(minimalMI); + expect(result.success).toBe(true); + const data = result.data as MI; + expect(data.specificationId).toBeUndefined(); + expect(data.groupId).toBeUndefined(); + expect(data.stockRemaining).toBeUndefined(); + }); + }); + + describe("field validation", () => { + it("should reject MI event missing required field 'id'", () => { + const invalidMI = { ...validMIEvent }; + delete (invalidMI as any).id; + + const result = $MI.safeParse(invalidMI); + expect(result.success).toBe(false); + }); + + it("should reject MI event missing required field 'lineItem'", () => { + const invalidMI = { ...validMIEvent }; + delete (invalidMI as any).lineItem; + + const result = $MI.safeParse(invalidMI); + expect(result.success).toBe(false); + }); + + it("should reject MI event missing required field 'timestamp'", () => { + const invalidMI = { ...validMIEvent }; + delete (invalidMI as any).timestamp; + + const result = $MI.safeParse(invalidMI); + expect(result.success).toBe(false); + }); + + it("should reject MI event missing required field 'quantity'", () => { + const invalidMI = { ...validMIEvent }; + delete (invalidMI as any).quantity; + + const result = $MI.safeParse(invalidMI); + expect(result.success).toBe(false); + }); + + it("should reject MI event missing required field 'supplierId'", () => { + const invalidMI = { ...validMIEvent }; + delete (invalidMI as any).supplierId; + + const result = $MI.safeParse(invalidMI); + expect(result.success).toBe(false); + }); + + it("should reject MI event with invalid quantity type", () => { + const invalidMI = { + ...validMIEvent, + quantity: "not-a-number", + }; + + const result = $MI.safeParse(invalidMI); + expect(result.success).toBe(false); + }); + + it("should reject MI event with invalid stockRemaining type", () => { + const invalidMI = { + ...validMIEvent, + stockRemaining: "not-a-number", + }; + + const result = $MI.safeParse(invalidMI); + expect(result.success).toBe(false); + }); + }); + + describe("testData examples", () => { + it("should parse a letter print MI event", () => { + const letterPrintMI = { + id: "mi-letter-001", + lineItem: "LETTER_PRINT_A4_COLOR", + timestamp: "2025-11-16T14:00:00.000Z", + quantity: 250, + specificationId: "letter-spec-001", + groupId: "batch-001", + supplierId: "supplier-abc", + createdAt: "2025-11-16T14:00:00.000Z", + updatedAt: "2025-11-16T14:00:00.000Z", + }; + + const result = $MI.safeParse(letterPrintMI); + expect(result.success).toBe(true); + }); + + it("should parse an envelope usage MI event", () => { + const envelopeMI = { + id: "mi-envelope-001", + lineItem: "ENVELOPE_DL", + timestamp: "2025-11-16T15:00:00.000Z", + quantity: 300, + stockRemaining: 2500, + supplierId: "supplier-xyz", + createdAt: "2025-11-16T15:00:00.000Z", + updatedAt: "2025-11-16T15:00:00.000Z", + }; + + const result = $MI.safeParse(envelopeMI); + expect(result.success).toBe(true); + const data = result.data as MI; + expect(data.stockRemaining).toBe(2500); + }); + + it("should parse a postage MI event", () => { + const postageMI = { + id: "mi-postage-001", + lineItem: "POSTAGE_FIRST_CLASS", + timestamp: "2025-11-16T16:00:00.000Z", + quantity: 500, + groupId: "daily-batch-16-11-2025", + supplierId: "supplier-123", + createdAt: "2025-11-16T16:00:00.000Z", + updatedAt: "2025-11-16T16:00:00.000Z", + }; + + const result = $MI.safeParse(postageMI); + expect(result.success).toBe(true); + const data = result.data as MI; + expect(data.lineItem).toBe("POSTAGE_FIRST_CLASS"); + expect(data.groupId).toBe("daily-batch-16-11-2025"); + }); + }); +}); diff --git a/internal/helpers/src/domain-base.ts b/internal/events/src/domain/domain-base.ts similarity index 87% rename from internal/helpers/src/domain-base.ts rename to internal/events/src/domain/domain-base.ts index c738220a..1e8dfd43 100644 --- a/internal/helpers/src/domain-base.ts +++ b/internal/events/src/domain/domain-base.ts @@ -1,6 +1,6 @@ import { z } from "zod"; -export function DomainBase( +export default function DomainBase( type: T, ): z.ZodObject<{ domainId: z.core.$ZodBranded }> { const idType = z diff --git a/internal/events/src/domain/letter.ts b/internal/events/src/domain/letter.ts new file mode 100644 index 00000000..7e854d41 --- /dev/null +++ b/internal/events/src/domain/letter.ts @@ -0,0 +1,124 @@ +import { z } from "zod"; +import DomainBase from "./domain-base"; + +/** + * Status values for letters in the supplier-api domain + */ +export const $LetterStatus = z + .enum([ + "PENDING", + "ACCEPTED", + "REJECTED", + "PRINTED", + "ENCLOSED", + "CANCELLED", + "DISPATCHED", + "FAILED", + "RETURNED", + "FORWARDED", + "DELIVERED", + ]) + .meta({ + title: "Letter Status", + description: "The status of a letter in the supplier-api domain.", + examples: ["ACCEPTED", "REJECTED", "PRINTED"], + }); + +export type LetterStatus = z.infer; + +/** + * Schema for letter status change events + */ +export const $Letter = DomainBase("Letter") + .extend({ + origin: z + .object({ + domain: z.string().meta({ + title: "Domain ID", + description: "The domain which requested this letter", + }), + + source: z.string().meta({ + title: "Event source", + description: "The source of the event which created this letter", + }), + + subject: z.string().meta({ + title: "Event subject", + description: + "The subject of the event which created this letter, scoped to source", + }), + + event: z.string().meta({ + title: "Event ID", + description: "The ID of the event which created this letter", + }), + }) + .meta({ + title: "Letter origin", + description: `Identifiers captured from the original event that introduced the letter to the supplier-api domain. + +The identifier will be included as the origin domain in the subject of any corresponding events emitted by the supplier-api domain.`, + examples: [ + { + domain: "letter-rendering", + subject: + "customer/00f3b388-bbe9-41c9-9e76-052d37ee8988/letter-rendering/letter-request/0o5Fs0EELR0fUjHjbCnEtdUwQe4_0o5Fs0EELR0fUjHjbCnEtdUwQe5", + event: "00f3b388-bbe9-41c9-9e76-052d37ee8988", + }, + ], + }), + + specificationId: z.string().meta({ + title: "Specification ID", + description: + "Reference to the letter specification which was used to produce a letter pack for this request.", + examples: ["1y3q9v1zzzz"], + }), + + supplierId: z.string().meta({ + title: "Supplier ID", + description: "Supplier ID allocated to the letter during creation.", + examples: ["supplier-1"], + }), + + groupId: z.string().meta({ + title: "Group ID", + description: + "Identifier for the group which this letter assigned to for reporting purposes.", + examples: [ + "client_template", + "00f3b388-bbe9-41c9-9e76-052d37ee8988_20a1ab22-6136-47ae-ac0f-989f382be8df", + ], + }), + + status: $LetterStatus, + + reasonCode: z + .string() + .optional() + .meta({ + title: "Reason Code", + description: + "Optional reason code for the status change, if applicable.", + examples: ["R01", "R08"], + }), + + reasonText: z + .string() + .optional() + .meta({ + title: "Reason Text", + description: + "Optional human-readable reason for the status change, if applicable.", + examples: ["Undeliverable", "Recipient moved"], + }), + }) + .meta({ + title: "Letter", + description: `The status of a letter in the supplier-api domain. + +This will include the current production status, any reason provided for the status, if applicable, and identifiers used for grouping in reports.`, + }); + +export type Letter = z.infer; diff --git a/internal/events/src/domain/mi.ts b/internal/events/src/domain/mi.ts new file mode 100644 index 00000000..63954267 --- /dev/null +++ b/internal/events/src/domain/mi.ts @@ -0,0 +1,18 @@ +import { z } from "zod"; + +export const $MI = z + .object({ + id: z.string(), + lineItem: z.string(), + timestamp: z.string(), + quantity: z.number(), + specificationId: z.string().optional(), + groupId: z.string().optional(), + stockRemaining: z.number().optional(), + supplierId: z.string(), + createdAt: z.string(), + updatedAt: z.string(), + }) + .describe("MI"); + +export type MI = z.infer; diff --git a/internal/events/src/events/__tests__/event-envelope.test.ts b/internal/events/src/events/__tests__/event-envelope.test.ts new file mode 100644 index 00000000..4842ef4f --- /dev/null +++ b/internal/events/src/events/__tests__/event-envelope.test.ts @@ -0,0 +1,368 @@ +import { z } from "zod"; +import { EventEnvelope } from "@nhsdigital/nhs-notify-event-schemas-supplier-api/src/events/event-envelope"; + +describe("EventEnvelope schema validation", () => { + const $Envelope = EventEnvelope("order.read", "order", z.any(), ["READ"]); + type Envelope = z.infer; + + const baseValidEnvelope: Envelope = { + dataschema: + "https://notify.nhs.uk/cloudevents/schemas/supplier-api/order.READ.1.0.0.schema.json", + dataschemaversion: "1.0.0", + specversion: "1.0", + id: "6f1c2a53-3d54-4a0a-9a0b-0e9ae2d4c111", + source: "/data-plane/supplier-api/ordering", + subject: "order/769acdd4", + type: "uk.nhs.notify.supplier-api.order.READ.v1", + plane: "data", + time: "2025-10-01T10:15:30.000Z", + datacontenttype: "application/json", + data: { + "notify-payload": { + "notify-data": { nhsNumber: "9434765919" }, + "notify-metadata": { + teamResponsible: "Team 1", + notifyDomain: "Ordering", + version: "1.3.0", + }, + }, + }, + traceparent: "00-0af7651916cd43dd8448eb211c80319c-b7ad6b7169203331-01", + recordedtime: "2025-10-01T10:15:30.250Z", + severitynumber: 2, + severitytext: "INFO", + }; + + describe("basic validation", () => { + it("should validate a valid envelope", () => { + const result = $Envelope.safeParse(baseValidEnvelope); + expect(result.error).toBeUndefined(); + expect(result.success).toBe(true); + }); + }); + + describe("superRefine: severity text and number validation", () => { + it("should accept TRACE with severitynumber 0", () => { + const envelope = { + ...baseValidEnvelope, + severitytext: "TRACE", + severitynumber: 0, + }; + + const result = $Envelope.safeParse(envelope); + expect(result.success).toBe(true); + }); + + it("should accept DEBUG with severitynumber 1", () => { + const envelope = { + ...baseValidEnvelope, + severitytext: "DEBUG", + severitynumber: 1, + }; + + const result = $Envelope.safeParse(envelope); + expect(result.success).toBe(true); + }); + + it("should accept INFO with severitynumber 2", () => { + const envelope = { + ...baseValidEnvelope, + severitytext: "INFO", + severitynumber: 2, + }; + + const result = $Envelope.safeParse(envelope); + expect(result.success).toBe(true); + }); + + it("should accept WARN with severitynumber 3", () => { + const envelope = { + ...baseValidEnvelope, + severitytext: "WARN", + severitynumber: 3, + }; + + const result = $Envelope.safeParse(envelope); + expect(result.success).toBe(true); + }); + + it("should accept ERROR with severitynumber 4", () => { + const envelope = { + ...baseValidEnvelope, + severitytext: "ERROR", + severitynumber: 4, + }; + + const result = $Envelope.safeParse(envelope); + expect(result.success).toBe(true); + }); + + it("should accept FATAL with severitynumber 5", () => { + const envelope = { + ...baseValidEnvelope, + severitytext: "FATAL", + severitynumber: 5, + }; + + const result = $Envelope.safeParse(envelope); + expect(result.success).toBe(true); + }); + + it("should reject TRACE with incorrect severitynumber", () => { + const envelope = { + ...baseValidEnvelope, + severitytext: "TRACE", + severitynumber: 1, + }; + + const result = $Envelope.safeParse(envelope); + expect(result.success).toBe(false); + }); + + it("should reject DEBUG with incorrect severitynumber", () => { + const envelope = { + ...baseValidEnvelope, + severitytext: "DEBUG", + severitynumber: 2, + }; + + const result = $Envelope.safeParse(envelope); + expect(result.success).toBe(false); + }); + + it("should reject INFO with incorrect severitynumber", () => { + const envelope = { + ...baseValidEnvelope, + severitytext: "INFO", + severitynumber: 1, + }; + + const result = $Envelope.safeParse(envelope); + expect(result.success).toBe(false); + }); + + it("should reject WARN with incorrect severitynumber", () => { + const envelope = { + ...baseValidEnvelope, + severitytext: "WARN", + severitynumber: 2, + }; + + const result = $Envelope.safeParse(envelope); + expect(result.success).toBe(false); + }); + + it("should reject ERROR with incorrect severitynumber", () => { + const envelope = { + ...baseValidEnvelope, + severitytext: "ERROR", + severitynumber: 3, + }; + + const result = $Envelope.safeParse(envelope); + expect(result.success).toBe(false); + }); + + it("should reject FATAL with incorrect severitynumber", () => { + const envelope = { + ...baseValidEnvelope, + severitytext: "FATAL", + severitynumber: 4, + }; + + const result = $Envelope.safeParse(envelope); + expect(result.success).toBe(false); + }); + + it("should reject severitynumber without severitytext", () => { + const envelope = { + ...baseValidEnvelope, + severitytext: undefined, + severitynumber: 2, + }; + + const result = $Envelope.safeParse(envelope); + expect(result.success).toBe(false); + }); + + it("should accept severitytext without severitynumber (optional)", () => { + const envelope = { + ...baseValidEnvelope, + severitytext: "INFO", + severitynumber: 2, + }; + + const result = $Envelope.safeParse(envelope); + expect(result.success).toBe(true); + }); + }); + + describe("optional fields validation", () => { + it("should accept envelope with all optional fields", () => { + const envelope = { + ...baseValidEnvelope, + datacontenttype: "application/json", + tracestate: "rojo=00f067aa0ba902b7", + partitionkey: "customer-920fca11", + sampledrate: 5, + sequence: "00000000000000000042", + severitytext: "DEBUG", + severitynumber: 1, + dataclassification: "restricted", + dataregulation: "GDPR", + datacategory: "sensitive", + }; + + const result = $Envelope.safeParse(envelope); + expect(result.error).toBeUndefined(); + expect(result.success).toBe(true); + }); + }); + + describe("edge cases", () => { + it("should reject invalid source pattern", () => { + const envelope = { + ...baseValidEnvelope, + source: "/invalid-plane/test", + }; + + const result = $Envelope.safeParse(envelope); + expect(result.success).toBe(false); + }); + }); + + describe("subject prefix validation", () => { + const $EnvelopeWithPrefix = EventEnvelope( + "letter.CREATED", + "letter", + z.any(), + ["CREATED"], + "letter-origin", + ); + + const baseLetterEnvelope = { + specversion: "1.0" as const, + id: "6f1c2a53-3d54-4a0a-9a0b-0e02b2c3d479", + type: "uk.nhs.notify.supplier-api.letter.CREATED.v1" as const, + plane: "data", + dataschema: + "https://notify.nhs.uk/cloudevents/schemas/supplier-api/letter.CREATED.1.0.0.schema.json", + dataschemaversion: "1.0.0", + source: "/data-plane/supplier-api/letters", + time: "2025-10-01T10:15:30.000Z", + datacontenttype: "application/json", + data: { status: "CREATED" }, + traceparent: "00-0af7651916cd43dd8448eb211c80319c-b7ad6b7169203331-01", + recordedtime: "2025-10-01T10:15:30.250Z", + severitynumber: 2, + severitytext: "INFO" as const, + }; + + it("should accept subject with valid prefix when prefix is required", () => { + const envelope = { + ...baseLetterEnvelope, + subject: + "letter-origin/letter-rendering/letter/f47ac10b-58cc-4372-a567-0e02b2c3d479", + }; + + const result = $EnvelopeWithPrefix.safeParse(envelope); + expect(result.error).toBeUndefined(); + expect(result.success).toBe(true); + }); + + it("should reject subject without prefix when prefix is required", () => { + const envelope = { + ...baseLetterEnvelope, + subject: "letter/f47ac10b-58cc-4372-a567-0e02b2c3d479", + }; + + const result = $EnvelopeWithPrefix.safeParse(envelope); + expect(result.success).toBe(false); + expect(result?.error?.issues[0].path).toContain("subject"); + }); + + it("should reject subject with incomplete prefix", () => { + const envelope = { + ...baseLetterEnvelope, + subject: "letter-origin/letter/f47ac10b-58cc-4372-a567-0e02b2c3d479", + }; + + const result = $EnvelopeWithPrefix.safeParse(envelope); + expect(result.success).toBe(false); + }); + + it("should accept subject without prefix when no prefix is specified", () => { + const $EnvelopeNoPrefix = EventEnvelope("order.READ", "order", z.any(), [ + "READ", + ]); + + const envelope = { + specversion: "1.0" as const, + id: "6f1c2a53-3d54-4a0a-9a0b-0e9ae2d4c111", + type: "uk.nhs.notify.supplier-api.order.READ.v1" as const, + plane: "data", + dataschema: + "https://notify.nhs.uk/cloudevents/schemas/supplier-api/order.READ.1.0.0.schema.json", + dataschemaversion: "1.0.0", + source: "/data-plane/supplier-api/ordering", + subject: "order/769acdd4", + time: "2025-10-01T10:15:30.000Z", + datacontenttype: "application/json", + data: { status: "READ" }, + traceparent: "00-0af7651916cd43dd8448eb211c80319c-b7ad6b7169203331-01", + recordedtime: "2025-10-01T10:15:30.250Z", + severitynumber: 2, + severitytext: "INFO" as const, + }; + + const result = $EnvelopeNoPrefix.safeParse(envelope); + expect(result.error).toBeUndefined(); + expect(result.success).toBe(true); + }); + + it("should accept various prefix formats", () => { + const $EnvelopeMultiSegmentPrefix = EventEnvelope( + "letter.CREATED", + "letter", + z.any(), + ["CREATED"], + "a/b", + ); + + const envelope = { + ...baseLetterEnvelope, + subject: "a/b/c/letter/test-id-123", + }; + + const result = $EnvelopeMultiSegmentPrefix.safeParse(envelope); + expect(result.error).toBeUndefined(); + expect(result.success).toBe(true); + }); + + it("should reject subject with prefix when no prefix is required", () => { + const $EnvelopeNoPrefix = EventEnvelope("order.read", "order", z.any(), [ + "READ", + ]); + + const envelope = { + specversion: "1.0" as const, + id: "6f1c2a53-3d54-4a0a-9a0b-0e9ae2d4c111", + type: "uk.nhs.notify.supplier-api.order.read.v1" as const, + dataschema: + "https://notify.nhs.uk/cloudevents/schemas/supplier-api/order.read.1.0.0.schema.json", + dataschemaversion: "1.0.0", + source: "/data-plane/supplier-api/ordering", + subject: "prefix/letter-rendering/order/769acdd4", + time: "2025-10-01T10:15:30.000Z", + data: { status: "READ" }, + traceparent: "00-0af7651916cd43dd8448eb211c80319c-b7ad6b7169203331-01", + recordedtime: "2025-10-01T10:15:30.250Z", + severitynumber: 2, + severitytext: "INFO" as const, + }; + + const result = $EnvelopeNoPrefix.safeParse(envelope); + expect(result.success).toBe(false); + }); + }); +}); diff --git a/internal/events/src/events/__tests__/letter-status-change-events.test.ts b/internal/events/src/events/__tests__/letter-status-change-events.test.ts new file mode 100644 index 00000000..25541fc5 --- /dev/null +++ b/internal/events/src/events/__tests__/letter-status-change-events.test.ts @@ -0,0 +1,89 @@ +import fs from "node:fs"; +import path from "node:path"; +import { letterEventMap } from "@nhsdigital/nhs-notify-event-schemas-supplier-api/src/events/letter-events"; + +function readJson(filename: string): unknown { + const filePath = path.resolve(__dirname, "./testData/", filename); + + return JSON.parse(fs.readFileSync(filePath, "utf8")); +} + +describe("LetterStatus event validations", () => { + it.each(["ACCEPTED", "FORWARDED", "RETURNED"])( + "should parse %s letter statuses successfully", + (status) => { + const json = readJson(`letter.${status}.json`); + + const { data: event, error } = + letterEventMap[`letter.${status}`].safeParse(json); + expect(error).toBeUndefined(); + expect(event).toBeDefined(); + expect(event).toEqual( + expect.objectContaining({ + type: `uk.nhs.notify.supplier-api.letter.${status}.v1`, + specversion: "1.0", + source: "/data-plane/supplier-api/prod/update-status", + id: "23f1f09c-a555-4d9b-8405-0b33490bc920", + time: "2025-08-28T08:45:00.000Z", + datacontenttype: "application/json", + dataschema: `https://notify.nhs.uk/cloudevents/schemas/supplier-api/letter.${status}.1.0.0.schema.json`, + subject: + "letter-origin/letter-rendering/letter/f47ac10b-58cc-4372-a567-0e02b2c3d479", + data: expect.objectContaining({ + origin: expect.objectContaining({ + subject: + "client/00f3b388-bbe9-41c9-9e76-052d37ee8988/letter-request/0o5Fs0EELR0fUjHjbCnEtdUwQe4_0o5Fs0EELR0fUjHjbCnEtdUwQe5", + event: "f47ac10b-58cc-4372-a567-0e02b2c3d479", + }), + domainId: "f47ac10b-58cc-4372-a567-0e02b2c3d479", + specificationId: "1y3q9v1zzzz", + groupId: "client_template", + status, + }), + }), + ); + }, + ); + + it("should parse reason code and text correctly for returned mail", () => { + const json = readJson("letter.RETURNED.json"); + + const event = letterEventMap["letter.RETURNED"].parse(json); + expect(event).toBeDefined(); + expect(event.data).toEqual( + expect.objectContaining({ + reasonCode: "R07", + reasonText: "No such address", + }), + ); + }); + + it("should parse reason code and text correctly for forwarded mail", () => { + const json = readJson("letter.FORWARDED.json"); + + const event = letterEventMap["letter.FORWARDED"].parse(json); + expect(event).toBeDefined(); + expect(event.data).toEqual( + expect.objectContaining({ + reasonCode: "RNIB", + reasonText: "RNIB", + }), + ); + }); + + it("should throw error for letter.ACCEPTED event with missing sourceSubject", () => { + const json = readJson("letter.ACCEPTED-with-missing-fields.json"); + + expect(() => letterEventMap["letter.ACCEPTED"].parse(json)).toThrow( + "subject", + ); + }); + + it("should throw error for letter.ACCEPTED event with invalid major schema version", () => { + const json = readJson("letter.ACCEPTED-with-invalid-major-version.json"); + + expect(() => letterEventMap["letter.ACCEPTED"].parse(json)).toThrow( + "dataschema", + ); + }); +}); diff --git a/internal/events/src/events/__tests__/mi-events.test.ts b/internal/events/src/events/__tests__/mi-events.test.ts new file mode 100644 index 00000000..f4a22c98 --- /dev/null +++ b/internal/events/src/events/__tests__/mi-events.test.ts @@ -0,0 +1,87 @@ +import fs from "node:fs"; +import path from "node:path"; +import { $MISubmittedEvent } from "@nhsdigital/nhs-notify-event-schemas-supplier-api/src/events/mi-events"; + +function readJson(filename: string): unknown { + const filePath = path.resolve(__dirname, "./testData/", filename); + + return JSON.parse(fs.readFileSync(filePath, "utf8")); +} + +describe("MI event validations", () => { + it("should parse mi.SUBMITTED event successfully", () => { + const json = readJson("mi.SUBMITTED.json"); + + const { data: event, error } = $MISubmittedEvent.safeParse(json); + expect(error).toBeUndefined(); + expect(event).toBeDefined(); + expect(event).toEqual( + expect.objectContaining({ + type: "uk.nhs.notify.supplier-api.mi.SUBMITTED.v1", + plane: "data", + specversion: "1.0", + source: "/data-plane/supplier-api/prod/submit-mi", + id: "8f2c3b44-4e65-5b1b-a678-1f0bf3d4d222", + time: "2025-11-16T10:30:00.000Z", + datacontenttype: "application/json", + dataschema: + "https://notify.nhs.uk/cloudevents/schemas/supplier-api/mi.SUBMITTED.1.0.0.schema.json", + subject: "mi/mi-test-001", + data: expect.objectContaining({ + id: "mi-test-001", + lineItem: "LETTER_PRINT_A4", + timestamp: "2025-11-16T10:30:00.000Z", + quantity: 150, + specificationId: "spec-123", + groupId: "group-456", + stockRemaining: 1000, + supplierId: "supplier-789", + }), + }), + ); + }); + + it("should parse minimal mi.SUBMITTED event successfully", () => { + const json = readJson("mi.SUBMITTED-minimal.json"); + + const event = $MISubmittedEvent.parse(json); + expect(event).toBeDefined(); + expect(event.data).toEqual( + expect.objectContaining({ + id: "mi-envelope-001", + lineItem: "ENVELOPE_DL", + quantity: 300, + stockRemaining: 2500, + supplierId: "supplier-xyz", + }), + ); + expect(event.data.specificationId).toBeUndefined(); + expect(event.data.groupId).toBeUndefined(); + }); + + it("should parse MI data fields correctly", () => { + const json = readJson("mi.SUBMITTED.json"); + + const event = $MISubmittedEvent.parse(json); + expect(event).toBeDefined(); + expect(event.data.id).toBe("mi-test-001"); + expect(event.data.lineItem).toBe("LETTER_PRINT_A4"); + expect(event.data.quantity).toBe(150); + expect(event.data.stockRemaining).toBe(1000); + expect(event.data.supplierId).toBe("supplier-789"); + expect(event.data.specificationId).toBe("spec-123"); + expect(event.data.groupId).toBe("group-456"); + }); + + it("should throw error for mi.SUBMITTED event with missing subject", () => { + const json = readJson("mi.SUBMITTED-with-missing-subject.json"); + + expect(() => $MISubmittedEvent.parse(json)).toThrow("subject"); + }); + + it("should throw error for mi.SUBMITTED event with invalid major schema version", () => { + const json = readJson("mi.SUBMITTED-with-invalid-major-version.json"); + + expect(() => $MISubmittedEvent.parse(json)).toThrow("dataschema"); + }); +}); diff --git a/internal/events/src/events/__tests__/testData/letter.ACCEPTED-with-invalid-major-version.json b/internal/events/src/events/__tests__/testData/letter.ACCEPTED-with-invalid-major-version.json new file mode 100644 index 00000000..5458449d --- /dev/null +++ b/internal/events/src/events/__tests__/testData/letter.ACCEPTED-with-invalid-major-version.json @@ -0,0 +1,29 @@ +{ + "data": { + "domainId": "f47ac10b-58cc-4372-a567-0e02b2c3d479", + "groupId": "client_template", + "origin": { + "domain": "letter-rendering", + "event": "f47ac10b-58cc-4372-a567-0e02b2c3d479", + "source": "/data-plane/letter-rendering/prod/render-pdf", + "subject": "client/00f3b388-bbe9-41c9-9e76-052d37ee8988/letter-request/0o5Fs0EELR0fUjHjbCnEtdUwQe4_0o5Fs0EELR0fUjHjbCnEtdUwQe5" + }, + "specificationId": "1y3q9v1zzzz", + "status": "ACCEPTED", + "supplierId": "supplier1" + }, + "datacontenttype": "application/json", + "dataschema": "https://notify.nhs.uk/cloudevents/schemas/supplier-api/letter.ACCEPTED.0.1.0.schema.json", + "dataschemaversion": "1.0.0", + "id": "23f1f09c-a555-4d9b-8405-0b33490bc920", + "plane": "data", + "recordedtime": "2025-08-28T08:45:00.000Z", + "severitynumber": 2, + "severitytext": "INFO", + "source": "/data-plane/supplier-api/prod/update-status", + "specversion": "1.0", + "subject": "letter-origin/letter-rendering/letter/f47ac10b-58cc-4372-a567-0e02b2c3d479", + "time": "2025-08-28T08:45:00.000Z", + "traceparent": "00-0af7651916cd43dd8448eb211c80319c-b7ad6b7169203331-01", + "type": "uk.nhs.notify.supplier-api.letter.ACCEPTED.v1" +} diff --git a/internal/events/src/events/__tests__/testData/letter.ACCEPTED-with-missing-fields.json b/internal/events/src/events/__tests__/testData/letter.ACCEPTED-with-missing-fields.json new file mode 100644 index 00000000..b7a1358b --- /dev/null +++ b/internal/events/src/events/__tests__/testData/letter.ACCEPTED-with-missing-fields.json @@ -0,0 +1,28 @@ +{ + "data": { + "domainId": "f47ac10b-58cc-4372-a567-0e02b2c3d479", + "groupId": "client_template", + "origin": { + "domain": "letter-rendering", + "event": "f47ac10b-58cc-4372-a567-0e02b2c3d479", + "source": "/data-plane/letter-rendering/prod/render-pdf" + }, + "specificationId": "1y3q9v1zzzz", + "status": "ACCEPTED", + "supplierId": "supplier1" + }, + "datacontenttype": "application/json", + "dataschema": "https://notify.nhs.uk/cloudevents/schemas/supplier-api/letter.ACCEPTED.1.0.0.schema.json", + "dataschemaversion": "1.0.0", + "id": "23f1f09c-a555-4d9b-8405-0b33490bc920", + "plane": "data", + "recordedtime": "2025-08-28T08:45:00.000Z", + "severitynumber": 2, + "severitytext": "INFO", + "source": "/data-plane/supplier-api/prod/update-status", + "specversion": "1.0", + "subject": "letter-origin/letter-rendering/letter/f47ac10b-58cc-4372-a567-0e02b2c3d479", + "time": "2025-08-28T08:45:00.000Z", + "traceparent": "00-0af7651916cd43dd8448eb211c80319c-b7ad6b7169203331-01", + "type": "uk.nhs.notify.supplier-api.letter.ACCEPTED.v1" +} diff --git a/internal/events/src/events/__tests__/testData/letter.ACCEPTED.json b/internal/events/src/events/__tests__/testData/letter.ACCEPTED.json new file mode 100644 index 00000000..e39b8366 --- /dev/null +++ b/internal/events/src/events/__tests__/testData/letter.ACCEPTED.json @@ -0,0 +1,29 @@ +{ + "data": { + "domainId": "f47ac10b-58cc-4372-a567-0e02b2c3d479", + "groupId": "client_template", + "origin": { + "domain": "letter-rendering", + "event": "f47ac10b-58cc-4372-a567-0e02b2c3d479", + "source": "/data-plane/letter-rendering/prod/render-pdf", + "subject": "client/00f3b388-bbe9-41c9-9e76-052d37ee8988/letter-request/0o5Fs0EELR0fUjHjbCnEtdUwQe4_0o5Fs0EELR0fUjHjbCnEtdUwQe5" + }, + "specificationId": "1y3q9v1zzzz", + "status": "ACCEPTED", + "supplierId": "supplier1" + }, + "datacontenttype": "application/json", + "dataschema": "https://notify.nhs.uk/cloudevents/schemas/supplier-api/letter.ACCEPTED.1.0.0.schema.json", + "dataschemaversion": "1.0.0", + "id": "23f1f09c-a555-4d9b-8405-0b33490bc920", + "plane": "data", + "recordedtime": "2025-08-28T08:45:00.000Z", + "severitynumber": 2, + "severitytext": "INFO", + "source": "/data-plane/supplier-api/prod/update-status", + "specversion": "1.0", + "subject": "letter-origin/letter-rendering/letter/f47ac10b-58cc-4372-a567-0e02b2c3d479", + "time": "2025-08-28T08:45:00.000Z", + "traceparent": "00-0af7651916cd43dd8448eb211c80319c-b7ad6b7169203331-01", + "type": "uk.nhs.notify.supplier-api.letter.ACCEPTED.v1" +} diff --git a/internal/events/src/events/__tests__/testData/letter.FORWARDED.json b/internal/events/src/events/__tests__/testData/letter.FORWARDED.json new file mode 100644 index 00000000..6b7b4c45 --- /dev/null +++ b/internal/events/src/events/__tests__/testData/letter.FORWARDED.json @@ -0,0 +1,31 @@ +{ + "data": { + "domainId": "f47ac10b-58cc-4372-a567-0e02b2c3d479", + "groupId": "client_template", + "origin": { + "domain": "letter-rendering", + "event": "f47ac10b-58cc-4372-a567-0e02b2c3d479", + "source": "/data-plane/letter-rendering/prod/render-pdf", + "subject": "client/00f3b388-bbe9-41c9-9e76-052d37ee8988/letter-request/0o5Fs0EELR0fUjHjbCnEtdUwQe4_0o5Fs0EELR0fUjHjbCnEtdUwQe5" + }, + "reasonCode": "RNIB", + "reasonText": "RNIB", + "specificationId": "1y3q9v1zzzz", + "status": "FORWARDED", + "supplierId": "supplier1" + }, + "datacontenttype": "application/json", + "dataschema": "https://notify.nhs.uk/cloudevents/schemas/supplier-api/letter.FORWARDED.1.0.0.schema.json", + "dataschemaversion": "1.0.0", + "id": "23f1f09c-a555-4d9b-8405-0b33490bc920", + "plane": "data", + "recordedtime": "2025-08-28T08:45:00.000Z", + "severitynumber": 2, + "severitytext": "INFO", + "source": "/data-plane/supplier-api/prod/update-status", + "specversion": "1.0", + "subject": "letter-origin/letter-rendering/letter/f47ac10b-58cc-4372-a567-0e02b2c3d479", + "time": "2025-08-28T08:45:00.000Z", + "traceparent": "00-0af7651916cd43dd8448eb211c80319c-b7ad6b7169203331-01", + "type": "uk.nhs.notify.supplier-api.letter.FORWARDED.v1" +} diff --git a/internal/events/src/events/__tests__/testData/letter.RETURNED.json b/internal/events/src/events/__tests__/testData/letter.RETURNED.json new file mode 100644 index 00000000..8a4a9e44 --- /dev/null +++ b/internal/events/src/events/__tests__/testData/letter.RETURNED.json @@ -0,0 +1,31 @@ +{ + "data": { + "domainId": "f47ac10b-58cc-4372-a567-0e02b2c3d479", + "groupId": "client_template", + "origin": { + "domain": "letter-rendering", + "event": "f47ac10b-58cc-4372-a567-0e02b2c3d479", + "source": "/data-plane/letter-rendering/prod/render-pdf", + "subject": "client/00f3b388-bbe9-41c9-9e76-052d37ee8988/letter-request/0o5Fs0EELR0fUjHjbCnEtdUwQe4_0o5Fs0EELR0fUjHjbCnEtdUwQe5" + }, + "reasonCode": "R07", + "reasonText": "No such address", + "specificationId": "1y3q9v1zzzz", + "status": "RETURNED", + "supplierId": "supplier1" + }, + "datacontenttype": "application/json", + "dataschema": "https://notify.nhs.uk/cloudevents/schemas/supplier-api/letter.RETURNED.1.0.0.schema.json", + "dataschemaversion": "1.0.0", + "id": "23f1f09c-a555-4d9b-8405-0b33490bc920", + "plane": "data", + "recordedtime": "2025-08-28T08:45:00.000Z", + "severitynumber": 2, + "severitytext": "INFO", + "source": "/data-plane/supplier-api/prod/update-status", + "specversion": "1.0", + "subject": "letter-origin/letter-rendering/letter/f47ac10b-58cc-4372-a567-0e02b2c3d479", + "time": "2025-08-28T08:45:00.000Z", + "traceparent": "00-0af7651916cd43dd8448eb211c80319c-b7ad6b7169203331-01", + "type": "uk.nhs.notify.supplier-api.letter.RETURNED.v1" +} diff --git a/internal/events/src/events/__tests__/testData/mi.SUBMITTED-minimal.json b/internal/events/src/events/__tests__/testData/mi.SUBMITTED-minimal.json new file mode 100644 index 00000000..5985006e --- /dev/null +++ b/internal/events/src/events/__tests__/testData/mi.SUBMITTED-minimal.json @@ -0,0 +1,26 @@ +{ + "data": { + "createdAt": "2025-11-16T15:00:00.000Z", + "id": "mi-envelope-001", + "lineItem": "ENVELOPE_DL", + "quantity": 300, + "stockRemaining": 2500, + "supplierId": "supplier-xyz", + "timestamp": "2025-11-16T15:00:00.000Z", + "updatedAt": "2025-11-16T15:00:00.000Z" + }, + "datacontenttype": "application/json", + "dataschema": "https://notify.nhs.uk/cloudevents/schemas/supplier-api/mi.SUBMITTED.1.0.0.schema.json", + "dataschemaversion": "1.0.0", + "id": "9a3d4c55-5f76-6c2c-b789-2f1cf4e5e333", + "plane": "data", + "recordedtime": "2025-11-16T15:00:00.250Z", + "severitynumber": 2, + "severitytext": "INFO", + "source": "/data-plane/supplier-api/prod/submit-mi", + "specversion": "1.0", + "subject": "mi/mi-envelope-001", + "time": "2025-11-16T15:00:00.000Z", + "traceparent": "00-2cf9873938ef65ff0660fd433e02541e-d9cf8d9380415553-01", + "type": "uk.nhs.notify.supplier-api.mi.SUBMITTED.v1" +} diff --git a/internal/events/src/events/__tests__/testData/mi.SUBMITTED-with-invalid-major-version.json b/internal/events/src/events/__tests__/testData/mi.SUBMITTED-with-invalid-major-version.json new file mode 100644 index 00000000..ff3c818f --- /dev/null +++ b/internal/events/src/events/__tests__/testData/mi.SUBMITTED-with-invalid-major-version.json @@ -0,0 +1,28 @@ +{ + "data": { + "createdAt": "2025-11-16T10:30:00.000Z", + "groupId": "group-456", + "id": "mi-test-001", + "lineItem": "LETTER_PRINT_A4", + "quantity": 150, + "specificationId": "spec-123", + "stockRemaining": 1000, + "supplierId": "supplier-789", + "timestamp": "2025-11-16T10:30:00.000Z", + "updatedAt": "2025-11-16T10:30:00.000Z" + }, + "datacontenttype": "application/json", + "dataschema": "https://notify.nhs.uk/cloudevents/schemas/supplier-api/mi.SUBMITTED.2.0.0.schema.json", + "dataschemaversion": "1.0.0", + "id": "8f2c3b44-4e65-5b1b-a678-1f0bf3d4d222", + "plane": "data", + "recordedtime": "2025-11-16T10:30:00.250Z", + "severitynumber": 2, + "severitytext": "INFO", + "source": "/data-plane/supplier-api/prod/submit-mi", + "specversion": "1.0", + "subject": "mi/mi-test-001", + "time": "2025-11-16T10:30:00.000Z", + "traceparent": "00-1bf8762827de54ee9559fc322d91430d-c8be7c8279304442-01", + "type": "uk.nhs.notify.supplier-api.mi.SUBMITTED.v1" +} diff --git a/internal/events/src/events/__tests__/testData/mi.SUBMITTED-with-missing-subject.json b/internal/events/src/events/__tests__/testData/mi.SUBMITTED-with-missing-subject.json new file mode 100644 index 00000000..ce9f769b --- /dev/null +++ b/internal/events/src/events/__tests__/testData/mi.SUBMITTED-with-missing-subject.json @@ -0,0 +1,24 @@ +{ + "data": { + "createdAt": "2025-11-16T10:30:00.000Z", + "id": "mi-invalid-001", + "lineItem": "LETTER_PRINT_A4", + "quantity": 150, + "supplierId": "supplier-789", + "timestamp": "2025-11-16T10:30:00.000Z", + "updatedAt": "2025-11-16T10:30:00.000Z" + }, + "datacontenttype": "application/json", + "dataschema": "https://notify.nhs.uk/cloudevents/schemas/supplier-api/mi.SUBMITTED.1.0.0.schema.json", + "dataschemaversion": "1.0.0", + "id": "8f2c3b44-4e65-5b1b-a678-1f0bf3d4d222", + "plane": "data", + "recordedtime": "2025-11-16T10:30:00.250Z", + "severitynumber": 2, + "severitytext": "INFO", + "source": "/data-plane/supplier-api/prod/submit-mi", + "specversion": "1.0", + "time": "2025-11-16T10:30:00.000Z", + "traceparent": "00-1bf8762827de54ee9559fc322d91430d-c8be7c8279304442-01", + "type": "uk.nhs.notify.supplier-api.mi.SUBMITTED.v1" +} diff --git a/internal/events/src/events/__tests__/testData/mi.SUBMITTED.json b/internal/events/src/events/__tests__/testData/mi.SUBMITTED.json new file mode 100644 index 00000000..00dc97a1 --- /dev/null +++ b/internal/events/src/events/__tests__/testData/mi.SUBMITTED.json @@ -0,0 +1,28 @@ +{ + "data": { + "createdAt": "2025-11-16T10:30:00.000Z", + "groupId": "group-456", + "id": "mi-test-001", + "lineItem": "LETTER_PRINT_A4", + "quantity": 150, + "specificationId": "spec-123", + "stockRemaining": 1000, + "supplierId": "supplier-789", + "timestamp": "2025-11-16T10:30:00.000Z", + "updatedAt": "2025-11-16T10:30:00.000Z" + }, + "datacontenttype": "application/json", + "dataschema": "https://notify.nhs.uk/cloudevents/schemas/supplier-api/mi.SUBMITTED.1.0.0.schema.json", + "dataschemaversion": "1.0.0", + "id": "8f2c3b44-4e65-5b1b-a678-1f0bf3d4d222", + "plane": "data", + "recordedtime": "2025-11-16T10:30:00.250Z", + "severitynumber": 2, + "severitytext": "INFO", + "source": "/data-plane/supplier-api/prod/submit-mi", + "specversion": "1.0", + "subject": "mi/mi-test-001", + "time": "2025-11-16T10:30:00.000Z", + "traceparent": "00-1bf8762827de54ee9559fc322d91430d-c8be7c8279304442-01", + "type": "uk.nhs.notify.supplier-api.mi.SUBMITTED.v1" +} diff --git a/internal/events/src/events/event-envelope.ts b/internal/events/src/events/event-envelope.ts new file mode 100644 index 00000000..ba9d4144 --- /dev/null +++ b/internal/events/src/events/event-envelope.ts @@ -0,0 +1,265 @@ +import { z } from "zod"; + +// eslint-disable-next-line import-x/prefer-default-export +export function EventEnvelope( + eventName: string, + resourceName: string, + data: TData, + statuses: readonly string[], + subjectPrefix?: string, +) { + const statusRegex = statuses.join("|"); + const subjectPrefixRegex = subjectPrefix + ? `${subjectPrefix}/[a-z0-9-]+/` + : ""; + const subjectExamplePrefix = subjectPrefix + ? `${subjectPrefix}/letter-rendering/` + : ""; + + // Pre-compute type strings to avoid repeated inference + const typeStrings = statuses.map( + (status) => + `uk.nhs.notify.supplier-api.${resourceName}.${status}.v1` as const, + ); + + const schemaExamples = statuses.map( + (status) => + `https://notify.nhs.uk/cloudevents/schemas/supplier-api/${resourceName}.${status}.1.0.0.schema.json`, + ); + + return z + .object({ + specversion: z.literal("1.0").meta({ + title: "CloudEvents spec version", + description: "CloudEvents specification version (fixed to 1.0).", + examples: ["1.0"], + }), + + id: z + .uuid() + .min(1) + .meta({ + title: "Event ID", + description: "Unique identifier for this event instance (UUID).", + examples: ["6f1c2a53-3d54-4a0a-9a0b-0e9ae2d4c111"], + }), + + type: z.enum(typeStrings as [string, ...string[]]).meta({ + title: `${eventName} event type`, + description: "Event type using reverse-DNS style", + examples: typeStrings, + }), + + plane: z.literal("data").meta({ + title: "plane", + description: "The event bus that this event will be published to", + examples: ["data"], + }), + + dataschema: z + .string() + .regex( + // eslint-disable-next-line security/detect-non-literal-regexp + new RegExp( + String.raw`^https://notify\.nhs\.uk/cloudevents/schemas/supplier-api/${resourceName}\.(?${statusRegex})\.1\.\d+\.\d+\.schema.json$`, + ), + ) + .meta({ + title: "Data Schema URI", + description: `URI of a schema that describes the event data\n\nData schema version must match the major version indicated by the type`, + examples: schemaExamples, + }), + + dataschemaversion: z + .string() + .regex(/^1\.\d+\.\d+$/) + .meta({ + title: "Data Schema URI", + description: `Version of the schema that describes the event data\n\nMust match the version in dataschema`, + examples: ["1.0.0"], + }), + + source: z + .string() + .regex(/^\/data-plane\/supplier-api(?:\/.*)?$/) + .meta({ + title: "Event Source", + description: + "Logical event producer path within the supplier-api domain", + }), + + subject: z + .string() + + .regex(new RegExp(`^${subjectPrefixRegex}${resourceName}/[a-z0-9-]+$`)) + .meta({ + title: "Event Subject", + description: + "Resource path (no leading slash) within the source made of segments separated by '/'.", + examples: [ + `${subjectExamplePrefix}${resourceName}/f47ac10b-58cc-4372-a567-0e02b2c3d479`, + ], + }), + + data, + + time: z.iso.datetime().meta({ + title: "Event Time", + description: "Timestamp when the event occurred (RFC 3339).", + examples: ["2025-10-01T10:15:30.000Z"], + }), + + datacontenttype: z.literal("application/json").meta({ + title: "Data Content Type", + description: + "Media type for the data field (fixed to application/json).", + examples: ["application/json"], + }), + + traceparent: z + .string() + .min(1) + .regex(/^00-[0-9a-f]{32}-[0-9a-f]{16}-[0-9a-f]{2}$/) + .meta({ + title: "Traceparent", + description: "W3C Trace Context traceparent header value.", + examples: ["00-0af7651916cd43dd8448eb211c80319c-b7ad6b7169203331-01"], + }), + + tracestate: z.optional( + z.string().meta({ + title: "Tracestate", + description: "Optional W3C Trace Context tracestate header value.", + examples: ["rojo=00f067aa0ba902b7,congo=t61rcWkgMzE"], + }), + ), + + partitionkey: z.optional( + z + .string() + .min(1) + .max(64) + .regex(/^[a-z0-9-]+$/) + .meta({ + title: "Partition Key", + description: + "Partition / ordering key (lowercase alphanumerics and hyphen, 1-64 chars).", + examples: ["customer-920fca11"], + }), + ), + + recordedtime: z.iso.datetime().meta({ + title: "Recorded Time", + description: + "Timestamp when the event was recorded/persisted (should be >= time).", + examples: ["2025-10-01T10:15:30.250Z"], + }), + + sampledrate: z.optional( + z + .number() + .int() + .min(1) + .meta({ + title: "Sampled Rate", + description: + "Sampling factor: number of similar occurrences this event represents.", + examples: [5], + }), + ), + + sequence: z.optional( + z + .string() + .regex(/^\d{20}$/) + .meta({ + title: "Sequence", + description: + "Zero-padded 20 digit numeric sequence (lexicographically sortable).", + examples: ["00000000000000000042"], + }), + ), + + severitytext: z.optional( + z.enum(["TRACE", "DEBUG", "INFO", "WARN", "ERROR", "FATAL"]).meta({ + title: "Severity Text", + description: "Log severity level name.", + examples: ["DEBUG"], + }), + ), + + severitynumber: z + .number() + .int() + .min(0) + .max(5) + .meta({ + title: "Severity Number", + description: + "Numeric severity (TRACE=0, DEBUG=1, INFO=2, WARN=3, ERROR=4, FATAL=5).", + examples: [1], + }), + + dataclassification: z.optional( + z.enum(["public", "internal", "confidential", "restricted"]).meta({ + title: "Data Classification", + description: "Data sensitivity classification.", + examples: ["restricted"], + }), + ), + + dataregulation: z.optional( + z + .enum([ + "GDPR", + "HIPAA", + "PCI-DSS", + "ISO-27001", + "NIST-800-53", + "CCPA", + ]) + .meta({ + title: "Data Regulation", + description: "Regulatory regime tag applied to this data.", + examples: ["ISO-27001"], + }), + ), + + datacategory: z.optional( + z + .enum(["non-sensitive", "standard", "sensitive", "special-category"]) + .meta({ + title: "Data Category", + description: + "Data category classification (e.g. standard, special-category).", + examples: ["sensitive"], + }), + ), + }) + .superRefine((obj, ctx) => { + if (obj.severitytext !== undefined) { + const mapping = { + TRACE: 0, + DEBUG: 1, + INFO: 2, + WARN: 3, + ERROR: 4, + FATAL: 5, + }; + if (obj.severitynumber !== mapping[obj.severitytext]) { + ctx.addIssue({ + code: "custom", + message: `severitynumber must be ${mapping[obj.severitytext]} when severitytext is ${obj.severitytext}`, + path: ["severitynumber"], + }); + } + } + if (obj.severitynumber && obj.severitytext === undefined) { + ctx.addIssue({ + code: "custom", + message: "severitytext is required when severitynumber is present", + path: ["severitytext"], + }); + } + }); +} diff --git a/internal/events/src/events/letter-events.ts b/internal/events/src/events/letter-events.ts new file mode 100644 index 00000000..49795068 --- /dev/null +++ b/internal/events/src/events/letter-events.ts @@ -0,0 +1,45 @@ +import { z } from "zod"; +import { + $Letter, + $LetterStatus, + LetterStatus, +} from "@nhsdigital/nhs-notify-event-schemas-supplier-api/src/domain/letter"; +import { EventEnvelope } from "@nhsdigital/nhs-notify-event-schemas-supplier-api/src/events/event-envelope"; + +/** + * A generic schema for parsing any letter status change event + */ +export const $LetterEvent = EventEnvelope( + "letter", + "letter", + $Letter, + $LetterStatus.options, + "letter-origin", +).meta({ + title: `letter.* Event`, + description: `Event schema for generic letter status change`, +}); +export type LetterEvent = z.infer; + +/** + * Specialise the generic event schema for a single status + * @param status + */ +const eventSchema = (status: LetterStatus) => + EventEnvelope( + `letter.${status}`, + "letter", + $Letter, + [status], + "letter-origin", + ).meta({ + title: `letter.${status} Event`, + description: `Event schema for letter status change to ${status}`, + }); + +export const letterEventMap = Object.fromEntries( + $LetterStatus.options.map((status) => [ + `letter.${status}`, + eventSchema(status), + ]), +); diff --git a/internal/events/src/events/mi-events.ts b/internal/events/src/events/mi-events.ts new file mode 100644 index 00000000..b6d4e44d --- /dev/null +++ b/internal/events/src/events/mi-events.ts @@ -0,0 +1,11 @@ +import { z } from "zod"; +import { EventEnvelope } from "@nhsdigital/nhs-notify-event-schemas-supplier-api/src/events/event-envelope"; +import { $MI } from "@nhsdigital/nhs-notify-event-schemas-supplier-api/src/domain/mi"; + +export const $MISubmittedEvent = EventEnvelope("mi.SUBMITTED", "mi", $MI, [ + "SUBMITTED", +]).meta({ + title: `mi.SUBMITTED Event`, + description: `Event schema for reporting that MI data has been submitted`, +}); +export type MISubmittedEvent = z.infer; diff --git a/internal/events/src/index.ts b/internal/events/src/index.ts new file mode 100644 index 00000000..c4255d26 --- /dev/null +++ b/internal/events/src/index.ts @@ -0,0 +1,6 @@ +export * from "./domain/letter"; +export * from "./domain/mi"; +export { default as DomainBase } from "./domain/domain-base"; +export * from "./events/event-envelope"; +export * from "./events/letter-events"; +export * from "./events/mi-events"; diff --git a/internal/events/tsconfig.build.json b/internal/events/tsconfig.build.json new file mode 100644 index 00000000..69e51c06 --- /dev/null +++ b/internal/events/tsconfig.build.json @@ -0,0 +1,17 @@ +{ + "compilerOptions": { + "noEmit": false, + "outDir": "dist", + "rootDir": "src" + }, + "exclude": [ + "node_modules", + "dist", + "src/cli", + "src/**/__tests__" + ], + "extends": "./tsconfig.json", + "include": [ + "src/**/*" + ] +} diff --git a/internal/events/tsconfig.jest.json b/internal/events/tsconfig.jest.json new file mode 100644 index 00000000..b6126778 --- /dev/null +++ b/internal/events/tsconfig.jest.json @@ -0,0 +1,9 @@ +{ + "exclude": [ + "./dist/" + ], + "extends": "./tsconfig.json", + "include": [ + "." + ] +} diff --git a/internal/events/tsconfig.json b/internal/events/tsconfig.json new file mode 100644 index 00000000..fa5c2949 --- /dev/null +++ b/internal/events/tsconfig.json @@ -0,0 +1,19 @@ +{ + "compilerOptions": { + "declaration": true, + "isolatedModules": true, + "module": "commonjs", + "outDir": "dist", + "resolveJsonModule": true, + "rootDir": "src" + }, + "exclude": [ + "node_modules", + "dist" + ], + "extends": "../../tsconfig.base.json", + "include": [ + "src/**/*", + "package.json" + ] +} diff --git a/internal/helpers/jest.config.ts b/internal/helpers/jest.config.ts index 3c1866d9..ff7aef38 100644 --- a/internal/helpers/jest.config.ts +++ b/internal/helpers/jest.config.ts @@ -1,15 +1,14 @@ -import type { Config } from "jest"; - -const config: Config = { +export default { preset: "ts-jest", testEnvironment: "node", - testMatch: ["**/src/__tests__/**/*.ts?(x)", "**/src/?(*.)+(spec|test).ts?(x)"], + testMatch: [ + "**/src/__tests__/**/*.ts?(x)", + "**/src/?(*.)+(spec|test).ts?(x)", + ], collectCoverageFrom: ["src/**/*.ts"], coveragePathIgnorePatterns: ["/node_modules/", "__tests__"], testPathIgnorePatterns: ["/node_modules/", "/dist/"], transform: { - "^.+\\.tsx?$": ["ts-jest", { }] - } + "^.+\\.tsx?$": ["ts-jest", {}], + }, }; - -export default config; diff --git a/internal/helpers/package.json b/internal/helpers/package.json index c6852074..4e1a73f5 100644 --- a/internal/helpers/package.json +++ b/internal/helpers/package.json @@ -6,15 +6,15 @@ "devDependencies": { "@stylistic/eslint-plugin": "^3.1.0", "@tsconfig/node22": "^22.0.2", - "@types/jest": "^29.5.14", + "@types/jest": "^30.0.0", "@typescript-eslint/eslint-plugin": "^8.27.0", "@typescript-eslint/parser": "^8.27.0", "eslint": "^9.27.0", "eslint-plugin-jest": "^29.0.1", - "jest": "^30.1.3", + "jest": "^30.2.0", "ts-jest": "^29.4.0", "ts-node": "^10.9.2", - "typescript": "^5.8.3" + "typescript": "^5.9.3" }, "license": "MIT", "main": "src/index.ts", diff --git a/internal/helpers/src/__tests__/id-ref.test.ts b/internal/helpers/src/__tests__/id-ref.test.ts index 3204b305..449e7798 100644 --- a/internal/helpers/src/__tests__/id-ref.test.ts +++ b/internal/helpers/src/__tests__/id-ref.test.ts @@ -12,7 +12,9 @@ describe("idRef", () => { // Should validate UUIDs like the original schema's domainId expect(() => refField.parse("not-a-uuid")).toThrow(); - expect(() => refField.parse("123e4567-e89b-12d3-a456-426614174000")).not.toThrow(); + expect(() => + refField.parse("123e4567-e89b-12d3-a456-426614174000"), + ).not.toThrow(); }); it("should add reference metadata", () => { @@ -20,10 +22,12 @@ describe("idRef", () => { expect(refField).toBeDefined(); expect(z.globalRegistry.has(refField)).toBe(true); - expect(z.globalRegistry.get(refField)).toEqual(expect.objectContaining({ - title: "TestEntity ID Reference", - description: "Reference to a TestEntity by its unique identifier", - })); + expect(z.globalRegistry.get(refField)).toEqual( + expect.objectContaining({ + title: "TestEntity ID Reference", + description: "Reference to a TestEntity by its unique identifier", + }), + ); }); it("should use custom ID field name when provided", () => { diff --git a/internal/helpers/src/environment.ts b/internal/helpers/src/environment.ts index ce997d4f..440e9164 100644 --- a/internal/helpers/src/environment.ts +++ b/internal/helpers/src/environment.ts @@ -1,11 +1,13 @@ -import { z } from 'zod'; +import { z } from "zod"; /** * Deployment / execution environment identifier. * Intentionally liberal; constrain in callers as needed. */ -export const $Environment = z.string().meta({ - title: 'Environment', - description: 'The environment in which the configuration has effect', - examples: ['dev', 'int', 'prod'], +const $Environment = z.string().meta({ + title: "Environment", + description: "The environment in which the configuration has effect", + examples: ["dev", "int", "prod"], }); + +export default $Environment; diff --git a/internal/helpers/src/id-ref.ts b/internal/helpers/src/id-ref.ts index e757725d..9a3973bf 100644 --- a/internal/helpers/src/id-ref.ts +++ b/internal/helpers/src/id-ref.ts @@ -24,15 +24,19 @@ import { z } from "zod"; // Overload for when a specific ID field is provided export function idRef< T extends z.ZodObject>, - K extends keyof T["shape"] & string + K extends keyof T["shape"] & string, >(schema: T, idFieldName: K, entityName?: string): T["shape"][K]; // Overload for when using the default "domainId" field export function idRef< T extends z.ZodObject> & { - shape: { domainId: z.ZodTypeAny } - } ->(schema: T, idFieldName?: undefined, entityName?: string): T["shape"]["domainId"]; + shape: { domainId: z.ZodTypeAny }; + }, +>( + schema: T, + idFieldName?: undefined, + entityName?: string, +): T["shape"]["domainId"]; // Implementation export function idRef< diff --git a/internal/helpers/src/index.ts b/internal/helpers/src/index.ts index 9a79b038..cc2c27e5 100644 --- a/internal/helpers/src/index.ts +++ b/internal/helpers/src/index.ts @@ -4,7 +4,6 @@ */ // Export all helpers -export * from './version'; -export * from './environment'; +export * from "./version"; +export { default as $Environment } from "./environment"; export * from "./id-ref"; -export * from "./domain-base"; diff --git a/internal/helpers/src/version.ts b/internal/helpers/src/version.ts index ab1f300a..41db690f 100644 --- a/internal/helpers/src/version.ts +++ b/internal/helpers/src/version.ts @@ -1,4 +1,4 @@ -import { z } from 'zod'; +import { z } from "zod"; /** * Semantic version (major.minor.patch) with numeric segments only. @@ -6,7 +6,7 @@ import { z } from 'zod'; */ export const $Version = z .string() - .regex(/^[0-9]+\.[0-9]+\.[0-9]+$/) - .brand('Version'); + .regex(/^\d+\.\d+\.\d+$/) + .brand("Version"); export type Version = z.infer; diff --git a/lambdas/api-handler/jest.config.ts b/lambdas/api-handler/jest.config.ts index d30f4cd1..f68246e6 100644 --- a/lambdas/api-handler/jest.config.ts +++ b/lambdas/api-handler/jest.config.ts @@ -1,7 +1,11 @@ -import type { Config } from 'jest'; - -export const baseJestConfig: Config = { - preset: 'ts-jest', +export const baseJestConfig = { + preset: "ts-jest", + extensionsToTreatAsEsm: [".ts"], + transform: { + "^.+\\.ts$": ["ts-jest", { + useESM: true + }] + }, // Automatically clear mock calls, instances, contexts and results before every test clearMocks: true, @@ -10,10 +14,10 @@ export const baseJestConfig: Config = { collectCoverage: true, // The directory where Jest should output its coverage files - coverageDirectory: './.reports/unit/coverage', + coverageDirectory: "./.reports/unit/coverage", // Indicates which provider should be used to instrument code for coverage - coverageProvider: 'babel', + coverageProvider: "babel", coverageThreshold: { global: { @@ -24,36 +28,35 @@ export const baseJestConfig: Config = { }, }, - coveragePathIgnorePatterns: ['/__tests__/'], - transform: { '^.+\\.ts$': 'ts-jest' }, - testPathIgnorePatterns: ['.build'], - testMatch: ['**/?(*.)+(spec|test).[jt]s?(x)'], + coveragePathIgnorePatterns: ["/__tests__/"], + testPathIgnorePatterns: [".build"], + testMatch: ["**/?(*.)+(spec|test).[jt]s?(x)"], // Use this configuration option to add custom reporters to Jest reporters: [ - 'default', + "default", [ - 'jest-html-reporter', + "jest-html-reporter", { - pageTitle: 'Test Report', - outputPath: './.reports/unit/test-report.html', + pageTitle: "Test Report", + outputPath: "./.reports/unit/test-report.html", includeFailureMsg: true, }, ], ], // The test environment that will be used for testing - testEnvironment: 'jsdom', + testEnvironment: "jsdom", }; const utilsJestConfig = { ...baseJestConfig, - testEnvironment: 'node', + testEnvironment: "node", coveragePathIgnorePatterns: [ ...(baseJestConfig.coveragePathIgnorePatterns ?? []), - 'zod-validators.ts', + "zod-validators.ts", ], }; diff --git a/lambdas/api-handler/package.json b/lambdas/api-handler/package.json index 18a422af..5a436473 100644 --- a/lambdas/api-handler/package.json +++ b/lambdas/api-handler/package.json @@ -1,19 +1,24 @@ { "dependencies": { + "@aws-sdk/client-dynamodb": "^3.925.0", + "@aws-sdk/client-s3": "^3.925.0", + "@aws-sdk/client-sqs": "^3.925.0", + "@aws-sdk/lib-dynamodb": "^3.925.0", + "@aws-sdk/s3-request-presigner": "^3.925.0", "@internal/datastore": "*", "@internal/helpers": "*", "esbuild": "^0.25.11", - "pino": "^9.7.0" + "pino": "^9.7.0", + "zod": "^4.1.11" }, "devDependencies": { - "@aws-sdk/s3-request-presigner": "^3.901.0", "@tsconfig/node22": "^22.0.2", "@types/aws-lambda": "^8.10.148", - "@types/jest": "^29.5.14", + "@types/jest": "^30.0.0", "jest": "^30.2.0", - "jest-mock-extended": "^3.0.7", - "typescript": "^5.8.3", - "zod": "^4.1.11" + "jest-mock-extended": "^4.0.0", + "ts-jest": "^29.4.0", + "typescript": "^5.9.3" }, "name": "nhs-notify-supplier-api-handler", "private": true, diff --git a/lambdas/api-handler/src/config/__tests__/deps.test.ts b/lambdas/api-handler/src/config/__tests__/deps.test.ts index 4381d961..64762d66 100644 --- a/lambdas/api-handler/src/config/__tests__/deps.test.ts +++ b/lambdas/api-handler/src/config/__tests__/deps.test.ts @@ -1,16 +1,14 @@ +import type { Deps } from "../deps"; -import type { Deps } from '../deps'; - -describe('createDependenciesContainer', () => { - +describe("createDependenciesContainer", () => { const env = { - LETTERS_TABLE_NAME: 'LettersTable', - LETTER_TTL_HOURS: 12960, - MI_TABLE_NAME: 'MITable', + LETTERS_TABLE_NAME: "LettersTable", + LETTER_TTL_HOURS: 12_960, + MI_TABLE_NAME: "MITable", MI_TTL_HOURS: 2160, - SUPPLIER_ID_HEADER: 'nhsd-supplier-id', - APIM_CORRELATION_HEADER: 'nhsd-correlation-id', - DOWNLOAD_URL_TTL_SECONDS: 60 + SUPPLIER_ID_HEADER: "nhsd-supplier-id", + APIM_CORRELATION_HEADER: "nhsd-correlation-id", + DOWNLOAD_URL_TTL_SECONDS: 60, }; beforeEach(() => { @@ -18,7 +16,7 @@ describe('createDependenciesContainer', () => { jest.resetModules(); // pino - jest.mock('pino', () => ({ + jest.mock("pino", () => ({ __esModule: true, default: jest.fn(() => ({ info: jest.fn(), @@ -28,47 +26,57 @@ describe('createDependenciesContainer', () => { })), })); - jest.mock('@aws-sdk/client-s3', () => ({ + jest.mock("@aws-sdk/client-s3", () => ({ S3Client: jest.fn(), })); + jest.mock("@aws-sdk/client-sqs", () => ({ + SQSClient: jest.fn(), + })); + // Repo client - jest.mock('@internal/datastore', () => ({ + jest.mock("@internal/datastore", () => ({ LetterRepository: jest.fn(), MIRepository: jest.fn(), + DBHealthcheck: jest.fn(), })); // Env - jest.mock('../env', () => ({envVars: env})); + jest.mock("../env", () => ({ envVars: env })); }); - test('constructs deps and wires repository config correctly', async () => { + test("constructs deps and wires repository config correctly", async () => { // get current mock instances - const { S3Client } = jest.requireMock('@aws-sdk/client-s3') as { S3Client: jest.Mock }; - const pinoMock = jest.requireMock('pino') as { default: jest.Mock }; - const { LetterRepository, MIRepository } = jest.requireMock('@internal/datastore') as { - LetterRepository: jest.Mock, - MIRepository: jest.Mock - }; + const { S3Client } = jest.requireMock("@aws-sdk/client-s3"); + const { SQSClient } = jest.requireMock("@aws-sdk/client-sqs"); + const pinoMock = jest.requireMock("pino"); + const { LetterRepository, MIRepository } = jest.requireMock( + "@internal/datastore", + ); - const { createDependenciesContainer } = require('../deps'); + // allow re-import of deps to leverage mocks + // eslint-disable-next-line @typescript-eslint/no-require-imports + const { createDependenciesContainer } = require("../deps"); const deps: Deps = createDependenciesContainer(); expect(S3Client).toHaveBeenCalledTimes(1); + + expect(SQSClient).toHaveBeenCalledTimes(1); + expect(pinoMock.default).toHaveBeenCalledTimes(1); expect(LetterRepository).toHaveBeenCalledTimes(1); - const letterRepoCtorArgs = (LetterRepository as jest.Mock).mock.calls[0]; + const letterRepoCtorArgs = LetterRepository.mock.calls[0]; expect(letterRepoCtorArgs[2]).toEqual({ - lettersTableName: 'LettersTable', - lettersTtlHours: 12960 + lettersTableName: "LettersTable", + lettersTtlHours: 12_960, }); expect(MIRepository).toHaveBeenCalledTimes(1); - const miRepoCtorArgs = (MIRepository as jest.Mock).mock.calls[0]; + const miRepoCtorArgs = MIRepository.mock.calls[0]; expect(miRepoCtorArgs[2]).toEqual({ - miTableName: 'MITable', - miTtlHours: 2160 + miTableName: "MITable", + miTtlHours: 2160, }); expect(deps.env).toEqual(env); diff --git a/lambdas/api-handler/src/config/__tests__/env.test.ts b/lambdas/api-handler/src/config/__tests__/env.test.ts index 2399c941..afbca1d8 100644 --- a/lambdas/api-handler/src/config/__tests__/env.test.ts +++ b/lambdas/api-handler/src/config/__tests__/env.test.ts @@ -1,6 +1,9 @@ -import { ZodError } from 'zod'; +/* eslint-disable @typescript-eslint/no-require-imports */ +/* Allow require imports to enable re-import of modules */ -describe('lambdaEnv', () => { +import { ZodError } from "zod"; + +describe("lambdaEnv", () => { const OLD_ENV = process.env; beforeEach(() => { @@ -12,62 +15,64 @@ describe('lambdaEnv', () => { process.env = OLD_ENV; // Restore }); - it('should load all environment variables successfully', () => { - process.env.SUPPLIER_ID_HEADER = 'nhsd-supplier-id'; - process.env.APIM_CORRELATION_HEADER = 'nhsd-correlation-id'; - process.env.LETTERS_TABLE_NAME = 'letters-table'; - process.env.MI_TABLE_NAME = 'mi-table'; - process.env.LETTER_TTL_HOURS = '12960'; - process.env.MI_TTL_HOURS = '2160'; - process.env.DOWNLOAD_URL_TTL_SECONDS = '60'; - process.env.MAX_LIMIT = '2500'; + it("should load all environment variables successfully", () => { + process.env.SUPPLIER_ID_HEADER = "nhsd-supplier-id"; + process.env.APIM_CORRELATION_HEADER = "nhsd-correlation-id"; + process.env.LETTERS_TABLE_NAME = "letters-table"; + process.env.MI_TABLE_NAME = "mi-table"; + process.env.LETTER_TTL_HOURS = "12960"; + process.env.MI_TTL_HOURS = "2160"; + process.env.DOWNLOAD_URL_TTL_SECONDS = "60"; + process.env.MAX_LIMIT = "2500"; + process.env.QUEUE_URL = "url"; - const { envVars } = require('../env'); + const { envVars } = require("../env"); expect(envVars).toEqual({ - SUPPLIER_ID_HEADER: 'nhsd-supplier-id', - APIM_CORRELATION_HEADER: 'nhsd-correlation-id', - LETTERS_TABLE_NAME: 'letters-table', - MI_TABLE_NAME: 'mi-table', - LETTER_TTL_HOURS: 12960, + SUPPLIER_ID_HEADER: "nhsd-supplier-id", + APIM_CORRELATION_HEADER: "nhsd-correlation-id", + LETTERS_TABLE_NAME: "letters-table", + MI_TABLE_NAME: "mi-table", + LETTER_TTL_HOURS: 12_960, MI_TTL_HOURS: 2160, DOWNLOAD_URL_TTL_SECONDS: 60, MAX_LIMIT: 2500, + QUEUE_URL: "url", }); }); - it('should throw if a required env var is missing', () => { - process.env.SUPPLIER_ID_HEADER = 'nhsd-supplier-id'; - process.env.APIM_CORRELATION_HEADER = 'nhsd-correlation-id'; + it("should throw if a required env var is missing", () => { + process.env.SUPPLIER_ID_HEADER = "nhsd-supplier-id"; + process.env.APIM_CORRELATION_HEADER = "nhsd-correlation-id"; process.env.LETTERS_TABLE_NAME = undefined; // simulate missing var - process.env.MI_TABLE_NAME = 'mi-table'; - process.env.LETTER_TTL_HOURS = '12960'; - process.env.MI_TTL_HOURS = '2160'; - process.env.DOWNLOAD_URL_TTL_SECONDS = '60'; + process.env.MI_TABLE_NAME = "mi-table"; + process.env.LETTER_TTL_HOURS = "12960"; + process.env.MI_TTL_HOURS = "2160"; + process.env.DOWNLOAD_URL_TTL_SECONDS = "60"; - expect(() => require('../env')).toThrow(ZodError); + expect(() => require("../env")).toThrow(ZodError); }); - it('should not throw if optional are not set', () => { - process.env.SUPPLIER_ID_HEADER = 'nhsd-supplier-id'; - process.env.APIM_CORRELATION_HEADER = 'nhsd-correlation-id'; - process.env.LETTERS_TABLE_NAME = 'letters-table'; - process.env.MI_TABLE_NAME = 'mi-table'; - process.env.LETTER_TTL_HOURS = '12960'; - process.env.MI_TTL_HOURS = '2160'; - process.env.DOWNLOAD_URL_TTL_SECONDS = '60'; + it("should not throw if optional are not set", () => { + process.env.SUPPLIER_ID_HEADER = "nhsd-supplier-id"; + process.env.APIM_CORRELATION_HEADER = "nhsd-correlation-id"; + process.env.LETTERS_TABLE_NAME = "letters-table"; + process.env.MI_TABLE_NAME = "mi-table"; + process.env.LETTER_TTL_HOURS = "12960"; + process.env.MI_TTL_HOURS = "2160"; + process.env.DOWNLOAD_URL_TTL_SECONDS = "60"; - const { envVars } = require('../env'); + const { envVars } = require("../env"); expect(envVars).toEqual({ - SUPPLIER_ID_HEADER: 'nhsd-supplier-id', - APIM_CORRELATION_HEADER: 'nhsd-correlation-id', - LETTERS_TABLE_NAME: 'letters-table', - MI_TABLE_NAME: 'mi-table', - LETTER_TTL_HOURS: 12960, + SUPPLIER_ID_HEADER: "nhsd-supplier-id", + APIM_CORRELATION_HEADER: "nhsd-correlation-id", + LETTERS_TABLE_NAME: "letters-table", + MI_TABLE_NAME: "mi-table", + LETTER_TTL_HOURS: 12_960, MI_TTL_HOURS: 2160, DOWNLOAD_URL_TTL_SECONDS: 60, - MAX_LIMIT: undefined + MAX_LIMIT: undefined, }); }); }); diff --git a/lambdas/api-handler/src/config/deps.ts b/lambdas/api-handler/src/config/deps.ts index 1942bc6c..c8d187bf 100644 --- a/lambdas/api-handler/src/config/deps.ts +++ b/lambdas/api-handler/src/config/deps.ts @@ -1,16 +1,23 @@ import { S3Client } from "@aws-sdk/client-s3"; -import { DynamoDBClient } from '@aws-sdk/client-dynamodb'; -import { DynamoDBDocumentClient } from '@aws-sdk/lib-dynamodb'; -import pino from 'pino'; -import { LetterRepository, MIRepository } from '../../../../internal/datastore'; -import { envVars, EnvVars } from "../config/env"; +import { DynamoDBClient } from "@aws-sdk/client-dynamodb"; +import { DynamoDBDocumentClient } from "@aws-sdk/lib-dynamodb"; +import { SQSClient } from "@aws-sdk/client-sqs"; +import pino from "pino"; +import { + DBHealthcheck, + LetterRepository, + MIRepository, +} from "@internal/datastore"; +import { EnvVars, envVars } from "./env"; export type Deps = { s3Client: S3Client; + sqsClient: SQSClient; letterRepo: LetterRepository; miRepo: MIRepository; + dbHealthcheck: DBHealthcheck; logger: pino.Logger; - env: EnvVars + env: EnvVars; }; function createDocumentClient(): DynamoDBDocumentClient { @@ -18,38 +25,49 @@ function createDocumentClient(): DynamoDBDocumentClient { return DynamoDBDocumentClient.from(ddbClient); } -function createLetterRepository(documentClient: DynamoDBDocumentClient, log: pino.Logger, envVars: EnvVars): LetterRepository { - const ddbClient = new DynamoDBClient({}); - const docClient = DynamoDBDocumentClient.from(ddbClient); +function createLetterRepository( + log: pino.Logger, + environment: EnvVars, +): LetterRepository { const config = { - lettersTableName: envVars.LETTERS_TABLE_NAME, - lettersTtlHours: envVars.LETTER_TTL_HOURS + lettersTableName: environment.LETTERS_TABLE_NAME, + lettersTtlHours: environment.LETTER_TTL_HOURS, }; - return new LetterRepository(docClient, log, config); + return new LetterRepository(createDocumentClient(), log, config); } -function createMIRepository(documentClient: DynamoDBDocumentClient, log: pino.Logger, envVars: EnvVars): MIRepository { - const ddbClient = new DynamoDBClient({}); - const docClient = DynamoDBDocumentClient.from(ddbClient); +function createDBHealthcheck(environment: EnvVars): DBHealthcheck { const config = { - miTableName: envVars.MI_TABLE_NAME, - miTtlHours: envVars.MI_TTL_HOURS + lettersTableName: environment.LETTERS_TABLE_NAME, + lettersTtlHours: environment.LETTER_TTL_HOURS, }; - return new MIRepository(docClient, log, config); + return new DBHealthcheck(createDocumentClient(), config); } -export function createDependenciesContainer(): Deps { +function createMIRepository( + log: pino.Logger, + environment: EnvVars, +): MIRepository { + const config = { + miTableName: environment.MI_TABLE_NAME, + miTtlHours: environment.MI_TTL_HOURS, + }; + + return new MIRepository(createDocumentClient(), log, config); +} +export function createDependenciesContainer(): Deps { const log = pino(); - const documentClient = createDocumentClient(); return { s3Client: new S3Client(), - letterRepo: createLetterRepository(documentClient, log, envVars), - miRepo: createMIRepository(documentClient, log, envVars), + sqsClient: new SQSClient(), + letterRepo: createLetterRepository(log, envVars), + miRepo: createMIRepository(log, envVars), + dbHealthcheck: createDBHealthcheck(envVars), logger: log, - env: envVars + env: envVars, }; } diff --git a/lambdas/api-handler/src/config/env.ts b/lambdas/api-handler/src/config/env.ts index f77f455e..fbdc0924 100644 --- a/lambdas/api-handler/src/config/env.ts +++ b/lambdas/api-handler/src/config/env.ts @@ -1,4 +1,4 @@ -import {z} from 'zod'; +import { z } from "zod"; const EnvVarsSchema = z.object({ SUPPLIER_ID_HEADER: z.string(), @@ -6,9 +6,10 @@ const EnvVarsSchema = z.object({ LETTERS_TABLE_NAME: z.string(), MI_TABLE_NAME: z.string(), LETTER_TTL_HOURS: z.coerce.number().int(), - MI_TTL_HOURS: z.coerce.number().int(), + MI_TTL_HOURS: z.coerce.number().int(), DOWNLOAD_URL_TTL_SECONDS: z.coerce.number().int(), - MAX_LIMIT: z.coerce.number().int().optional() + MAX_LIMIT: z.coerce.number().int().optional(), + QUEUE_URL: z.coerce.string().optional(), }); export type EnvVars = z.infer; diff --git a/lambdas/api-handler/src/contracts/errors.ts b/lambdas/api-handler/src/contracts/errors.ts index a508739b..ceb9ce51 100644 --- a/lambdas/api-handler/src/contracts/errors.ts +++ b/lambdas/api-handler/src/contracts/errors.ts @@ -1,45 +1,49 @@ export interface ApiError { id: string; code: ApiErrorCode; - links: {about: 'https://digital.nhs.uk/developer/api-catalogue/nhs-notify-supplier'}; + links: { + about: "https://digital.nhs.uk/developer/api-catalogue/nhs-notify-supplier"; + }; status: ApiErrorStatus; title: ApiErrorTitle; detail: ApiErrorDetail | string; } export enum ApiErrorCode { - InternalServerError = 'NOTIFY_INTERNAL_SERVER_ERROR', - InvalidRequest = 'NOTIFY_INVALID_REQUEST', - NotFound = 'NOTIFY_LETTER_NOT_FOUND' + InternalServerError = "NOTIFY_INTERNAL_SERVER_ERROR", + InvalidRequest = "NOTIFY_INVALID_REQUEST", + NotFound = "NOTIFY_LETTER_NOT_FOUND", } export enum ApiErrorTitle { - InternalServerError = 'Internal server error', - InvalidRequest = 'Invalid request', - NotFound = 'Not found' + InternalServerError = "Internal server error", + InvalidRequest = "Invalid request", + NotFound = "Not found", } export enum ApiErrorStatus { InternalServerError = "500", InvalidRequest = "400", - NotFound = "404" + NotFound = "404", } export enum ApiErrorDetail { - NotFoundLetterId = 'No resource found with that ID', - InvalidRequestMissingBody = 'The request is missing the body', - InvalidRequestMissingLetterIdPathParameter = 'The request is missing the letter id path parameter', - InvalidRequestLetterIdsMismatch = 'The letter ID in the request body does not match the letter ID path parameter', - InvalidRequestBody = 'The request body is invalid', - InvalidRequestLimitNotANumber = 'The limit parameter is not a number', - InvalidRequestLimitNotInRange = 'The limit parameter must be a positive number not greater than %s', + NotFoundLetterId = "No resource found with that ID", + InvalidRequestMissingBody = "The request is missing the body", + InvalidRequestMissingLetterIdPathParameter = "The request is missing the letter id path parameter", + InvalidRequestLetterIdsMismatch = "The letter ID in the request body does not match the letter ID path parameter", + InvalidRequestBody = "The request body is invalid", + InvalidRequestLimitNotANumber = "The limit parameter is not a number", + InvalidRequestLimitNotInRange = "The limit parameter must be a positive number not greater than %s", InvalidRequestLimitOnly = "Only 'limit' query parameter is supported", - InvalidRequestNoRequestId = 'The request does not contain a request id', - InvalidRequestTimestamp = 'Timestamps should be UTC date/times in ISO8601 format, with a Z suffix' + InvalidRequestNoRequestId = "The request does not contain a request id", + InvalidRequestTimestamp = "Timestamps should be UTC date/times in ISO8601 format, with a Z suffix", + InvalidRequestLettersToUpdate = "The request exceeds the maximum of %s items allowed for update", + InvalidRequestDuplicateLetterId = "The request cannot include multiple letter objects with the same id", } export function buildApiError(params: { - id: string + id: string; code: ApiErrorCode; status: ApiErrorStatus; title: ApiErrorTitle; @@ -48,9 +52,16 @@ export function buildApiError(params: { return { id: params.id, code: params.code, - links: { about: 'https://digital.nhs.uk/developer/api-catalogue/nhs-notify-supplier' }, + links: { + about: + "https://digital.nhs.uk/developer/api-catalogue/nhs-notify-supplier", + }, status: params.status, title: params.title, detail: params.detail, }; } + +export interface ErrorResponse { + errors: ApiError[]; +} diff --git a/lambdas/api-handler/src/contracts/json-api.ts b/lambdas/api-handler/src/contracts/json-api.ts index 46607d29..44f3e60f 100644 --- a/lambdas/api-handler/src/contracts/json-api.ts +++ b/lambdas/api-handler/src/contracts/json-api.ts @@ -1,9 +1,10 @@ -import { z } from 'zod'; +import { z } from "zod"; // Single document wrapper export const makeDocumentSchema = (resourceSchema: T) => z.object({ data: resourceSchema }).strict(); // Collection document wrapper -export const makeCollectionSchema = (resourceSchema: T) => - z.object({ data: z.array(resourceSchema) }).strict(); +export const makeCollectionSchema = ( + resourceSchema: T, +) => z.object({ data: z.array(resourceSchema) }).strict(); diff --git a/lambdas/api-handler/src/contracts/letters.ts b/lambdas/api-handler/src/contracts/letters.ts index d777018a..5f671701 100644 --- a/lambdas/api-handler/src/contracts/letters.ts +++ b/lambdas/api-handler/src/contracts/letters.ts @@ -1,73 +1,105 @@ -import { z } from 'zod'; -import { makeCollectionSchema, makeDocumentSchema } from './json-api'; - -export type LetterDto = { - id: string, - status: LetterStatus, - supplierId: string, - specificationId?: string, - groupId?: string, - reasonCode?: number, - reasonText?: string -}; +import { z } from "zod"; +import { makeCollectionSchema, makeDocumentSchema } from "./json-api"; export const LetterStatusSchema = z.enum([ - 'PENDING', - 'ACCEPTED', - 'REJECTED', - 'PRINTED', - 'ENCLOSED', - 'CANCELLED', - 'DISPATCHED', - 'FAILED', - 'RETURNED', - 'DESTROYED', - 'FORWARDED', - 'DELIVERED' + "PENDING", + "ACCEPTED", + "REJECTED", + "PRINTED", + "ENCLOSED", + "CANCELLED", + "DISPATCHED", + "FAILED", + "RETURNED", + "FORWARDED", + "DELIVERED", ]); -export const PatchLetterRequestResourceSchema = z.object({ - id: z.string(), - type: z.literal('Letter'), - attributes: z.object({ - status: LetterStatusSchema, - reasonCode: z.number().optional(), - reasonText: z.string().optional(), - }).strict() -}).strict(); - -export const GetLetterResponseResourceSchema = z.object({ - id: z.string(), - type: z.literal('Letter'), - attributes: z.object({ +export const LetterDtoSchema = z + .object({ + id: z.string(), status: LetterStatusSchema, - specificationId: z.string(), + supplierId: z.string(), + specificationId: z.string().optional(), groupId: z.string().optional(), - reasonCode: z.number().optional(), + reasonCode: z.string().optional(), reasonText: z.string().optional(), - }).strict() -}).strict(); + }) + .strict(); -export const GetLettersResponseResourceSchema = z.object({ - id: z.string(), - type: z.literal('Letter'), - attributes: z.object({ - status: LetterStatusSchema, - specificationId: z.string(), - groupId: z.string().optional(), - }).strict() -}).strict(); +export type LetterDto = z.infer; -export const PatchLetterResponseResourceSchema = GetLetterResponseResourceSchema; +export const PatchLetterRequestResourceSchema = z + .object({ + id: z.string(), + type: z.literal("Letter"), + attributes: z + .object({ + status: LetterStatusSchema, + reasonCode: z.string().optional(), + reasonText: z.string().optional(), + }) + .strict(), + }) + .strict(); + +export const GetLetterResponseResourceSchema = z + .object({ + id: z.string(), + type: z.literal("Letter"), + attributes: z + .object({ + status: LetterStatusSchema, + specificationId: z.string(), + groupId: z.string().optional(), + reasonCode: z.string().optional(), + reasonText: z.string().optional(), + }) + .strict(), + }) + .strict(); + +export const GetLettersResponseResourceSchema = z + .object({ + id: z.string(), + type: z.literal("Letter"), + attributes: z + .object({ + status: LetterStatusSchema, + specificationId: z.string(), + groupId: z.string().optional(), + }) + .strict(), + }) + .strict(); + +export const PatchLetterResponseResourceSchema = + GetLetterResponseResourceSchema; export type LetterStatus = z.infer; -export const PatchLetterRequestSchema = makeDocumentSchema(PatchLetterRequestResourceSchema); -export const GetLetterResponseSchema = makeDocumentSchema(GetLetterResponseResourceSchema); -export const GetLettersResponseSchema = makeCollectionSchema(GetLettersResponseResourceSchema); -export const PatchLetterResponseSchema = makeDocumentSchema(PatchLetterResponseResourceSchema); +export const PatchLetterRequestSchema = makeDocumentSchema( + PatchLetterRequestResourceSchema, +); +export const PatchLetterResponseSchema = makeDocumentSchema( + PatchLetterResponseResourceSchema, +); +export const PostLettersRequestSchema = makeCollectionSchema( + PatchLetterRequestResourceSchema, +); +export const GetLetterResponseSchema = makeDocumentSchema( + GetLetterResponseResourceSchema, +); +export const GetLettersResponseSchema = makeCollectionSchema( + GetLettersResponseResourceSchema, +); + +export type PostLettersRequestResource = z.infer< + typeof PatchLetterRequestResourceSchema +>; export type PatchLetterRequest = z.infer; +export type PatchLetterResponse = z.infer; +export type PostLettersRequest = z.infer; export type GetLetterResponse = z.infer; export type GetLettersResponse = z.infer; -export type PatchLetterResponse = z.infer; diff --git a/lambdas/api-handler/src/contracts/mi.ts b/lambdas/api-handler/src/contracts/mi.ts index 9223a636..81a5d166 100644 --- a/lambdas/api-handler/src/contracts/mi.ts +++ b/lambdas/api-handler/src/contracts/mi.ts @@ -1,27 +1,39 @@ import z from "zod"; import { makeDocumentSchema } from "./json-api"; -export const PostMIRequestResourceSchema = z.object({ - type: z.literal('ManagementInformation'), - attributes: z.object({ - lineItem: z.string(), - timestamp: z.string(), - quantity: z.number(), - specificationId: z.string().optional(), - groupId: z.string().optional(), - stockRemaining: z.number().optional(), - }).strict() -}).strict(); +export const PostMIRequestResourceSchema = z + .object({ + type: z.literal("ManagementInformation"), + attributes: z + .object({ + lineItem: z.string(), + timestamp: z.string(), + quantity: z.number(), + specificationId: z.string().optional(), + groupId: z.string().optional(), + stockRemaining: z.number().optional(), + }) + .strict(), + }) + .strict(); -export const PostMIResponseResourceSchema = z.object({ - id: z.string(), - ...PostMIRequestResourceSchema.shape, -}).strict(); +export const PostMIResponseResourceSchema = z + .object({ + id: z.string(), + ...PostMIRequestResourceSchema.shape, + }) + .strict(); -export const PostMIRequestSchema = makeDocumentSchema(PostMIRequestResourceSchema); -export const PostMIResponseSchema = makeDocumentSchema(PostMIResponseResourceSchema); +export const PostMIRequestSchema = makeDocumentSchema( + PostMIRequestResourceSchema, +); +export const PostMIResponseSchema = makeDocumentSchema( + PostMIResponseResourceSchema, +); export type PostMIRequest = z.infer; export type PostMIResponse = z.infer; -export type IncomingMI = PostMIRequest['data']['attributes'] & {supplierId: string}; +export type IncomingMI = PostMIRequest["data"]["attributes"] & { + supplierId: string; +}; diff --git a/lambdas/api-handler/src/errors/api-error.ts b/lambdas/api-handler/src/errors/api-error.ts new file mode 100644 index 00000000..d602dce5 --- /dev/null +++ b/lambdas/api-handler/src/errors/api-error.ts @@ -0,0 +1,14 @@ +import { format } from "node:util"; + +export default class ApiError extends Error { + readonly detail: string; + + constructor( + detail: string, + opts: { args?: unknown[]; cause?: unknown } = {}, + ) { + const formatted = opts.args?.length ? format(detail, ...opts.args) : detail; + super(formatted, opts.cause ? { cause: opts.cause } : undefined); + this.detail = formatted; + } +} diff --git a/lambdas/api-handler/src/errors/index.ts b/lambdas/api-handler/src/errors/index.ts deleted file mode 100644 index eeeb179f..00000000 --- a/lambdas/api-handler/src/errors/index.ts +++ /dev/null @@ -1,15 +0,0 @@ -import util from "util"; - -class ApiError extends Error { - readonly detail: string; - - constructor(detail: string, opts: { args?: unknown[]; cause?: unknown } = {}) { - const formatted = opts.args?.length ? util.format(detail, ...(opts.args)) : detail; - super(formatted, opts.cause ? { cause: opts.cause } : undefined); - this.detail = formatted; - } -} - -export class NotFoundError extends ApiError {} - -export class ValidationError extends ApiError {} diff --git a/lambdas/api-handler/src/errors/not-found-error.ts b/lambdas/api-handler/src/errors/not-found-error.ts new file mode 100644 index 00000000..8e219aed --- /dev/null +++ b/lambdas/api-handler/src/errors/not-found-error.ts @@ -0,0 +1,3 @@ +import ApiError from "./api-error"; + +export default class NotFoundError extends ApiError {} diff --git a/lambdas/api-handler/src/errors/validation-error.ts b/lambdas/api-handler/src/errors/validation-error.ts new file mode 100644 index 00000000..de3cfe4a --- /dev/null +++ b/lambdas/api-handler/src/errors/validation-error.ts @@ -0,0 +1,3 @@ +import ApiError from "./api-error"; + +export default class ValidationError extends ApiError {} diff --git a/lambdas/api-handler/src/handlers/__tests__/get-letter-data.test.ts b/lambdas/api-handler/src/handlers/__tests__/get-letter-data.test.ts index 38c339ed..eb1aa056 100644 --- a/lambdas/api-handler/src/handlers/__tests__/get-letter-data.test.ts +++ b/lambdas/api-handler/src/handlers/__tests__/get-letter-data.test.ts @@ -1,61 +1,63 @@ // mock error mapper -jest.mock('../../mappers/error-mapper'); -import { mapErrorToResponse } from '../../mappers/error-mapper'; -const mockedMapErrorToResponse = jest.mocked(mapErrorToResponse); +import type { APIGatewayProxyResult, Context } from "aws-lambda"; +import { mockDeep } from "jest-mock-extended"; +import { S3Client } from "@aws-sdk/client-s3"; +import pino from "pino"; +import { LetterRepository } from "@internal/datastore/src"; +import { processError } from "../../mappers/error-mapper"; +import * as letterService from "../../services/letter-operations"; + +import { makeApiGwEvent } from "./utils/test-utils"; +import ValidationError from "../../errors/validation-error"; +import * as errors from "../../contracts/errors"; +import createGetLetterDataHandler from "../get-letter-data"; +import { EnvVars } from "../../config/env"; +import { Deps } from "../../config/deps"; + +jest.mock("../../mappers/error-mapper"); +const mockedProcessError = jest.mocked(processError); const expectedErrorResponse: APIGatewayProxyResult = { statusCode: 400, - body: 'Error' + body: "Error", }; -mockedMapErrorToResponse.mockReturnValue(expectedErrorResponse); +mockedProcessError.mockReturnValue(expectedErrorResponse); // mock letterService -jest.mock('../../services/letter-operations'); -import * as letterService from '../../services/letter-operations'; - -import type { APIGatewayProxyResult, Context } from 'aws-lambda'; -import { mockDeep } from 'jest-mock-extended'; -import { makeApiGwEvent } from './utils/test-utils'; -import { ValidationError } from '../../errors'; -import * as errors from '../../contracts/errors'; -import { createGetLetterDataHandler } from '../get-letter-data'; -import { S3Client } from '@aws-sdk/client-s3'; -import pino from 'pino'; -import { LetterRepository } from '@internal/datastore/src'; -import { EnvVars } from '../../config/env'; -import { Deps } from "../../config/deps"; - -describe('API Lambda handler', () => { +jest.mock("../../services/letter-operations"); +describe("API Lambda handler", () => { const mockedDeps: jest.Mocked = { s3Client: {} as unknown as S3Client, letterRepo: {} as unknown as LetterRepository, logger: { info: jest.fn(), error: jest.fn() } as unknown as pino.Logger, env: { - SUPPLIER_ID_HEADER: 'nhsd-supplier-id', - APIM_CORRELATION_HEADER: 'nhsd-correlation-id', - LETTERS_TABLE_NAME: 'LETTERS_TABLE_NAME', - LETTER_TTL_HOURS: 12960, - DOWNLOAD_URL_TTL_SECONDS: 60 - } as unknown as EnvVars + SUPPLIER_ID_HEADER: "nhsd-supplier-id", + APIM_CORRELATION_HEADER: "nhsd-correlation-id", + LETTERS_TABLE_NAME: "LETTERS_TABLE_NAME", + LETTER_TTL_HOURS: 12_960, + DOWNLOAD_URL_TTL_SECONDS: 60, + } as unknown as EnvVars, } as Deps; beforeEach(() => { jest.clearAllMocks(); }); - it('returns 303 Found with a pre signed url', async () => { - - const mockedGetLetterDataUrlService = letterService.getLetterDataUrl as jest.Mock; - mockedGetLetterDataUrlService.mockResolvedValue('https://somePreSignedUrl.com'); + it("returns 303 Found with a pre signed url", async () => { + const mockedGetLetterDataUrlService = + letterService.getLetterDataUrl as jest.Mock; + mockedGetLetterDataUrlService.mockResolvedValue( + "https://somePreSignedUrl.com", + ); const event = makeApiGwEvent({ - path: '/letters/letter1/data', + path: "/letters/letter1/data", headers: { - 'nhsd-supplier-id': 'supplier1', - 'nhsd-correlation-id': 'correlationId', - 'x-request-id': 'requestId' + "nhsd-supplier-id": "supplier1", + "nhsd-correlation-id": "correlationId", + "x-request-id": "requestId", }, - pathParameters: {id: 'id1'} + pathParameters: { id: "id1" }, }); const context = mockDeep(); const callback = jest.fn(); @@ -66,15 +68,17 @@ describe('API Lambda handler', () => { expect(result).toEqual({ statusCode: 303, headers: { - 'Location': 'https://somePreSignedUrl.com', + Location: "https://somePreSignedUrl.com", }, - body: '' + body: "", }); }); - it('returns error if headers are empty', async () => { - const event = makeApiGwEvent({ path: '/letters/letter1/data', headers: {}, - pathParameters: {id: 'id1'} + it("returns error if headers are empty", async () => { + const event = makeApiGwEvent({ + path: "/letters/letter1/data", + headers: {}, + pathParameters: { id: "id1" }, }); const context = mockDeep(); const callback = jest.fn(); @@ -82,19 +86,23 @@ describe('API Lambda handler', () => { const getLetterDataHandler = createGetLetterDataHandler(mockedDeps); const result = await getLetterDataHandler(event, context, callback); - expect(mockedMapErrorToResponse).toHaveBeenCalledWith(new Error('The request headers are empty'), undefined, mockedDeps.logger); + expect(mockedProcessError).toHaveBeenCalledWith( + new Error("The request headers are empty"), + undefined, + mockedDeps.logger, + ); expect(result).toEqual(expectedErrorResponse); }); - it('returns error if correlation id not provided in request', async () => { + it("returns error if correlation id not provided in request", async () => { const event = makeApiGwEvent({ - path: '/letters/letter1/data', - queryStringParameters: { limit: '2000' }, + path: "/letters/letter1/data", + queryStringParameters: { limit: "2000" }, headers: { - 'nhsd-supplier-id': 'supplier1', - 'x-request-id': 'requestId' + "nhsd-supplier-id": "supplier1", + "x-request-id": "requestId", }, - pathParameters: {id: 'id1'} + pathParameters: { id: "id1" }, }); const context = mockDeep(); const callback = jest.fn(); @@ -102,17 +110,21 @@ describe('API Lambda handler', () => { const getLetterDataHandler = createGetLetterDataHandler(mockedDeps); const result = await getLetterDataHandler(event, context, callback); - expect(mockedMapErrorToResponse).toHaveBeenCalledWith(new Error("The request headers don't contain the APIM correlation id"), undefined, mockedDeps.logger); + expect(mockedProcessError).toHaveBeenCalledWith( + new Error("The request headers don't contain the APIM correlation id"), + undefined, + mockedDeps.logger, + ); expect(result).toEqual(expectedErrorResponse); }); - it('returns error response when path parameter letterId is not found', async () => { + it("returns error response when path parameter letterId is not found", async () => { const event = makeApiGwEvent({ - path: '/letters/', + path: "/letters/", headers: { - 'nhsd-supplier-id': 'supplier1', - 'nhsd-correlation-id': 'correlationId', - 'x-request-id': 'requestId' + "nhsd-supplier-id": "supplier1", + "nhsd-correlation-id": "correlationId", + "x-request-id": "requestId", }, }); const context = mockDeep(); @@ -121,7 +133,13 @@ describe('API Lambda handler', () => { const getLetterDataHandler = createGetLetterDataHandler(mockedDeps); const result = await getLetterDataHandler(event, context, callback); - expect(mockedMapErrorToResponse).toHaveBeenCalledWith(new ValidationError(errors.ApiErrorDetail.InvalidRequestMissingLetterIdPathParameter), 'correlationId', mockedDeps.logger); + expect(mockedProcessError).toHaveBeenCalledWith( + new ValidationError( + errors.ApiErrorDetail.InvalidRequestMissingLetterIdPathParameter, + ), + "correlationId", + mockedDeps.logger, + ); expect(result).toEqual(expectedErrorResponse); }); }); diff --git a/lambdas/api-handler/src/handlers/__tests__/get-letter.test.ts b/lambdas/api-handler/src/handlers/__tests__/get-letter.test.ts index 742bf274..030fca48 100644 --- a/lambdas/api-handler/src/handlers/__tests__/get-letter.test.ts +++ b/lambdas/api-handler/src/handlers/__tests__/get-letter.test.ts @@ -1,33 +1,31 @@ -import { Context } from 'aws-lambda'; -import { mockDeep } from 'jest-mock-extended'; -import * as letterService from '../../services/letter-operations'; -import { makeApiGwEvent } from './utils/test-utils'; -import { ApiErrorDetail } from '../../contracts/errors'; -import { NotFoundError } from '../../errors'; -import { S3Client } from '@aws-sdk/client-s3'; -import pino from 'pino'; -import { LetterRepository } from '@internal/datastore/src'; -import { Deps } from '../../config/deps'; -import { EnvVars } from '../../config/env'; -import { createGetLetterHandler } from '../get-letter'; - -jest.mock('../../services/letter-operations'); - - -describe('API Lambda handler', () => { - +import { Context } from "aws-lambda"; +import { mockDeep } from "jest-mock-extended"; +import { S3Client } from "@aws-sdk/client-s3"; +import pino from "pino"; +import { LetterRepository } from "@internal/datastore/src"; +import * as letterService from "../../services/letter-operations"; +import { makeApiGwEvent } from "./utils/test-utils"; +import { ApiErrorDetail } from "../../contracts/errors"; +import NotFoundError from "../../errors/not-found-error"; +import { Deps } from "../../config/deps"; +import { EnvVars } from "../../config/env"; +import createGetLetterHandler from "../get-letter"; + +jest.mock("../../services/letter-operations"); + +describe("API Lambda handler", () => { const mockedDeps: jest.Mocked = { s3Client: {} as unknown as S3Client, letterRepo: {} as unknown as LetterRepository, logger: { info: jest.fn(), error: jest.fn() } as unknown as pino.Logger, env: { - SUPPLIER_ID_HEADER: 'nhsd-supplier-id', - APIM_CORRELATION_HEADER: 'nhsd-correlation-id', - LETTERS_TABLE_NAME: 'LETTERS_TABLE_NAME', - LETTER_TTL_HOURS: 12960, + SUPPLIER_ID_HEADER: "nhsd-supplier-id", + APIM_CORRELATION_HEADER: "nhsd-correlation-id", + LETTERS_TABLE_NAME: "LETTERS_TABLE_NAME", + LETTER_TTL_HOURS: 12_960, DOWNLOAD_URL_TTL_SECONDS: 60, - MAX_LIMIT: 2500 - } as unknown as EnvVars + MAX_LIMIT: 2500, + } as unknown as EnvVars, } as Deps; beforeEach(() => { @@ -35,33 +33,38 @@ describe('API Lambda handler', () => { jest.resetModules(); }); - it('returns 200 OK and the letter status', async () => { - + it("returns 200 OK and the letter status", async () => { const mockedGetLetterById = letterService.getLetterById as jest.Mock; mockedGetLetterById.mockResolvedValue({ - id: 'id1', - specificationId: 'spec1', - groupId: 'group1', - status: 'PENDING' + id: "id1", + specificationId: "spec1", + groupId: "group1", + status: "PENDING", }); - const event = makeApiGwEvent({path: '/letters/id1', - headers: {'nhsd-supplier-id': 'supplier1', 'nhsd-correlation-id': 'correlationId', 'x-request-id': 'requestId'}, - pathParameters: {id: 'id1'}}); + const event = makeApiGwEvent({ + path: "/letters/id1", + headers: { + "nhsd-supplier-id": "supplier1", + "nhsd-correlation-id": "correlationId", + "x-request-id": "requestId", + }, + pathParameters: { id: "id1" }, + }); const getLetter = createGetLetterHandler(mockedDeps); const result = await getLetter(event, mockDeep(), jest.fn()); const expected = { data: { - id: 'id1', - type: 'Letter', + id: "id1", + type: "Letter", attributes: { - status: 'PENDING', - specificationId: 'spec1', - groupId: 'group1' - } - } + status: "PENDING", + specificationId: "spec1", + groupId: "group1", + }, + }, }; expect(result).toEqual({ @@ -70,37 +73,42 @@ describe('API Lambda handler', () => { }); }); - it('includes the reason code and reason text if present', async () => { - + it("includes the reason code and reason text if present", async () => { const mockedGetLetterById = letterService.getLetterById as jest.Mock; mockedGetLetterById.mockResolvedValue({ - id: 'id1', - specificationId: 'spec1', - groupId: 'group1', - status: 'FAILED', - reasonCode: 100, - reasonText: 'failed validation' + id: "id1", + specificationId: "spec1", + groupId: "group1", + status: "FAILED", + reasonCode: "R01", + reasonText: "failed validation", }); - const event = makeApiGwEvent({path: '/letters/id1', - headers: {'nhsd-supplier-id': 'supplier1', 'nhsd-correlation-id': 'correlationId', 'x-request-id': 'requestId'}, - pathParameters: {id: 'id1'}}); + const event = makeApiGwEvent({ + path: "/letters/id1", + headers: { + "nhsd-supplier-id": "supplier1", + "nhsd-correlation-id": "correlationId", + "x-request-id": "requestId", + }, + pathParameters: { id: "id1" }, + }); const getLetter = createGetLetterHandler(mockedDeps); const result = await getLetter(event, mockDeep(), jest.fn()); const expected = { data: { - id: 'id1', - type: 'Letter', + id: "id1", + type: "Letter", attributes: { - status: 'FAILED', - specificationId: 'spec1', - groupId: 'group1', - reasonCode: 100, - reasonText: 'failed validation' - } - } + status: "FAILED", + specificationId: "spec1", + groupId: "group1", + reasonCode: "R01", + reasonText: "failed validation", + }, + }, }; expect(result).toEqual({ @@ -109,61 +117,86 @@ describe('API Lambda handler', () => { }); }); - it('returns 404 Not Found when letter matching id is not found', async () => { - - const mockedGetLetterById = letterService.getLetterById as jest.Mock; - mockedGetLetterById.mockImplementation(() => { - throw new NotFoundError(ApiErrorDetail.NotFoundLetterId); - }); + it("returns 404 Not Found when letter matching id is not found", async () => { + const mockedGetLetterById = letterService.getLetterById as jest.Mock; + mockedGetLetterById.mockImplementation(() => { + throw new NotFoundError(ApiErrorDetail.NotFoundLetterId); + }); - const event = makeApiGwEvent({path: '/letters/id1', - headers: {'nhsd-supplier-id': 'supplier1', 'nhsd-correlation-id': 'correlationId', 'x-request-id': 'requestId'}, - pathParameters: {id: 'id1'}}); + const event = makeApiGwEvent({ + path: "/letters/id1", + headers: { + "nhsd-supplier-id": "supplier1", + "nhsd-correlation-id": "correlationId", + "x-request-id": "requestId", + }, + pathParameters: { id: "id1" }, + }); - const getLetter = createGetLetterHandler(mockedDeps); - const result = await getLetter(event, mockDeep(), jest.fn()); + const getLetter = createGetLetterHandler(mockedDeps); + const result = await getLetter(event, mockDeep(), jest.fn()); - expect(result).toEqual(expect.objectContaining({ + expect(result).toEqual( + expect.objectContaining({ statusCode: 404, - })); + }), + ); }); - it ('returns 500 when correlation id is missing from header', async() => { - const event = makeApiGwEvent({path: '/letters/id1', - headers: {'nhsd-supplier-id': 'supplier1', 'x-request-id': 'requestId'}, - pathParameters: {id: 'id1'}}); + it("returns 500 when correlation id is missing from header", async () => { + const event = makeApiGwEvent({ + path: "/letters/id1", + headers: { "nhsd-supplier-id": "supplier1", "x-request-id": "requestId" }, + pathParameters: { id: "id1" }, + }); - const getLetter = createGetLetterHandler(mockedDeps); - const result = await getLetter(event, mockDeep(), jest.fn()); + const getLetter = createGetLetterHandler(mockedDeps); + const result = await getLetter(event, mockDeep(), jest.fn()); - expect(result).toEqual(expect.objectContaining({ + expect(result).toEqual( + expect.objectContaining({ statusCode: 500, - })); + }), + ); }); - it ('returns 500 when supplier id is missing from header', async() => { - const event = makeApiGwEvent({path: '/letters/id1', - headers: {'nhsd-correlation-id': 'correlationId', 'x-request-id': 'requestId'}, - pathParameters: {id: 'id1'}}); + it("returns 500 when supplier id is missing from header", async () => { + const event = makeApiGwEvent({ + path: "/letters/id1", + headers: { + "nhsd-correlation-id": "correlationId", + "x-request-id": "requestId", + }, + pathParameters: { id: "id1" }, + }); - const getLetter = createGetLetterHandler(mockedDeps); - const result = await getLetter(event, mockDeep(), jest.fn()); + const getLetter = createGetLetterHandler(mockedDeps); + const result = await getLetter(event, mockDeep(), jest.fn()); - expect(result).toEqual(expect.objectContaining({ + expect(result).toEqual( + expect.objectContaining({ statusCode: 500, - })); + }), + ); }); + it("returns 400 when letter id is missing from path", async () => { + const event = makeApiGwEvent({ + path: "/letters/id1", + headers: { + "nhsd-supplier-id": "supplier1", + "nhsd-correlation-id": "correlationId", + "x-request-id": "requestId", + }, + }); - it ('returns 400 when letter id is missing from path', async() => { - const event = makeApiGwEvent({path: '/letters/id1', - headers: {'nhsd-supplier-id': 'supplier1', 'nhsd-correlation-id': 'correlationId', 'x-request-id': 'requestId'}}); - - const getLetter = createGetLetterHandler(mockedDeps); - const result = await getLetter(event, mockDeep(), jest.fn()); + const getLetter = createGetLetterHandler(mockedDeps); + const result = await getLetter(event, mockDeep(), jest.fn()); - expect(result).toEqual(expect.objectContaining({ + expect(result).toEqual( + expect.objectContaining({ statusCode: 400, - })); + }), + ); }); }); diff --git a/lambdas/api-handler/src/handlers/__tests__/get-letters.test.ts b/lambdas/api-handler/src/handlers/__tests__/get-letters.test.ts index 292538da..6723084a 100644 --- a/lambdas/api-handler/src/handlers/__tests__/get-letters.test.ts +++ b/lambdas/api-handler/src/handlers/__tests__/get-letters.test.ts @@ -1,82 +1,81 @@ // mock error mapper -jest.mock('../../mappers/error-mapper'); -import { mapErrorToResponse } from '../../mappers/error-mapper'; -const mockedMapErrorToResponse = jest.mocked(mapErrorToResponse); +import type { APIGatewayProxyResult, Context } from "aws-lambda"; +import { mockDeep } from "jest-mock-extended"; +import { S3Client } from "@aws-sdk/client-s3"; +import pino from "pino"; +import { LetterRepository } from "@internal/datastore/src"; +import { processError } from "../../mappers/error-mapper"; +import * as letterService from "../../services/letter-operations"; + +import { makeApiGwEvent } from "./utils/test-utils"; +import ValidationError from "../../errors/validation-error"; +import * as errors from "../../contracts/errors"; +import createGetLettersHandler from "../get-letters"; +import { Deps } from "../../config/deps"; +import { EnvVars } from "../../config/env"; + +jest.mock("../../mappers/error-mapper"); +const mockedProcessError = jest.mocked(processError); const expectedErrorResponse: APIGatewayProxyResult = { statusCode: 400, - body: 'Error' + body: "Error", }; -mockedMapErrorToResponse.mockReturnValue(expectedErrorResponse); - -//mock letter service -jest.mock('../../services/letter-operations'); -import * as letterService from '../../services/letter-operations'; - -import type { APIGatewayProxyResult, Context } from 'aws-lambda'; -import { mockDeep } from 'jest-mock-extended'; -import { makeApiGwEvent } from './utils/test-utils'; -import { ValidationError } from '../../errors'; -import * as errors from '../../contracts/errors'; -import { S3Client } from '@aws-sdk/client-s3'; -import pino from 'pino'; -import { LetterRepository } from '@internal/datastore/src'; -import { createGetLettersHandler } from '../get-letters'; -import { Deps } from '../../config/deps'; -import { EnvVars } from '../../config/env'; - -describe('API Lambda handler', () => { +mockedProcessError.mockReturnValue(expectedErrorResponse); +// mock letter service +jest.mock("../../services/letter-operations"); + +describe("API Lambda handler", () => { const mockedDeps: jest.Mocked = { s3Client: {} as unknown as S3Client, letterRepo: {} as unknown as LetterRepository, logger: { info: jest.fn(), error: jest.fn() } as unknown as pino.Logger, env: { - SUPPLIER_ID_HEADER: 'nhsd-supplier-id', - APIM_CORRELATION_HEADER: 'nhsd-correlation-id', - LETTERS_TABLE_NAME: 'LETTERS_TABLE_NAME', - LETTER_TTL_HOURS: 12960, + SUPPLIER_ID_HEADER: "nhsd-supplier-id", + APIM_CORRELATION_HEADER: "nhsd-correlation-id", + LETTERS_TABLE_NAME: "LETTERS_TABLE_NAME", + LETTER_TTL_HOURS: 12_960, DOWNLOAD_URL_TTL_SECONDS: 60, - MAX_LIMIT: 2500 - } as unknown as EnvVars + MAX_LIMIT: 2500, + } as unknown as EnvVars, } as Deps; beforeEach(() => { jest.clearAllMocks(); }); - it('returns 200 OK with basic paginated resources', async () => { - + it("returns 200 OK with basic paginated resources", async () => { const mockedGetLetters = letterService.getLettersForSupplier as jest.Mock; mockedGetLetters.mockResolvedValue([ { - id: 'l1', - specificationId: 's1', - groupId: 'g1', - status: 'PENDING' + id: "l1", + specificationId: "s1", + groupId: "g1", + status: "PENDING", }, { - id: 'l2', - specificationId: 's1', - groupId: 'g1', - status: 'PENDING', + id: "l2", + specificationId: "s1", + groupId: "g1", + status: "PENDING", }, { - id: 'l3', - specificationId: 's1', - groupId: 'g1', - status: 'PENDING', - reasonCode: 123, // shouldn't be returned if present - reasonText: 'Reason text' // shouldn't be returned if present + id: "l3", + specificationId: "s1", + groupId: "g1", + status: "PENDING", + reasonCode: "R02", // shouldn't be returned if present + reasonText: "Reason text", // shouldn't be returned if present }, ]); const event = makeApiGwEvent({ - path: '/letters', + path: "/letters", headers: { - 'nhsd-supplier-id': 'supplier1', - 'nhsd-correlation-id': 'correlationId', - 'x-request-id': 'requestId' - } + "nhsd-supplier-id": "supplier1", + "nhsd-correlation-id": "correlationId", + "x-request-id": "requestId", + }, }); const context = mockDeep(); const callback = jest.fn(); @@ -84,25 +83,42 @@ describe('API Lambda handler', () => { const getLettersHandler = createGetLettersHandler(mockedDeps); const result = await getLettersHandler(event, context, callback); - expect(mockedGetLetters).toHaveBeenCalledWith('supplier1', 'PENDING', mockedDeps.env.MAX_LIMIT, mockedDeps.letterRepo); + expect(mockedGetLetters).toHaveBeenCalledWith( + "supplier1", + "PENDING", + mockedDeps.env.MAX_LIMIT, + mockedDeps.letterRepo, + ); const expected = { data: [ { - id: 'l1', - type: 'Letter', - attributes: { status: 'PENDING', specificationId: 's1', groupId: 'g1' }, + id: "l1", + type: "Letter", + attributes: { + status: "PENDING", + specificationId: "s1", + groupId: "g1", + }, }, { - id: 'l2', - type: 'Letter', - attributes: { status: 'PENDING', specificationId: 's1', groupId: 'g1' }, + id: "l2", + type: "Letter", + attributes: { + status: "PENDING", + specificationId: "s1", + groupId: "g1", + }, }, { - id: 'l3', - type: 'Letter', - attributes: { status: 'PENDING', specificationId: 's1', groupId: 'g1' } - } + id: "l3", + type: "Letter", + attributes: { + status: "PENDING", + specificationId: "s1", + groupId: "g1", + }, + }, ], }; @@ -112,26 +128,25 @@ describe('API Lambda handler', () => { }); }); - it('returns 200 OK with a valid limit', async () => { - + it("returns 200 OK with a valid limit", async () => { const mockedGetLetters = letterService.getLettersForSupplier as jest.Mock; mockedGetLetters.mockResolvedValue([ { - id: 'l1', - specificationId: 's1', - groupId: 'g1', - status: 'PENDING' + id: "l1", + specificationId: "s1", + groupId: "g1", + status: "PENDING", }, ]); const event = makeApiGwEvent({ - path: '/letters', - queryStringParameters: { limit: '50' }, + path: "/letters", + queryStringParameters: { limit: "50" }, headers: { - 'nhsd-supplier-id': 'supplier1', - 'nhsd-correlation-id': 'correlationId', - 'x-request-id': 'requestId' - } + "nhsd-supplier-id": "supplier1", + "nhsd-correlation-id": "correlationId", + "x-request-id": "requestId", + }, }); const context = mockDeep(); const callback = jest.fn(); @@ -139,14 +154,23 @@ describe('API Lambda handler', () => { const getLettersHandler = createGetLettersHandler(mockedDeps); const result = await getLettersHandler(event, context, callback); - expect(mockedGetLetters).toHaveBeenCalledWith('supplier1', 'PENDING', 50, mockedDeps.letterRepo); + expect(mockedGetLetters).toHaveBeenCalledWith( + "supplier1", + "PENDING", + 50, + mockedDeps.letterRepo, + ); const expected = { data: [ { - id: 'l1', - type: 'Letter', - attributes: { status: 'PENDING', specificationId: 's1', groupId: 'g1' }, + id: "l1", + type: "Letter", + attributes: { + status: "PENDING", + specificationId: "s1", + groupId: "g1", + }, }, ], }; @@ -157,16 +181,15 @@ describe('API Lambda handler', () => { }); }); - it('returns error if the limit parameter is not a number', async () => { - + it("returns error if the limit parameter is not a number", async () => { const event = makeApiGwEvent({ - path: '/letters', - queryStringParameters: { limit: '1%' }, + path: "/letters", + queryStringParameters: { limit: "1%" }, headers: { - 'nhsd-supplier-id': 'supplier1', - 'nhsd-correlation-id': 'correlationId', - 'x-request-id': 'requestId' - } + "nhsd-supplier-id": "supplier1", + "nhsd-correlation-id": "correlationId", + "x-request-id": "requestId", + }, }); const context = mockDeep(); const callback = jest.fn(); @@ -174,20 +197,23 @@ describe('API Lambda handler', () => { const getLettersHandler = createGetLettersHandler(mockedDeps); const result = await getLettersHandler(event, context, callback); - - expect(mockedMapErrorToResponse).toHaveBeenCalledWith(new ValidationError(errors.ApiErrorDetail.InvalidRequestLimitNotANumber), 'correlationId', mockedDeps.logger); + expect(mockedProcessError).toHaveBeenCalledWith( + new ValidationError(errors.ApiErrorDetail.InvalidRequestLimitNotANumber), + "correlationId", + mockedDeps.logger, + ); expect(result).toEqual(expectedErrorResponse); }); - it('returns error if the limit parameter is negative', async () => { + it("returns error if the limit parameter is negative", async () => { const event = makeApiGwEvent({ - path: '/letters', - queryStringParameters: { limit: '-1' }, + path: "/letters", + queryStringParameters: { limit: "-1" }, headers: { - 'nhsd-supplier-id': 'supplier1', - 'nhsd-correlation-id': 'correlationId', - 'x-request-id': 'requestId' - } + "nhsd-supplier-id": "supplier1", + "nhsd-correlation-id": "correlationId", + "x-request-id": "requestId", + }, }); const context = mockDeep(); const callback = jest.fn(); @@ -195,20 +221,25 @@ describe('API Lambda handler', () => { const getLettersHandler = createGetLettersHandler(mockedDeps); const result = await getLettersHandler(event, context, callback); - expect(mockedMapErrorToResponse).toHaveBeenCalledWith( - new ValidationError(errors.ApiErrorDetail.InvalidRequestLimitNotInRange, { args: [mockedDeps.env.MAX_LIMIT] }), 'correlationId', mockedDeps.logger); + expect(mockedProcessError).toHaveBeenCalledWith( + new ValidationError(errors.ApiErrorDetail.InvalidRequestLimitNotInRange, { + args: [mockedDeps.env.MAX_LIMIT], + }), + "correlationId", + mockedDeps.logger, + ); expect(result).toEqual(expectedErrorResponse); }); - it('returns error if the limit parameter is zero', async () => { + it("returns error if the limit parameter is zero", async () => { const event = makeApiGwEvent({ - path: '/letters', - queryStringParameters: { limit: '0' }, + path: "/letters", + queryStringParameters: { limit: "0" }, headers: { - 'nhsd-supplier-id': 'supplier1', - 'nhsd-correlation-id': 'correlationId', - 'x-request-id': 'requestId' - } + "nhsd-supplier-id": "supplier1", + "nhsd-correlation-id": "correlationId", + "x-request-id": "requestId", + }, }); const context = mockDeep(); const callback = jest.fn(); @@ -216,20 +247,25 @@ describe('API Lambda handler', () => { const getLettersHandler = createGetLettersHandler(mockedDeps); const result = await getLettersHandler(event, context, callback); - expect(mockedMapErrorToResponse).toHaveBeenCalledWith( - new ValidationError(errors.ApiErrorDetail.InvalidRequestLimitNotInRange, { args: [mockedDeps.env.MAX_LIMIT] }), 'correlationId', mockedDeps.logger); + expect(mockedProcessError).toHaveBeenCalledWith( + new ValidationError(errors.ApiErrorDetail.InvalidRequestLimitNotInRange, { + args: [mockedDeps.env.MAX_LIMIT], + }), + "correlationId", + mockedDeps.logger, + ); expect(result).toEqual(expectedErrorResponse); }); - it('returns error if the limit parameter is higher than max limit', async () => { + it("returns error if the limit parameter is higher than max limit", async () => { const event = makeApiGwEvent({ - path: '/letters', - queryStringParameters: { limit: '2501' }, + path: "/letters", + queryStringParameters: { limit: "2501" }, headers: { - 'nhsd-supplier-id': 'supplier1', - 'nhsd-correlation-id': 'correlationId', - 'x-request-id': 'requestId' - } + "nhsd-supplier-id": "supplier1", + "nhsd-correlation-id": "correlationId", + "x-request-id": "requestId", + }, }); const context = mockDeep(); const callback = jest.fn(); @@ -237,20 +273,25 @@ describe('API Lambda handler', () => { const getLettersHandler = createGetLettersHandler(mockedDeps); const result = await getLettersHandler(event, context, callback); - expect(mockedMapErrorToResponse).toHaveBeenCalledWith( - new ValidationError(errors.ApiErrorDetail.InvalidRequestLimitNotInRange, { args: [mockedDeps.env.MAX_LIMIT] }), 'correlationId', mockedDeps.logger); + expect(mockedProcessError).toHaveBeenCalledWith( + new ValidationError(errors.ApiErrorDetail.InvalidRequestLimitNotInRange, { + args: [mockedDeps.env.MAX_LIMIT], + }), + "correlationId", + mockedDeps.logger, + ); expect(result).toEqual(expectedErrorResponse); }); - it('returns error if unknown parameters are present', async () => { + it("returns error if unknown parameters are present", async () => { const event = makeApiGwEvent({ - path: '/letters', - queryStringParameters: { max: '2000' }, + path: "/letters", + queryStringParameters: { max: "2000" }, headers: { - 'nhsd-supplier-id': 'supplier1', - 'nhsd-correlation-id': 'correlationId', - 'x-request-id': 'requestId' - } + "nhsd-supplier-id": "supplier1", + "nhsd-correlation-id": "correlationId", + "x-request-id": "requestId", + }, }); const context = mockDeep(); const callback = jest.fn(); @@ -258,30 +299,38 @@ describe('API Lambda handler', () => { const getLettersHandler = createGetLettersHandler(mockedDeps); const result = await getLettersHandler(event, context, callback); - expect(mockedMapErrorToResponse).toHaveBeenCalledWith(new ValidationError(errors.ApiErrorDetail.InvalidRequestLimitOnly), 'correlationId', mockedDeps.logger); + expect(mockedProcessError).toHaveBeenCalledWith( + new ValidationError(errors.ApiErrorDetail.InvalidRequestLimitOnly), + "correlationId", + mockedDeps.logger, + ); expect(result).toEqual(expectedErrorResponse); }); - it('returns error if headers are empty', async () => { - const event = makeApiGwEvent({ path: '/letters', headers: {} }); + it("returns error if headers are empty", async () => { + const event = makeApiGwEvent({ path: "/letters", headers: {} }); const context = mockDeep(); const callback = jest.fn(); const getLettersHandler = createGetLettersHandler(mockedDeps); const result = await getLettersHandler(event, context, callback); - expect(mockedMapErrorToResponse).toHaveBeenCalledWith(new Error('The request headers are empty'), undefined, mockedDeps.logger); + expect(mockedProcessError).toHaveBeenCalledWith( + new Error("The request headers are empty"), + undefined, + mockedDeps.logger, + ); expect(result).toEqual(expectedErrorResponse); }); - it('returns error if correlation id not provided in request', async () => { + it("returns error if correlation id not provided in request", async () => { const event = makeApiGwEvent({ - path: '/letters', - queryStringParameters: { limit: '2000' }, + path: "/letters", + queryStringParameters: { limit: "2000" }, headers: { - 'nhsd-supplier-id': 'supplier1', - 'x-request-id': 'requestId' - } + "nhsd-supplier-id": "supplier1", + "x-request-id": "requestId", + }, }); const context = mockDeep(); const callback = jest.fn(); @@ -289,17 +338,22 @@ describe('API Lambda handler', () => { const getLettersHandler = createGetLettersHandler(mockedDeps); const result = await getLettersHandler(event, context, callback); - expect(mockedMapErrorToResponse).toHaveBeenCalledWith(new Error("The request headers don't contain the APIM correlation id"), undefined, mockedDeps.logger); + expect(mockedProcessError).toHaveBeenCalledWith( + new Error("The request headers don't contain the APIM correlation id"), + undefined, + mockedDeps.logger, + ); expect(result).toEqual(expectedErrorResponse); }); - it('returns error if max limit is not set', async () => { - const event = makeApiGwEvent({path: '/letters', + it("returns error if max limit is not set", async () => { + const event = makeApiGwEvent({ + path: "/letters", headers: { - 'nhsd-supplier-id': 'supplier1', - 'nhsd-correlation-id': 'correlationId', - 'x-request-id': 'requestId' - } + "nhsd-supplier-id": "supplier1", + "nhsd-correlation-id": "correlationId", + "x-request-id": "requestId", + }, }); const context = mockDeep(); const callback = jest.fn(); @@ -313,7 +367,11 @@ describe('API Lambda handler', () => { const getLettersHandler = createGetLettersHandler(mockedDepsNoMaxLimit); const result = await getLettersHandler(event, context, callback); - expect(mockedMapErrorToResponse).toHaveBeenCalledWith(new Error('MAX_LIMIT is required for getLetters'), 'correlationId', mockedDepsNoMaxLimit.logger); + expect(mockedProcessError).toHaveBeenCalledWith( + new Error("Missing required environment variable: MAX_LIMIT"), + "correlationId", + mockedDepsNoMaxLimit.logger, + ); expect(result).toEqual(expectedErrorResponse); }); }); diff --git a/lambdas/api-handler/src/handlers/__tests__/get-status.test.ts b/lambdas/api-handler/src/handlers/__tests__/get-status.test.ts new file mode 100644 index 00000000..6f797098 --- /dev/null +++ b/lambdas/api-handler/src/handlers/__tests__/get-status.test.ts @@ -0,0 +1,83 @@ +import { S3Client } from "@aws-sdk/client-s3"; +import { DBHealthcheck } from "@internal/datastore/src"; +import pino from "pino"; +import { mockDeep } from "jest-mock-extended"; +import { Context } from "aws-lambda"; +import { Deps } from "../../config/deps"; +import { makeApiGwEvent } from "./utils/test-utils"; +import createGetStatusHandler from "../get-status"; + +const getMockedDeps = (): jest.Mocked => { + return { + s3Client: { send: jest.fn() } as unknown as S3Client, + dbHealthcheck: { check: jest.fn() } as unknown as DBHealthcheck, + logger: { info: jest.fn(), error: jest.fn() } as unknown as pino.Logger, + env: { + SUPPLIER_ID_HEADER: "nhsd-supplier-id", + APIM_CORRELATION_HEADER: "nhsd-correlation-id", + }, + } as Deps; +}; + +describe("API Lambda handler", () => { + it("passes if S3 and DynamoDB are available", async () => { + const event = makeApiGwEvent({ path: "/_status", headers: undefined }); + + const getLetterDataHandler = createGetStatusHandler(getMockedDeps()); + const result = await getLetterDataHandler( + event, + mockDeep(), + jest.fn(), + ); + + expect(result).toEqual({ + statusCode: 200, + body: JSON.stringify({ code: 200 }, null, 2), + }); + }); + + it("fails if S3 is unavailable", async () => { + const mockedDeps = getMockedDeps(); + mockedDeps.s3Client.send = jest + .fn() + .mockRejectedValue(new Error("unexpected error")); + + const event = makeApiGwEvent({ path: "/_status", headers: undefined }); + + const getLetterDataHandler = createGetStatusHandler(mockedDeps); + const result = await getLetterDataHandler( + event, + mockDeep(), + jest.fn(), + ); + + expect(result).toEqual({ + statusCode: 500, + body: JSON.stringify({ code: 500 }, null, 2), + }); + }); + + it("fails if DynamoDB is unavailable", async () => { + const mockedDeps = getMockedDeps(); + mockedDeps.dbHealthcheck.check = jest + .fn() + .mockRejectedValue(new Error("unexpected error")); + + const event = makeApiGwEvent({ + path: "/_status", + headers: { "Nhsd-Correlation-Id": "correlationId" }, + }); + + const getLetterDataHandler = createGetStatusHandler(mockedDeps); + const result = await getLetterDataHandler( + event, + mockDeep(), + jest.fn(), + ); + + expect(result).toEqual({ + statusCode: 500, + body: JSON.stringify({ code: 500 }, null, 2), + }); + }); +}); diff --git a/lambdas/api-handler/src/handlers/__tests__/letter-status-update.test.ts b/lambdas/api-handler/src/handlers/__tests__/letter-status-update.test.ts new file mode 100644 index 00000000..4f249f95 --- /dev/null +++ b/lambdas/api-handler/src/handlers/__tests__/letter-status-update.test.ts @@ -0,0 +1,158 @@ +import { Context, SQSEvent, SQSRecord } from "aws-lambda"; +import { mockDeep } from "jest-mock-extended"; +import { S3Client } from "@aws-sdk/client-s3"; +import pino from "pino"; +import { LetterRepository } from "@internal/datastore/src"; +import { LetterDto } from "../../contracts/letters"; +import { EnvVars } from "../../config/env"; +import { Deps } from "../../config/deps"; +import createLetterStatusUpdateHandler from "../letter-status-update"; + +const buildEvent = (lettersToUpdate: LetterDto[]): SQSEvent => { + const records: Partial[] = lettersToUpdate.map((letter) => { + return { + messageId: `mid-${letter.id}`, + body: JSON.stringify(letter), + messageAttributes: { + CorrelationId: { + dataType: "String", + stringValue: `correlationId-${letter.id}`, + }, + }, + }; + }); + + const event: Partial = { + Records: records as SQSRecord[], + }; + + return event as SQSEvent; +}; + +describe("createLetterStatusUpdateHandler", () => { + beforeEach(() => { + jest.clearAllMocks(); + }); + + it("processes letters successfully", async () => { + const lettersToUpdate: LetterDto[] = [ + { + id: "id1", + status: "REJECTED", + supplierId: "s1", + specificationId: "spec1", + groupId: "g1", + reasonCode: "123", + reasonText: "Reason text", + }, + { + id: "id2", + supplierId: "s2", + status: "ACCEPTED", + }, + { + id: "id3", + supplierId: "s3", + status: "DELIVERED", + }, + ]; + + const mockedDeps: jest.Mocked = { + s3Client: {} as unknown as S3Client, + letterRepo: { + updateLetterStatus: jest + .fn() + .mockResolvedValueOnce(lettersToUpdate[0]) + .mockResolvedValueOnce(lettersToUpdate[1]) + .mockResolvedValueOnce(lettersToUpdate[2]), + } as unknown as LetterRepository, + logger: { info: jest.fn(), error: jest.fn() } as unknown as pino.Logger, + env: { + SUPPLIER_ID_HEADER: "nhsd-supplier-id", + APIM_CORRELATION_HEADER: "nhsd-correlation-id", + LETTERS_TABLE_NAME: "LETTERS_TABLE_NAME", + LETTER_TTL_HOURS: 12_960, + DOWNLOAD_URL_TTL_SECONDS: 60, + MAX_LIMIT: 2500, + QUEUE_URL: "SQS_URL", + } as unknown as EnvVars, + } as Deps; + + const context = mockDeep(); + const callback = jest.fn(); + + const letterStatusUpdateHandler = + createLetterStatusUpdateHandler(mockedDeps); + await letterStatusUpdateHandler( + buildEvent(lettersToUpdate), + context, + callback, + ); + + expect(mockedDeps.letterRepo.updateLetterStatus).toHaveBeenNthCalledWith( + 1, + lettersToUpdate[0], + ); + expect(mockedDeps.letterRepo.updateLetterStatus).toHaveBeenNthCalledWith( + 2, + lettersToUpdate[1], + ); + expect(mockedDeps.letterRepo.updateLetterStatus).toHaveBeenNthCalledWith( + 3, + lettersToUpdate[2], + ); + }); + + it("logs error if error thrown when updating", async () => { + const mockError = new Error("Update error"); + + const mockedDeps: jest.Mocked = { + s3Client: {} as unknown as S3Client, + letterRepo: { + updateLetterStatus: jest.fn().mockRejectedValue(mockError), + } as unknown as LetterRepository, + logger: { info: jest.fn(), error: jest.fn() } as unknown as pino.Logger, + env: { + SUPPLIER_ID_HEADER: "nhsd-supplier-id", + APIM_CORRELATION_HEADER: "nhsd-correlation-id", + LETTERS_TABLE_NAME: "LETTERS_TABLE_NAME", + LETTER_TTL_HOURS: 12_960, + DOWNLOAD_URL_TTL_SECONDS: 60, + MAX_LIMIT: 2500, + QUEUE_URL: "SQS_URL", + } as unknown as EnvVars, + } as Deps; + + const context = mockDeep(); + const callback = jest.fn(); + + const letterToUpdate: LetterDto[] = [ + { + id: "id1", + status: "ACCEPTED", + supplierId: "s1", + }, + ]; + + const letterStatusUpdateHandler = + createLetterStatusUpdateHandler(mockedDeps); + await letterStatusUpdateHandler( + buildEvent(letterToUpdate), + context, + callback, + ); + + expect(mockedDeps.letterRepo.updateLetterStatus).toHaveBeenCalledWith( + letterToUpdate[0], + ); + expect(mockedDeps.logger.error).toHaveBeenCalledWith( + { + err: mockError, + messageId: "mid-id1", + correlationId: "correlationId-id1", + messageBody: '{"id":"id1","status":"ACCEPTED","supplierId":"s1"}', + }, + "Error processing letter status update", + ); + }); +}); diff --git a/lambdas/api-handler/src/handlers/__tests__/patch-letter.test.ts b/lambdas/api-handler/src/handlers/__tests__/patch-letter.test.ts index 72737e4b..252eeae3 100644 --- a/lambdas/api-handler/src/handlers/__tests__/patch-letter.test.ts +++ b/lambdas/api-handler/src/handlers/__tests__/patch-letter.test.ts @@ -1,111 +1,100 @@ // mock service -jest.mock('../../services/letter-operations'); -import * as letterService from '../../services/letter-operations'; -const mockedPatchLetterStatus = jest.mocked(letterService.patchLetterStatus); +import { APIGatewayProxyResult, Context } from "aws-lambda"; +import { mockDeep } from "jest-mock-extended"; +import { S3Client } from "@aws-sdk/client-s3"; +import pino from "pino"; +import { LetterRepository } from "@internal/datastore/src"; +import * as letterService from "../../services/letter-operations"; +import { processError } from "../../mappers/error-mapper"; + +import { makeApiGwEvent } from "./utils/test-utils"; +import { PatchLetterRequest } from "../../contracts/letters"; +import ValidationError from "../../errors/validation-error"; +import * as errors from "../../contracts/errors"; +import { EnvVars } from "../../config/env"; +import createPatchLetterHandler from "../patch-letter"; +import { Deps } from "../../config/deps"; + +jest.mock("../../services/letter-operations"); +const mockedBatchUpdateStatus = jest.mocked( + letterService.enqueueLetterUpdateRequests, +); // mock mapper -jest.mock('../../mappers/error-mapper'); -import { mapErrorToResponse } from '../../mappers/error-mapper'; -const mockedMapErrorToResponse = jest.mocked(mapErrorToResponse); +jest.mock("../../mappers/error-mapper"); +const mockedProcessError = jest.mocked(processError); const expectedErrorResponse: APIGatewayProxyResult = { statusCode: 400, - body: 'Error' + body: "Error", }; -mockedMapErrorToResponse.mockReturnValue(expectedErrorResponse); - -import { APIGatewayProxyResult, Context } from 'aws-lambda'; -import { mockDeep } from 'jest-mock-extended'; -import { makeApiGwEvent } from './utils/test-utils'; -import { PatchLetterRequest, PatchLetterResponse } from '../../contracts/letters'; -import { ValidationError } from '../../errors'; -import * as errors from '../../contracts/errors'; -import { S3Client } from '@aws-sdk/client-s3'; -import pino from 'pino'; -import { LetterRepository } from '@internal/datastore/src'; -import { EnvVars } from '../../config/env'; -import { createPatchLetterHandler } from '../patch-letter'; -import { Deps } from "../../config/deps"; - -const updateLetterStatusRequest : PatchLetterRequest = { - data: { - id: 'id1', - type: 'Letter', - attributes: { - status: 'REJECTED', - reasonCode: 123, - reasonText: 'Reason text', - } - } +mockedProcessError.mockReturnValue(expectedErrorResponse); + +const updateLetterStatusRequest: PatchLetterRequest = { + data: { + id: "id1", + type: "Letter", + attributes: { + status: "REJECTED", + reasonCode: "R01", + reasonText: "Reason text", + }, + }, }; const requestBody = JSON.stringify(updateLetterStatusRequest, null, 2); -describe('patchLetter API Handler', () => { - +describe("patchLetter API Handler", () => { beforeEach(() => { jest.clearAllMocks(); }); const mockedDeps: jest.Mocked = { - s3Client: {} as unknown as S3Client, - letterRepo: {} as unknown as LetterRepository, - logger: { info: jest.fn(), error: jest.fn() } as unknown as pino.Logger, - env: { - SUPPLIER_ID_HEADER: 'nhsd-supplier-id', - APIM_CORRELATION_HEADER: 'nhsd-correlation-id', - LETTERS_TABLE_NAME: 'LETTERS_TABLE_NAME', - LETTER_TTL_HOURS: 12960, - DOWNLOAD_URL_TTL_SECONDS: 60 - } as unknown as EnvVars - } as Deps; - - it('returns 200 OK with updated resource', async () => { + s3Client: {} as unknown as S3Client, + letterRepo: {} as unknown as LetterRepository, + logger: { info: jest.fn(), error: jest.fn() } as unknown as pino.Logger, + env: { + SUPPLIER_ID_HEADER: "nhsd-supplier-id", + APIM_CORRELATION_HEADER: "nhsd-correlation-id", + LETTERS_TABLE_NAME: "LETTERS_TABLE_NAME", + LETTER_TTL_HOURS: 12_960, + DOWNLOAD_URL_TTL_SECONDS: 60, + } as unknown as EnvVars, + } as Deps; + + it("returns 202 Accepted", async () => { const event = makeApiGwEvent({ - path: '/letters/id1', + path: "/letters/id1", body: requestBody, - pathParameters: {id: 'id1'}, + pathParameters: { id: "id1" }, headers: { - 'nhsd-supplier-id': 'supplier1', - 'nhsd-correlation-id': 'correlationId', - 'x-request-id': 'requestId' - } + "nhsd-supplier-id": "supplier1", + "nhsd-correlation-id": "correlationId", + "x-request-id": "requestId", + }, }); const context = mockDeep(); const callback = jest.fn(); - const updateLetterServiceResponse : PatchLetterResponse = { - data: { - id: 'id1', - type: 'Letter', - attributes: { - status: 'REJECTED', - specificationId: 'spec1', - groupId: 'group1', - reasonCode: 123, - reasonText: 'Reason text', - } - } - }; - mockedPatchLetterStatus.mockResolvedValue(updateLetterServiceResponse); + mockedBatchUpdateStatus.mockResolvedValue(); const patchLetterHandler = createPatchLetterHandler(mockedDeps); const result = await patchLetterHandler(event, context, callback); expect(result).toEqual({ - statusCode: 200, - body: JSON.stringify(updateLetterServiceResponse, null, 2) + statusCode: 202, + body: "", }); }); - it('returns error response when there is no body', async () => { + it("returns error response when there is no body", async () => { const event = makeApiGwEvent({ - path: '/letters/id1', - pathParameters: {id: 'id1'}, + path: "/letters/id1", + pathParameters: { id: "id1" }, headers: { - 'nhsd-supplier-id': 'supplier1', - 'nhsd-correlation-id': 'correlationId', - 'x-request-id': 'requestId' - } + "nhsd-supplier-id": "supplier1", + "nhsd-correlation-id": "correlationId", + "x-request-id": "requestId", + }, }); const context = mockDeep(); const callback = jest.fn(); @@ -113,19 +102,23 @@ describe('patchLetter API Handler', () => { const patchLetterHandler = createPatchLetterHandler(mockedDeps); const result = await patchLetterHandler(event, context, callback); - expect(mockedMapErrorToResponse).toHaveBeenCalledWith(new ValidationError(errors.ApiErrorDetail.InvalidRequestMissingBody), 'correlationId', mockedDeps.logger); + expect(mockedProcessError).toHaveBeenCalledWith( + new ValidationError(errors.ApiErrorDetail.InvalidRequestMissingBody), + "correlationId", + mockedDeps.logger, + ); expect(result).toEqual(expectedErrorResponse); }); - it('returns error response when path parameter letterId is not found', async () => { + it("returns error response when path parameter letterId is not found", async () => { const event = makeApiGwEvent({ - path: '/letters/', + path: "/letters/", body: requestBody, headers: { - 'nhsd-supplier-id': 'supplier1', - 'nhsd-correlation-id': 'correlationId', - 'x-request-id': 'requestId' - } + "nhsd-supplier-id": "supplier1", + "nhsd-correlation-id": "correlationId", + "x-request-id": "requestId", + }, }); const context = mockDeep(); const callback = jest.fn(); @@ -133,23 +126,25 @@ describe('patchLetter API Handler', () => { const patchLetterHandler = createPatchLetterHandler(mockedDeps); const result = await patchLetterHandler(event, context, callback); - expect(mockedMapErrorToResponse).toHaveBeenCalledWith(new ValidationError(errors.ApiErrorDetail.InvalidRequestMissingLetterIdPathParameter), 'correlationId', mockedDeps.logger); + expect(mockedProcessError).toHaveBeenCalledWith( + new ValidationError( + errors.ApiErrorDetail.InvalidRequestMissingLetterIdPathParameter, + ), + "correlationId", + mockedDeps.logger, + ); expect(result).toEqual(expectedErrorResponse); }); - it('returns error response when error is thrown by service', async () => { - const error = new Error('Service error'); - mockedPatchLetterStatus.mockRejectedValue(error); - + it("returns error when supplier id is missing", async () => { const event = makeApiGwEvent({ - path: '/letters/id1', + path: "/letters/id1", body: requestBody, - pathParameters: {id: 'id1'}, + pathParameters: { id: "id1" }, headers: { - 'nhsd-supplier-id': 'supplier1', - 'nhsd-correlation-id': 'correlationId', - 'x-request-id': 'requestId' - } + "nhsd-correlation-id": "correlationId", + "x-request-id": "requestId", + }, }); const context = mockDeep(); const callback = jest.fn(); @@ -157,19 +152,24 @@ describe('patchLetter API Handler', () => { const patchLetterHandler = createPatchLetterHandler(mockedDeps); const result = await patchLetterHandler(event, context, callback); - expect(mockedMapErrorToResponse).toHaveBeenCalledWith(error, 'correlationId', mockedDeps.logger); + expect(mockedProcessError).toHaveBeenCalledWith( + new Error("The supplier ID is missing from the request"), + "correlationId", + mockedDeps.logger, + ); expect(result).toEqual(expectedErrorResponse); }); - it('returns error when supplier id is missing', async () => { + it("returns error when request body does not have correct shape", async () => { const event = makeApiGwEvent({ - path: '/letters/id1', - body: requestBody, - pathParameters: {id: 'id1'}, + path: "/letters/id1", + body: "{test: 'test'}", + pathParameters: { id: "id1" }, headers: { - 'nhsd-correlation-id': 'correlationId', - 'x-request-id': 'requestId' - } + "nhsd-supplier-id": "supplier1", + "nhsd-correlation-id": "correlationId", + "x-request-id": "requestId", + }, }); const context = mockDeep(); const callback = jest.fn(); @@ -177,20 +177,24 @@ describe('patchLetter API Handler', () => { const patchLetterHandler = createPatchLetterHandler(mockedDeps); const result = await patchLetterHandler(event, context, callback); - expect(mockedMapErrorToResponse).toHaveBeenCalledWith(new Error('The supplier ID is missing from the request'), 'correlationId', mockedDeps.logger); + expect(mockedProcessError).toHaveBeenCalledWith( + new ValidationError(errors.ApiErrorDetail.InvalidRequestBody), + "correlationId", + mockedDeps.logger, + ); expect(result).toEqual(expectedErrorResponse); }); - it('returns error when request body does not have correct shape', async () => { + it("returns error when request body is not json", async () => { const event = makeApiGwEvent({ - path: '/letters/id1', - body: "{test: 'test'}", - pathParameters: {id: 'id1'}, + path: "/letters/id1", + body: "{#invalidJSON", + pathParameters: { id: "id1" }, headers: { - 'nhsd-supplier-id': 'supplier1', - 'nhsd-correlation-id': 'correlationId', - 'x-request-id': 'requestId' - } + "nhsd-supplier-id": "supplier1", + "nhsd-correlation-id": "correlationId", + "x-request-id": "requestId", + }, }); const context = mockDeep(); const callback = jest.fn(); @@ -198,20 +202,24 @@ describe('patchLetter API Handler', () => { const patchLetterHandler = createPatchLetterHandler(mockedDeps); const result = await patchLetterHandler(event, context, callback); - expect(mockedMapErrorToResponse).toHaveBeenCalledWith(new ValidationError(errors.ApiErrorDetail.InvalidRequestBody), 'correlationId', mockedDeps.logger); + expect(mockedProcessError).toHaveBeenCalledWith( + new ValidationError(errors.ApiErrorDetail.InvalidRequestBody), + "correlationId", + mockedDeps.logger, + ); expect(result).toEqual(expectedErrorResponse); }); - it('returns error when request body is not json', async () => { + it("returns error if path letterId and body letterId do not match", async () => { const event = makeApiGwEvent({ - path: '/letters/id1', - body: '{#invalidJSON', - pathParameters: {id: 'id1'}, + path: "/letters/id2", + body: requestBody, + pathParameters: { id: "id2" }, headers: { - 'nhsd-supplier-id': 'supplier1', - 'nhsd-correlation-id': 'correlationId', - 'x-request-id': 'requestId' - } + "nhsd-supplier-id": "supplier1", + "nhsd-correlation-id": "correlationId", + "x-request-id": "requestId", + }, }); const context = mockDeep(); const callback = jest.fn(); @@ -219,47 +227,59 @@ describe('patchLetter API Handler', () => { const patchLetterHandler = createPatchLetterHandler(mockedDeps); const result = await patchLetterHandler(event, context, callback); - expect(mockedMapErrorToResponse).toHaveBeenCalledWith(new ValidationError(errors.ApiErrorDetail.InvalidRequestBody), 'correlationId', mockedDeps.logger); + expect(mockedProcessError).toHaveBeenCalledWith( + new ValidationError( + errors.ApiErrorDetail.InvalidRequestLetterIdsMismatch, + ), + "correlationId", + mockedDeps.logger, + ); expect(result).toEqual(expectedErrorResponse); }); - it('returns error if unexpected error is thrown', async () => { + it("returns error if unexpected error is thrown", async () => { const event = makeApiGwEvent({ - path: '/letters/id1', - body: 'somebody', - pathParameters: {id: 'id1'}, + path: "/letters/id1", + body: "somebody", + pathParameters: { id: "id1" }, headers: { - 'nhsd-supplier-id': 'supplier1', - 'nhsd-correlation-id': 'correlationId', - 'x-request-id': 'requestId' - } + "nhsd-supplier-id": "supplier1", + "nhsd-correlation-id": "correlationId", + "x-request-id": "requestId", + }, }); const context = mockDeep(); const callback = jest.fn(); - const error = 'Unexpected error'; - const spy = jest.spyOn(JSON, 'parse').mockImplementation(() => { + const error = "Unexpected error"; + const spy = jest.spyOn(JSON, "parse").mockImplementation(() => { + // disable throw error lint to allow unexpected throw behaviours to be tested + // eslint-disable-next-line @typescript-eslint/only-throw-error throw error; }); const patchLetterHandler = createPatchLetterHandler(mockedDeps); const result = await patchLetterHandler(event, context, callback); - expect(mockedMapErrorToResponse).toHaveBeenCalledWith(error, 'correlationId', mockedDeps.logger); + expect(mockedProcessError).toHaveBeenCalledWith( + error, + "correlationId", + mockedDeps.logger, + ); expect(result).toEqual(expectedErrorResponse); spy.mockRestore(); }); - it('returns error if correlation id not provided in request', async () => { + it("returns error if correlation id not provided in request", async () => { const event = makeApiGwEvent({ - path: '/letters/id1', + path: "/letters/id1", body: requestBody, - pathParameters: {id: 'id1'}, + pathParameters: { id: "id1" }, headers: { - 'nhsd-supplier-id': 'supplier1', - 'x-request-id': 'requestId' - } + "nhsd-supplier-id": "supplier1", + "x-request-id": "requestId", + }, }); const context = mockDeep(); const callback = jest.fn(); @@ -267,16 +287,20 @@ describe('patchLetter API Handler', () => { const patchLetterHandler = createPatchLetterHandler(mockedDeps); const result = await patchLetterHandler(event, context, callback); - expect(mockedMapErrorToResponse).toHaveBeenCalledWith(new Error("The request headers don't contain the APIM correlation id"), undefined, mockedDeps.logger); + expect(mockedProcessError).toHaveBeenCalledWith( + new Error("The request headers don't contain the APIM correlation id"), + undefined, + mockedDeps.logger, + ); expect(result).toEqual(expectedErrorResponse); }); - it('returns error if headers are empty', async () => { + it("returns error if headers are empty", async () => { const event = makeApiGwEvent({ - path: '/letters/id1', + path: "/letters/id1", body: requestBody, - pathParameters: {id: 'id1'}, - headers: {} + pathParameters: { id: "id1" }, + headers: {}, }); const context = mockDeep(); const callback = jest.fn(); @@ -284,19 +308,23 @@ describe('patchLetter API Handler', () => { const patchLetterHandler = createPatchLetterHandler(mockedDeps); const result = await patchLetterHandler(event, context, callback); - expect(mockedMapErrorToResponse).toHaveBeenCalledWith(new Error('The request headers are empty'), undefined, mockedDeps.logger); + expect(mockedProcessError).toHaveBeenCalledWith( + new Error("The request headers are empty"), + undefined, + mockedDeps.logger, + ); expect(result).toEqual(expectedErrorResponse); }); - it('returns error when request id is missing', async () => { + it("returns error when request id is missing", async () => { const event = makeApiGwEvent({ - path: '/letters/id1', + path: "/letters/id1", body: requestBody, - pathParameters: {id: 'id1'}, + pathParameters: { id: "id1" }, headers: { - 'nhsd-supplier-id': 'supplier1', - 'nhsd-correlation-id': 'correlationId' - } + "nhsd-supplier-id": "supplier1", + "nhsd-correlation-id": "correlationId", + }, }); const context = mockDeep(); const callback = jest.fn(); @@ -304,7 +332,11 @@ describe('patchLetter API Handler', () => { const patchLetterHandler = createPatchLetterHandler(mockedDeps); const result = await patchLetterHandler(event, context, callback); - expect(mockedMapErrorToResponse).toHaveBeenCalledWith(new Error("The request headers don't contain the x-request-id"), 'correlationId', mockedDeps.logger); + expect(mockedProcessError).toHaveBeenCalledWith( + new Error("The request headers don't contain the x-request-id"), + "correlationId", + mockedDeps.logger, + ); expect(result).toEqual(expectedErrorResponse); }); }); diff --git a/lambdas/api-handler/src/handlers/__tests__/post-letters.test.ts b/lambdas/api-handler/src/handlers/__tests__/post-letters.test.ts new file mode 100644 index 00000000..f20d51c2 --- /dev/null +++ b/lambdas/api-handler/src/handlers/__tests__/post-letters.test.ts @@ -0,0 +1,332 @@ +// mock service +import { APIGatewayProxyResult, Context } from "aws-lambda"; +import { mockDeep } from "jest-mock-extended"; +import { S3Client } from "@aws-sdk/client-s3"; +import pino from "pino"; +import { LetterRepository } from "@internal/datastore/src"; +import * as letterService from "../../services/letter-operations"; +import { processError } from "../../mappers/error-mapper"; + +import { makeApiGwEvent } from "./utils/test-utils"; +import { PostLettersRequest } from "../../contracts/letters"; +import ValidationError from "../../errors/validation-error"; +import * as errors from "../../contracts/errors"; +import { EnvVars } from "../../config/env"; +import { Deps } from "../../config/deps"; +import createPostLettersHandler from "../post-letters"; + +jest.mock("../../services/letter-operations"); +const mockedBatchUpdateStatus = jest.mocked( + letterService.enqueueLetterUpdateRequests, +); + +// mock mapper +jest.mock("../../mappers/error-mapper"); +const mockedProcessError = jest.mocked(processError); +const expectedErrorResponse: APIGatewayProxyResult = { + statusCode: 400, + body: "Error", +}; +mockedProcessError.mockReturnValue(expectedErrorResponse); + +const updateLettersRequest: PostLettersRequest = { + data: [ + { + id: "id1", + type: "Letter", + attributes: { + status: "REJECTED", + reasonCode: "123", + reasonText: "Reason text", + }, + }, + { + id: "id2", + type: "Letter", + attributes: { + status: "ACCEPTED", + }, + }, + { + id: "id3", + type: "Letter", + attributes: { + status: "DELIVERED", + }, + }, + ], +}; + +const requestBody = JSON.stringify(updateLettersRequest, null, 2); + +describe("postLetters API Handler", () => { + beforeEach(() => { + jest.clearAllMocks(); + }); + + const mockedDeps: jest.Mocked = { + s3Client: {} as unknown as S3Client, + letterRepo: {} as unknown as LetterRepository, + logger: { info: jest.fn(), error: jest.fn() } as unknown as pino.Logger, + env: { + SUPPLIER_ID_HEADER: "nhsd-supplier-id", + APIM_CORRELATION_HEADER: "nhsd-correlation-id", + LETTERS_TABLE_NAME: "LETTERS_TABLE_NAME", + LETTER_TTL_HOURS: 12_960, + DOWNLOAD_URL_TTL_SECONDS: 60, + MAX_LIMIT: 2500, + QUEUE_URL: "SQS_URL", + } as unknown as EnvVars, + } as Deps; + + it("returns 202 Accepted", async () => { + const event = makeApiGwEvent({ + path: "/letters", + body: requestBody, + headers: { + "nhsd-supplier-id": "supplier1", + "nhsd-correlation-id": "correlationId", + "x-request-id": "requestId", + }, + }); + const context = mockDeep(); + const callback = jest.fn(); + + mockedBatchUpdateStatus.mockResolvedValue(); + + const postLettersHandler = createPostLettersHandler(mockedDeps); + const result = await postLettersHandler(event, context, callback); + + expect(result).toEqual({ + statusCode: 202, + body: "", + }); + }); + + it("returns error when supplier id is missing", async () => { + const event = makeApiGwEvent({ + path: "/letters", + body: requestBody, + headers: { + "nhsd-correlation-id": "correlationId", + "x-request-id": "requestId", + }, + }); + const context = mockDeep(); + const callback = jest.fn(); + + const postLettersHandler = createPostLettersHandler(mockedDeps); + const result = await postLettersHandler(event, context, callback); + + expect(mockedProcessError).toHaveBeenCalledWith( + new Error("The supplier ID is missing from the request"), + "correlationId", + mockedDeps.logger, + ); + expect(result).toEqual(expectedErrorResponse); + }); + + it("returns error if correlation id not provided in request", async () => { + const event = makeApiGwEvent({ + path: "/letters", + body: requestBody, + headers: { + "nhsd-supplier-id": "supplier1", + "x-request-id": "requestId", + }, + }); + const context = mockDeep(); + const callback = jest.fn(); + + const postLettersHandler = createPostLettersHandler(mockedDeps); + const result = await postLettersHandler(event, context, callback); + + expect(mockedProcessError).toHaveBeenCalledWith( + new Error("The request headers don't contain the APIM correlation id"), + undefined, + mockedDeps.logger, + ); + expect(result).toEqual(expectedErrorResponse); + }); + + it("returns error response when there is no body", async () => { + const event = makeApiGwEvent({ + path: "/letters", + headers: { + "nhsd-supplier-id": "supplier1", + "nhsd-correlation-id": "correlationId", + "x-request-id": "requestId", + }, + }); + const context = mockDeep(); + const callback = jest.fn(); + + const postLettersHandler = createPostLettersHandler(mockedDeps); + const result = await postLettersHandler(event, context, callback); + + expect(mockedProcessError).toHaveBeenCalledWith( + new ValidationError(errors.ApiErrorDetail.InvalidRequestMissingBody), + "correlationId", + mockedDeps.logger, + ); + expect(result).toEqual(expectedErrorResponse); + }); + + it("returns error when request body does not have correct shape", async () => { + const event = makeApiGwEvent({ + path: "/letters", + body: "{test: 'test'}", + headers: { + "nhsd-supplier-id": "supplier1", + "nhsd-correlation-id": "correlationId", + "x-request-id": "requestId", + }, + }); + const context = mockDeep(); + const callback = jest.fn(); + + const postLettersHandler = createPostLettersHandler(mockedDeps); + const result = await postLettersHandler(event, context, callback); + + expect(mockedProcessError).toHaveBeenCalledWith( + new ValidationError(errors.ApiErrorDetail.InvalidRequestBody), + "correlationId", + mockedDeps.logger, + ); + expect(result).toEqual(expectedErrorResponse); + }); + + it("returns error when request contains more than 2500 letters to update", async () => { + const event = makeApiGwEvent({ + path: "/letters", + body: JSON.stringify({ + data: Array.from({ length: 2501 }, () => ({ + id: "id1", + type: "Letter", + attributes: { + status: "ACCEPTED", + }, + })), + }), + headers: { + "nhsd-supplier-id": "supplier1", + "nhsd-correlation-id": "correlationId", + "x-request-id": "requestId", + }, + }); + const context = mockDeep(); + const callback = jest.fn(); + + const postLettersHandler = createPostLettersHandler(mockedDeps); + const result = await postLettersHandler(event, context, callback); + + expect(mockedProcessError).toHaveBeenCalledWith( + new ValidationError(errors.ApiErrorDetail.InvalidRequestLettersToUpdate, { + args: [mockedDeps.env.MAX_LIMIT], + }), + "correlationId", + mockedDeps.logger, + ); + expect(result).toEqual(expectedErrorResponse); + }); + + it("returns error when the request has duplicate letter ids", async () => { + const event = makeApiGwEvent({ + path: "/letters", + body: JSON.stringify({ + data: [ + { + id: "id1", + type: "Letter", + attributes: { + status: "ACCEPTED", + }, + }, + { + id: "id1", + type: "Letter", + attributes: { + status: "ACCEPTED", + }, + }, + ], + }), + headers: { + "nhsd-supplier-id": "supplier1", + "nhsd-correlation-id": "correlationId", + "x-request-id": "requestId", + }, + }); + const context = mockDeep(); + const callback = jest.fn(); + + const postLettersHandler = createPostLettersHandler(mockedDeps); + const result = await postLettersHandler(event, context, callback); + + expect(mockedProcessError).toHaveBeenCalledWith( + new ValidationError( + errors.ApiErrorDetail.InvalidRequestDuplicateLetterId, + ), + "correlationId", + mockedDeps.logger, + ); + expect(result).toEqual(expectedErrorResponse); + }); + + it("returns error when request body is not json", async () => { + const event = makeApiGwEvent({ + path: "/letters", + body: "{#invalidJSON", + headers: { + "nhsd-supplier-id": "supplier1", + "nhsd-correlation-id": "correlationId", + "x-request-id": "requestId", + }, + }); + const context = mockDeep(); + const callback = jest.fn(); + + const postLettersHandler = createPostLettersHandler(mockedDeps); + const result = await postLettersHandler(event, context, callback); + + expect(mockedProcessError).toHaveBeenCalledWith( + new ValidationError(errors.ApiErrorDetail.InvalidRequestBody), + "correlationId", + mockedDeps.logger, + ); + expect(result).toEqual(expectedErrorResponse); + }); + + it("returns error if unexpected error is thrown", async () => { + const event = makeApiGwEvent({ + path: "/letters", + body: "somebody", + headers: { + "nhsd-supplier-id": "supplier1", + "nhsd-correlation-id": "correlationId", + "x-request-id": "requestId", + }, + }); + const context = mockDeep(); + const callback = jest.fn(); + + const error = "Unexpected error"; + const spy = jest.spyOn(JSON, "parse").mockImplementation(() => { + // disable throw error lint to allow unexpected throw behaviours to be tested + // eslint-disable-next-line @typescript-eslint/only-throw-error + throw error; + }); + + const postLettersHandler = createPostLettersHandler(mockedDeps); + const result = await postLettersHandler(event, context, callback); + + expect(mockedProcessError).toHaveBeenCalledWith( + error, + "correlationId", + mockedDeps.logger, + ); + expect(result).toEqual(expectedErrorResponse); + + spy.mockRestore(); + }); +}); diff --git a/lambdas/api-handler/src/handlers/__tests__/post-mi.test.ts b/lambdas/api-handler/src/handlers/__tests__/post-mi.test.ts index d7d942e1..c1090879 100644 --- a/lambdas/api-handler/src/handlers/__tests__/post-mi.test.ts +++ b/lambdas/api-handler/src/handlers/__tests__/post-mi.test.ts @@ -1,202 +1,246 @@ import { Context } from "aws-lambda"; import { mockDeep } from "jest-mock-extended"; +import pino from "pino"; +import { MIRepository } from "@internal/datastore/src"; import { makeApiGwEvent } from "./utils/test-utils"; import { PostMIRequest, PostMIResponse } from "../../contracts/mi"; -import * as miService from '../../services/mi-operations'; -import pino from 'pino'; -import { MIRepository } from "../../../../../internal/datastore/src"; +import postMiOperation from "../../services/mi-operations"; import { Deps } from "../../config/deps"; import { EnvVars } from "../../config/env"; -import { createPostMIHandler } from "../post-mi"; - -jest.mock('../../services/mi-operations'); - -const postMIRequest : PostMIRequest = { - data: { - type: 'ManagementInformation', - attributes: { - lineItem: 'envelope-business-standard', - timestamp: '2023-11-17T14:27:51.413Z', - quantity: 22, - specificationId: 'spec1', - groupId: 'group1', - stockRemaining: 20000 - } - } +import createPostMIHandler from "../post-mi"; + +jest.mock("../../services/mi-operations"); + +const postMIRequest: PostMIRequest = { + data: { + type: "ManagementInformation", + attributes: { + lineItem: "envelope-business-standard", + timestamp: "2023-11-17T14:27:51.413Z", + quantity: 22, + specificationId: "spec1", + groupId: "group1", + stockRemaining: 20_000, + }, + }, }; const requestBody = JSON.stringify(postMIRequest, null, 2); - const postMIResponse : PostMIResponse = { - data: { - id: 'id1', - ...postMIRequest.data - } - }; +const postMIResponse: PostMIResponse = { + data: { + id: "id1", + ...postMIRequest.data, + }, +}; -const mockedPostMIOperation = jest.mocked(miService.postMI); +const mockedPostMIOperation = jest.mocked(postMiOperation); beforeEach(() => { jest.clearAllMocks(); }); - -describe('postMI API Handler', () => { - - const mockedDeps: jest.Mocked = { - miRepo: {} as unknown as MIRepository, - logger: { info: jest.fn(), error: jest.fn() } as unknown as pino.Logger, - env: { - SUPPLIER_ID_HEADER: 'nhsd-supplier-id', - APIM_CORRELATION_HEADER: 'nhsd-correlation-id', - DOWNLOAD_URL_TTL_SECONDS: 1 - } as unknown as EnvVars - } as Deps; - - - it('returns 200 OK with updated resource', async () => { +describe("postMI API Handler", () => { + const mockedDeps: jest.Mocked = { + miRepo: {} as unknown as MIRepository, + logger: { info: jest.fn(), error: jest.fn() } as unknown as pino.Logger, + env: { + SUPPLIER_ID_HEADER: "nhsd-supplier-id", + APIM_CORRELATION_HEADER: "nhsd-correlation-id", + DOWNLOAD_URL_TTL_SECONDS: 1, + } as unknown as EnvVars, + } as Deps; + + it("returns 200 OK with updated resource", async () => { const event = makeApiGwEvent({ - path: '/mi', + path: "/mi", body: requestBody, - headers: {'nhsd-supplier-id': 'supplier1', 'nhsd-correlation-id': 'correlationId', 'x-request-id': 'requestId'} + headers: { + "nhsd-supplier-id": "supplier1", + "nhsd-correlation-id": "correlationId", + "x-request-id": "requestId", + }, }); mockedPostMIOperation.mockResolvedValue(postMIResponse); const postMI = createPostMIHandler(mockedDeps); - const result = await postMI(event, mockDeep(), jest.fn()); + const result = await postMI(event, mockDeep(), jest.fn()); expect(result).toEqual({ statusCode: 201, - body: JSON.stringify(postMIResponse, null, 2) + body: JSON.stringify(postMIResponse, null, 2), }); }); - - it('rejects invalid timestamps', async() => { + it("rejects invalid timestamps", async () => { const modifiedRequest = JSON.parse(requestBody); - modifiedRequest['data']['attributes']['timestamp'] = '2025-02-31T00:00:00Z'; + modifiedRequest.data.attributes.timestamp = "2025-02-31T00:00:00Z"; const event = makeApiGwEvent({ - path: '/mi', + path: "/mi", body: JSON.stringify(modifiedRequest), - headers: {'nhsd-supplier-id': 'supplier1', 'nhsd-correlation-id': 'correlationId', 'x-request-id': 'requestId'} + headers: { + "nhsd-supplier-id": "supplier1", + "nhsd-correlation-id": "correlationId", + "x-request-id": "requestId", + }, }); const postMI = createPostMIHandler(mockedDeps); - const result = await postMI(event, mockDeep(), jest.fn()); + const result = await postMI(event, mockDeep(), jest.fn()); - expect(result).toEqual(expect.objectContaining({ - statusCode: 400 - })); + expect(result).toEqual( + expect.objectContaining({ + statusCode: 400, + }), + ); }); - it('returns 400 Bad Request when there is no body', async () => { - const event = makeApiGwEvent({ - path: '/mi', - headers: {'nhsd-supplier-id': 'supplier1', 'nhsd-correlation-id': 'correlationId', 'x-request-id': 'requestId'} - }); - - const postMI = createPostMIHandler(mockedDeps); - const result = await postMI(event, mockDeep(), jest.fn()); - - expect(result).toEqual(expect.objectContaining({ - statusCode: 400 - })); + it("returns 400 Bad Request when there is no body", async () => { + const event = makeApiGwEvent({ + path: "/mi", + headers: { + "nhsd-supplier-id": "supplier1", + "nhsd-correlation-id": "correlationId", + "x-request-id": "requestId", + }, }); + const postMI = createPostMIHandler(mockedDeps); + const result = await postMI(event, mockDeep(), jest.fn()); - it('returns 500 Internal Error when error is thrown by service', async () => { - const event = makeApiGwEvent({ - path: '/mi', - body: requestBody, - pathParameters: {id: 'id1'}, - headers: {'nhsd-supplier-id': 'supplier1', 'nhsd-correlation-id': 'correlationId', 'x-request-id': 'requestId'} - }); - mockedPostMIOperation.mockRejectedValue(new Error()); - - const postMI = createPostMIHandler(mockedDeps); - const result = await postMI(event, mockDeep(), jest.fn()); + expect(result).toEqual( + expect.objectContaining({ + statusCode: 400, + }), + ); + }); - expect(result).toEqual(expect.objectContaining({ - statusCode: 500 - })); + it("returns 500 Internal Error when error is thrown by service", async () => { + const event = makeApiGwEvent({ + path: "/mi", + body: requestBody, + pathParameters: { id: "id1" }, + headers: { + "nhsd-supplier-id": "supplier1", + "nhsd-correlation-id": "correlationId", + "x-request-id": "requestId", + }, }); + mockedPostMIOperation.mockRejectedValue(new Error("Unknown error")); - it('returns 500 Bad Request when supplier id is missing', async () => { + const postMI = createPostMIHandler(mockedDeps); + const result = await postMI(event, mockDeep(), jest.fn()); + + expect(result).toEqual( + expect.objectContaining({ + statusCode: 500, + }), + ); + }); + + it("returns 500 Bad Request when supplier id is missing", async () => { const event = makeApiGwEvent({ - path: '/mi', + path: "/mi", body: requestBody, - headers: {'nhsd-correlation-id': 'correlationId', 'x-request-id': 'requestId'} + headers: { + "nhsd-correlation-id": "correlationId", + "x-request-id": "requestId", + }, }); const postMI = createPostMIHandler(mockedDeps); - const result = await postMI(event, mockDeep(), jest.fn()); + const result = await postMI(event, mockDeep(), jest.fn()); - expect(result).toEqual(expect.objectContaining({ - statusCode: 500 - })); + expect(result).toEqual( + expect.objectContaining({ + statusCode: 500, + }), + ); }); - it('returns 500 Internal Server Error when correlation id is missing', async () => { + it("returns 500 Internal Server Error when correlation id is missing", async () => { const event = makeApiGwEvent({ - path: '/mi', + path: "/mi", body: requestBody, - headers: {'nhsd-supplier-id': 'supplier1', 'x-request-id': 'requestId'} + headers: { "nhsd-supplier-id": "supplier1", "x-request-id": "requestId" }, }); const postMI = createPostMIHandler(mockedDeps); - const result = await postMI(event, mockDeep(), jest.fn()); + const result = await postMI(event, mockDeep(), jest.fn()); - expect(result).toEqual(expect.objectContaining({ - statusCode: 500 - })); + expect(result).toEqual( + expect.objectContaining({ + statusCode: 500, + }), + ); }); - it('returns 400 Bad Request when request does not have correct shape', async () => { + it("returns 400 Bad Request when request does not have correct shape", async () => { const event = makeApiGwEvent({ - path: '/mi', + path: "/mi", body: '{"test": "test"}', - headers: {'nhsd-supplier-id': 'supplier1', 'nhsd-correlation-id': 'correlationId', 'x-request-id': 'requestId'} + headers: { + "nhsd-supplier-id": "supplier1", + "nhsd-correlation-id": "correlationId", + "x-request-id": "requestId", + }, }); const postMI = createPostMIHandler(mockedDeps); - const result = await postMI(event, mockDeep(), jest.fn()); + const result = await postMI(event, mockDeep(), jest.fn()); - expect(result).toEqual(expect.objectContaining({ - statusCode: 400 - })); + expect(result).toEqual( + expect.objectContaining({ + statusCode: 400, + }), + ); }); - it('returns 400 Bad Request when request body is not json', async () => { + it("returns 400 Bad Request when request body is not json", async () => { const event = makeApiGwEvent({ - path: '/mi', - body: '{#invalidJSON', - headers: {'nhsd-supplier-id': 'supplier1', 'nhsd-correlation-id': 'correlationId', 'x-request-id': 'requestId'} + path: "/mi", + body: "{#invalidJSON", + headers: { + "nhsd-supplier-id": "supplier1", + "nhsd-correlation-id": "correlationId", + "x-request-id": "requestId", + }, }); const postMI = createPostMIHandler(mockedDeps); - const result = await postMI(event, mockDeep(), jest.fn()); + const result = await postMI(event, mockDeep(), jest.fn()); - expect(result).toEqual(expect.objectContaining({ - statusCode: 400 - })); + expect(result).toEqual( + expect.objectContaining({ + statusCode: 400, + }), + ); }); - it('returns 500 Internal Server Error when parsing fails', async () => { + it("returns 500 Internal Server Error when parsing fails", async () => { const event = makeApiGwEvent({ - path: '/mi', + path: "/mi", body: requestBody, - headers: {'nhsd-supplier-id': 'supplier1', 'nhsd-correlation-id': 'correlationId', 'x-request-id': 'requestId'} + headers: { + "nhsd-supplier-id": "supplier1", + "nhsd-correlation-id": "correlationId", + "x-request-id": "requestId", + }, + }); + const spy = jest.spyOn(JSON, "parse").mockImplementation(() => { + // disable throw error lint to allow unexpected throw behaviours to be tested + // eslint-disable-next-line @typescript-eslint/only-throw-error + throw "Unexpected error"; }); - const spy = jest.spyOn(JSON, 'parse').mockImplementation(() => { - throw 'Unexpected error'; - }) const postMI = createPostMIHandler(mockedDeps); - const result = await postMI(event, mockDeep(), jest.fn()); + const result = await postMI(event, mockDeep(), jest.fn()); - expect(result).toEqual(expect.objectContaining({ - statusCode: 500 - })); + expect(result).toEqual( + expect.objectContaining({ + statusCode: 500, + }), + ); spy.mockRestore(); }); diff --git a/lambdas/api-handler/src/handlers/__tests__/utils/test-utils.ts b/lambdas/api-handler/src/handlers/__tests__/utils/test-utils.ts index 4a3a9171..160946fc 100644 --- a/lambdas/api-handler/src/handlers/__tests__/utils/test-utils.ts +++ b/lambdas/api-handler/src/handlers/__tests__/utils/test-utils.ts @@ -1,14 +1,14 @@ -import type { APIGatewayProxyEvent } from 'aws-lambda'; +import type { APIGatewayProxyEvent } from "aws-lambda"; export function makeApiGwEvent( - overrides: Partial = {} + overrides: Partial = {}, ): APIGatewayProxyEvent { return { - resource: '/{proxy+}', - path: '/', - httpMethod: 'GET', + resource: "/{proxy+}", + path: "/", + httpMethod: "GET", headers: { - 'NHSD-Supplier-ID': 'supplier1' + "NHSD-Supplier-ID": "supplier1", }, multiValueHeaders: {}, queryStringParameters: null, @@ -16,18 +16,18 @@ export function makeApiGwEvent( pathParameters: null, stageVariables: null, requestContext: { - accountId: '123456789012', - apiId: 'api-id', - authorizer: {}, - protocol: 'HTTP/1.1', - httpMethod: 'GET', + accountId: "123456789012", + apiId: "api-id", + authorizer: null, + protocol: "HTTP/1.1", + httpMethod: "GET", identity: {} as any, - path: '/', - stage: 'test', - requestId: 'req-id', + path: "/", + stage: "test", + requestId: "req-id", requestTimeEpoch: Date.now(), - resourceId: 'res-id', - resourcePath: '/{proxy+}', + resourceId: "res-id", + resourcePath: "/{proxy+}", }, body: null, isBase64Encoded: false, diff --git a/lambdas/api-handler/src/handlers/get-letter-data.ts b/lambdas/api-handler/src/handlers/get-letter-data.ts index 547c8e17..4d516645 100644 --- a/lambdas/api-handler/src/handlers/get-letter-data.ts +++ b/lambdas/api-handler/src/handlers/get-letter-data.ts @@ -1,36 +1,51 @@ import { APIGatewayProxyHandler } from "aws-lambda"; -import { assertNotEmpty, validateCommonHeaders } from "../utils/validation"; -import { ApiErrorDetail } from '../contracts/errors'; -import { mapErrorToResponse } from "../mappers/error-mapper"; -import { ValidationError } from "../errors"; +import { assertNotEmpty } from "../utils/validation"; +import { extractCommonIds } from "../utils/common-ids"; +import { ApiErrorDetail } from "../contracts/errors"; +import { processError } from "../mappers/error-mapper"; +import ValidationError from "../errors/validation-error"; import { getLetterDataUrl } from "../services/letter-operations"; import type { Deps } from "../config/deps"; - -export function createGetLetterDataHandler(deps: Deps): APIGatewayProxyHandler { - +export default function createGetLetterDataHandler( + deps: Deps, +): APIGatewayProxyHandler { return async (event) => { + const commonIds = extractCommonIds( + event.headers, + event.requestContext, + deps, + ); - const commonHeadersResult = validateCommonHeaders(event.headers, deps); - - if (!commonHeadersResult.ok) { - return mapErrorToResponse(commonHeadersResult.error, commonHeadersResult.correlationId, deps.logger); + if (!commonIds.ok) { + return processError( + commonIds.error, + commonIds.correlationId, + deps.logger, + ); } try { - const letterId = assertNotEmpty( event.pathParameters?.id, - new ValidationError(ApiErrorDetail.InvalidRequestMissingLetterIdPathParameter)); + const letterId = assertNotEmpty( + event.pathParameters?.id, + new ValidationError( + ApiErrorDetail.InvalidRequestMissingLetterIdPathParameter, + ), + ); return { statusCode: 303, headers: { - 'Location': await getLetterDataUrl(commonHeadersResult.value.supplierId, letterId, deps) + Location: await getLetterDataUrl( + commonIds.value.supplierId, + letterId, + deps, + ), }, - body: '' + body: "", }; + } catch (error) { + return processError(error, commonIds.value.correlationId, deps.logger); } - catch (error) { - return mapErrorToResponse(error, commonHeadersResult.value.correlationId, deps.logger); - } - } -}; + }; +} diff --git a/lambdas/api-handler/src/handlers/get-letter.ts b/lambdas/api-handler/src/handlers/get-letter.ts index 5c428dce..a88a4882 100644 --- a/lambdas/api-handler/src/handlers/get-letter.ts +++ b/lambdas/api-handler/src/handlers/get-letter.ts @@ -1,43 +1,59 @@ import { APIGatewayProxyHandler } from "aws-lambda"; -import { assertNotEmpty, validateCommonHeaders } from "../utils/validation"; -import { ValidationError } from "../errors"; +import { assertNotEmpty } from "../utils/validation"; +import { extractCommonIds } from "../utils/common-ids"; +import ValidationError from "../errors/validation-error"; import { ApiErrorDetail } from "../contracts/errors"; import { getLetterById } from "../services/letter-operations"; -import { mapErrorToResponse } from "../mappers/error-mapper"; +import { processError } from "../mappers/error-mapper"; import { mapToGetLetterResponse } from "../mappers/letter-mapper"; import { Deps } from "../config/deps"; - -export function createGetLetterHandler(deps: Deps): APIGatewayProxyHandler { - +export default function createGetLetterHandler( + deps: Deps, +): APIGatewayProxyHandler { return async (event) => { - - const commonHeadersResult = validateCommonHeaders(event.headers, deps); - - if (!commonHeadersResult.ok) { - return mapErrorToResponse(commonHeadersResult.error, commonHeadersResult.correlationId, deps.logger); + const commonIds = extractCommonIds( + event.headers, + event.requestContext, + deps, + ); + + if (!commonIds.ok) { + return processError( + commonIds.error, + commonIds.correlationId, + deps.logger, + ); } try { - const letterId = assertNotEmpty(event.pathParameters?.id, new ValidationError(ApiErrorDetail.InvalidRequestMissingLetterIdPathParameter)); - - const letter = await getLetterById(commonHeadersResult.value.supplierId, letterId, deps.letterRepo); + const letterId = assertNotEmpty( + event.pathParameters?.id, + new ValidationError( + ApiErrorDetail.InvalidRequestMissingLetterIdPathParameter, + ), + ); + + const letter = await getLetterById( + commonIds.value.supplierId, + letterId, + deps.letterRepo, + ); const response = mapToGetLetterResponse(letter); deps.logger.info({ - description: 'Letter successfully fetched by id', - supplierId: commonHeadersResult.value.supplierId, - letterId + description: "Letter successfully fetched by id", + supplierId: commonIds.value.supplierId, + letterId, }); return { statusCode: 200, body: JSON.stringify(response, null, 2), }; - } catch (error) - { - return mapErrorToResponse(error, commonHeadersResult.value.correlationId, deps.logger); + } catch (error) { + return processError(error, commonIds.value.correlationId, deps.logger); } - } + }; } diff --git a/lambdas/api-handler/src/handlers/get-letters.ts b/lambdas/api-handler/src/handlers/get-letters.ts index 0d42ea20..adc3febc 100644 --- a/lambdas/api-handler/src/handlers/get-letters.ts +++ b/lambdas/api-handler/src/handlers/get-letters.ts @@ -1,119 +1,134 @@ -import { APIGatewayProxyEventQueryStringParameters, APIGatewayProxyHandler } from 'aws-lambda'; -import { getLettersForSupplier } from '../services/letter-operations'; -import { validateCommonHeaders } from '../utils/validation'; -import { ApiErrorDetail } from '../contracts/errors'; -import { mapErrorToResponse } from '../mappers/error-mapper'; -import { ValidationError } from '../errors'; -import { mapToGetLettersResponse } from '../mappers/letter-mapper'; -import type { Deps } from '../config/deps'; -import { Logger } from 'pino'; - +import { + APIGatewayProxyEventQueryStringParameters, + APIGatewayProxyHandler, +} from "aws-lambda"; +import { Logger } from "pino"; +import { getLettersForSupplier } from "../services/letter-operations"; +import { extractCommonIds } from "../utils/common-ids"; +import { requireEnvVar } from "../utils/validation"; +import { ApiErrorDetail } from "../contracts/errors"; +import { processError } from "../mappers/error-mapper"; +import ValidationError from "../errors/validation-error"; +import { mapToGetLettersResponse } from "../mappers/letter-mapper"; +import type { Deps } from "../config/deps"; // The endpoint should only return pending letters for now -const status = 'PENDING'; - -export function createGetLettersHandler(deps: Deps): APIGatewayProxyHandler { - - return async (event) => { - - const commonHeadersResult = validateCommonHeaders(event.headers, deps); - - if (!commonHeadersResult.ok) { - return mapErrorToResponse(commonHeadersResult.error, commonHeadersResult.correlationId, deps.logger); - } - - try { - const maxLimit = getMaxLimit(deps); - - const limitNumber = getLimitOrDefault(event.queryStringParameters, maxLimit, deps.logger); - - const letters = await getLettersForSupplier( - commonHeadersResult.value.supplierId, - status, - limitNumber, - deps.letterRepo, - ); - - const response = mapToGetLettersResponse(letters); - - deps.logger.info({ - description: 'Pending letters successfully fetched', - supplierId: commonHeadersResult.value.supplierId, - limitNumber, - status, - lettersCount: letters.length - }); +const status = "PENDING"; - return { - statusCode: 200, - body: JSON.stringify(response, null, 2), - }; - } - catch (error) { - return mapErrorToResponse(error, commonHeadersResult.value.correlationId, deps.logger); - } - } -}; - -function getLimitOrDefault(queryStringParameters: APIGatewayProxyEventQueryStringParameters | null, maxLimit: number, logger: Logger) : number { - - validateLimitParamOnly(queryStringParameters, logger); - return getLimit(queryStringParameters?.limit, maxLimit, logger); -} - -function validateLimitParamOnly(queryStringParameters: APIGatewayProxyEventQueryStringParameters | null, logger: Logger) { +function validateLimitParamOnly( + queryStringParameters: APIGatewayProxyEventQueryStringParameters | null, + logger: Logger, +) { if ( queryStringParameters && - Object.keys(queryStringParameters).some( - (key) => key !== 'limit' - ) + Object.keys(queryStringParameters).some((key) => key !== "limit") ) { logger.info({ - description: 'Unexpected query parameter(s) present', - queryStringParameters: queryStringParameters, + description: "Unexpected query parameter(s) present", + queryStringParameters, }); throw new ValidationError(ApiErrorDetail.InvalidRequestLimitOnly); } } -function getLimit(limit: string | undefined, maxLimit: number, logger: Logger) { - let result; - if (limit) { - let limitParam = limit; - result = Number(limitParam); - assertIsNumber(result, logger); - assertLimitInRange(result, maxLimit, logger); - } else { - result = maxLimit; - } - return result; -} - function assertIsNumber(limitNumber: number, logger: Logger) { - if (isNaN(limitNumber)) { + if (Number.isNaN(limitNumber)) { logger.info({ - description: 'limit parameter is not a number', + description: "limit parameter is not a number", limitNumber, }); throw new ValidationError(ApiErrorDetail.InvalidRequestLimitNotANumber); } } -function assertLimitInRange(limitNumber: number, maxLimit: number, logger: Logger) { +function assertLimitInRange( + limitNumber: number, + maxLimit: number, + logger: Logger, +) { if (limitNumber <= 0 || limitNumber > maxLimit) { logger.info({ - description: 'Limit value is invalid', + description: "Limit value is invalid", limitNumber, }); - throw new ValidationError(ApiErrorDetail.InvalidRequestLimitNotInRange, { args: [maxLimit]}); + throw new ValidationError(ApiErrorDetail.InvalidRequestLimitNotInRange, { + args: [maxLimit], + }); } } -function getMaxLimit(deps: Deps): number{ - - if (deps.env.MAX_LIMIT == null) { - throw new Error('MAX_LIMIT is required for getLetters'); +function getLimit(limit: string | undefined, maxLimit: number, logger: Logger) { + let result; + if (limit) { + const limitParam = limit; + result = Number(limitParam); + assertIsNumber(result, logger); + assertLimitInRange(result, maxLimit, logger); + } else { + result = maxLimit; } + return result; +} + +function getLimitOrDefault( + queryStringParameters: APIGatewayProxyEventQueryStringParameters | null, + maxLimit: number, + logger: Logger, +): number { + validateLimitParamOnly(queryStringParameters, logger); + return getLimit(queryStringParameters?.limit, maxLimit, logger); +} + +export default function createGetLettersHandler( + deps: Deps, +): APIGatewayProxyHandler { + return async (event) => { + const commonIds = extractCommonIds( + event.headers, + event.requestContext, + deps, + ); + + if (!commonIds.ok) { + return processError( + commonIds.error, + commonIds.correlationId, + deps.logger, + ); + } - return deps.env.MAX_LIMIT; + try { + const maxLimit = requireEnvVar(deps.env, "MAX_LIMIT"); + + const limitNumber = getLimitOrDefault( + event.queryStringParameters, + maxLimit, + deps.logger, + ); + + const letters = await getLettersForSupplier( + commonIds.value.supplierId, + status, + limitNumber, + deps.letterRepo, + ); + + const response = mapToGetLettersResponse(letters); + + deps.logger.info({ + description: "Pending letters successfully fetched", + supplierId: commonIds.value.supplierId, + limitNumber, + status, + lettersCount: letters.length, + }); + + return { + statusCode: 200, + body: JSON.stringify(response, null, 2), + }; + } catch (error) { + return processError(error, commonIds.value.correlationId, deps.logger); + } + }; } diff --git a/lambdas/api-handler/src/handlers/get-status.ts b/lambdas/api-handler/src/handlers/get-status.ts new file mode 100644 index 00000000..f1973296 --- /dev/null +++ b/lambdas/api-handler/src/handlers/get-status.ts @@ -0,0 +1,39 @@ +import { APIGatewayProxyHandler } from "aws-lambda"; +import { ListBucketsCommand, S3Client } from "@aws-sdk/client-s3"; +import { Deps } from "../config/deps"; + +async function s3HealthCheck(s3Client: S3Client) { + const command: ListBucketsCommand = new ListBucketsCommand({ + MaxBuckets: 1, + }); + await s3Client.send(command); +} + +export default function createGetStatusHandler( + deps: Deps, +): APIGatewayProxyHandler { + return async (_) => { + try { + await deps.dbHealthcheck.check(); + await s3HealthCheck(deps.s3Client); + + deps.logger.info({ + description: "Healthcheck passed", + }); + + return { + statusCode: 200, + body: JSON.stringify({ code: 200 }, null, 2), + }; + } catch (error) { + deps.logger.error( + { err: error }, + "Status endpoint error, services not available", + ); + return { + statusCode: 500, + body: JSON.stringify({ code: 500 }, null, 2), + }; + } + }; +} diff --git a/lambdas/api-handler/src/handlers/letter-status-update.ts b/lambdas/api-handler/src/handlers/letter-status-update.ts new file mode 100644 index 00000000..6930db48 --- /dev/null +++ b/lambdas/api-handler/src/handlers/letter-status-update.ts @@ -0,0 +1,30 @@ +import { SQSEvent, SQSHandler } from "aws-lambda"; +import { LetterDto, LetterDtoSchema } from "../contracts/letters"; +import { Deps } from "../config/deps"; + +export default function createLetterStatusUpdateHandler( + deps: Deps, +): SQSHandler { + return async (event: SQSEvent) => { + const tasks = event.Records.map(async (message) => { + try { + const letterToUpdate: LetterDto = LetterDtoSchema.parse( + JSON.parse(message.body), + ); + await deps.letterRepo.updateLetterStatus(letterToUpdate); + } catch (error) { + deps.logger.error( + { + err: error, + messageId: message.messageId, + correlationId: message.messageAttributes.CorrelationId.stringValue, + messageBody: message.body, + }, + "Error processing letter status update", + ); + } + }); + + await Promise.all(tasks); + }; +} diff --git a/lambdas/api-handler/src/handlers/patch-letter.ts b/lambdas/api-handler/src/handlers/patch-letter.ts index e8442fae..1e854970 100644 --- a/lambdas/api-handler/src/handlers/patch-letter.ts +++ b/lambdas/api-handler/src/handlers/patch-letter.ts @@ -1,49 +1,85 @@ -import { APIGatewayProxyHandler } from 'aws-lambda'; -import { patchLetterStatus } from '../services/letter-operations'; -import { PatchLetterRequest, PatchLetterRequestSchema } from '../contracts/letters'; -import { ApiErrorDetail } from '../contracts/errors'; -import { ValidationError } from '../errors'; -import { mapErrorToResponse } from '../mappers/error-mapper'; -import { assertNotEmpty, validateCommonHeaders } from '../utils/validation'; -import { mapToLetterDto } from '../mappers/letter-mapper'; +import { APIGatewayProxyHandler } from "aws-lambda"; +import { enqueueLetterUpdateRequests } from "../services/letter-operations"; +import { + LetterDto, + PatchLetterRequest, + PatchLetterRequestSchema, +} from "../contracts/letters"; +import { ApiErrorDetail } from "../contracts/errors"; +import ValidationError from "../errors/validation-error"; +import { processError } from "../mappers/error-mapper"; +import { assertNotEmpty } from "../utils/validation"; +import { extractCommonIds } from "../utils/common-ids"; +import { mapPatchLetterToDto } from "../mappers/letter-mapper"; import type { Deps } from "../config/deps"; - -export function createPatchLetterHandler(deps: Deps): APIGatewayProxyHandler { - +export default function createPatchLetterHandler( + deps: Deps, +): APIGatewayProxyHandler { return async (event) => { + const commonIds = extractCommonIds( + event.headers, + event.requestContext, + deps, + ); - const commonHeadersResult = validateCommonHeaders(event.headers, deps); - - if (!commonHeadersResult.ok) { - return mapErrorToResponse(commonHeadersResult.error, commonHeadersResult.correlationId, deps.logger); + if (!commonIds.ok) { + return processError( + commonIds.error, + commonIds.correlationId, + deps.logger, + ); } try { - const letterId = assertNotEmpty( event.pathParameters?.id, - new ValidationError(ApiErrorDetail.InvalidRequestMissingLetterIdPathParameter)); - const body = assertNotEmpty(event.body, new ValidationError(ApiErrorDetail.InvalidRequestMissingBody)); + const letterId = assertNotEmpty( + event.pathParameters?.id, + new ValidationError( + ApiErrorDetail.InvalidRequestMissingLetterIdPathParameter, + ), + ); + const body = assertNotEmpty( + event.body, + new ValidationError(ApiErrorDetail.InvalidRequestMissingBody), + ); let patchLetterRequest: PatchLetterRequest; try { patchLetterRequest = PatchLetterRequestSchema.parse(JSON.parse(body)); } catch (error) { - if (error instanceof Error) { - throw new ValidationError(ApiErrorDetail.InvalidRequestBody, { cause: error}); - } - else throw error; + const typedError = + error instanceof Error + ? new ValidationError(ApiErrorDetail.InvalidRequestBody, { + cause: error, + }) + : error; + throw typedError; } - const updatedLetter = await patchLetterStatus(mapToLetterDto(patchLetterRequest, commonHeadersResult.value.supplierId), letterId, deps.letterRepo); + const letterToUpdate: LetterDto = mapPatchLetterToDto( + patchLetterRequest, + commonIds.value.supplierId, + ); + + if (letterToUpdate.id !== letterId) { + throw new ValidationError( + ApiErrorDetail.InvalidRequestLetterIdsMismatch, + ); + } + + await enqueueLetterUpdateRequests( + [letterToUpdate], + commonIds.value.correlationId, + deps, + ); return { - statusCode: 200, - body: JSON.stringify(updatedLetter, null, 2) + statusCode: 202, + body: "", }; - } catch (error) { - return mapErrorToResponse(error, commonHeadersResult.value.correlationId, deps.logger); + return processError(error, commonIds.value.correlationId, deps.logger); } }; -}; +} diff --git a/lambdas/api-handler/src/handlers/post-letters.ts b/lambdas/api-handler/src/handlers/post-letters.ts new file mode 100644 index 00000000..f9d73acd --- /dev/null +++ b/lambdas/api-handler/src/handlers/post-letters.ts @@ -0,0 +1,91 @@ +import { APIGatewayProxyHandler } from "aws-lambda"; +import type { Deps } from "../config/deps"; +import { ApiErrorDetail } from "../contracts/errors"; +import { + PostLettersRequest, + PostLettersRequestSchema, +} from "../contracts/letters"; +import ValidationError from "../errors/validation-error"; +import { processError } from "../mappers/error-mapper"; +import { mapPostLettersToDtoArray } from "../mappers/letter-mapper"; +import { enqueueLetterUpdateRequests } from "../services/letter-operations"; +import { extractCommonIds } from "../utils/common-ids"; +import { assertNotEmpty, requireEnvVar } from "../utils/validation"; + +function duplicateIdsExist(postLettersRequest: PostLettersRequest) { + const ids = postLettersRequest.data.map((item) => item.id); + return new Set(ids).size !== ids.length; +} + +export default function createPostLettersHandler( + deps: Deps, +): APIGatewayProxyHandler { + return async (event) => { + const commonIds = extractCommonIds( + event.headers, + event.requestContext, + deps, + ); + + if (!commonIds.ok) { + return processError( + commonIds.error, + commonIds.correlationId, + deps.logger, + ); + } + + const maxUpdateItems = requireEnvVar(deps.env, "MAX_LIMIT"); + requireEnvVar(deps.env, "QUEUE_URL"); + + try { + const body = assertNotEmpty( + event.body, + new ValidationError(ApiErrorDetail.InvalidRequestMissingBody), + ); + + let postLettersRequest: PostLettersRequest; + + try { + postLettersRequest = PostLettersRequestSchema.parse(JSON.parse(body)); + } catch (error) { + const typedError = + error instanceof Error + ? new ValidationError(ApiErrorDetail.InvalidRequestBody, { + cause: error, + }) + : error; + throw typedError; + } + + if (postLettersRequest.data.length > maxUpdateItems) { + throw new ValidationError( + ApiErrorDetail.InvalidRequestLettersToUpdate, + { args: [maxUpdateItems] }, + ); + } + + if (duplicateIdsExist(postLettersRequest)) { + throw new ValidationError( + ApiErrorDetail.InvalidRequestDuplicateLetterId, + ); + } + + await enqueueLetterUpdateRequests( + mapPostLettersToDtoArray( + postLettersRequest, + commonIds.value.supplierId, + ), + commonIds.value.correlationId, + deps, + ); + + return { + statusCode: 202, + body: "", + }; + } catch (error) { + return processError(error, commonIds.value.correlationId, deps.logger); + } + }; +} diff --git a/lambdas/api-handler/src/handlers/post-mi.ts b/lambdas/api-handler/src/handlers/post-mi.ts index 526f849b..74fc82e1 100644 --- a/lambdas/api-handler/src/handlers/post-mi.ts +++ b/lambdas/api-handler/src/handlers/post-mi.ts @@ -1,47 +1,64 @@ import { APIGatewayProxyHandler } from "aws-lambda"; -import { postMI as postMIOperation } from '../services/mi-operations'; +import postMIOperation from "../services/mi-operations"; import { ApiErrorDetail } from "../contracts/errors"; -import { ValidationError } from "../errors"; -import { mapErrorToResponse } from "../mappers/error-mapper"; -import { assertNotEmpty, validateCommonHeaders, validateIso8601Timestamp } from "../utils/validation"; +import ValidationError from "../errors/validation-error"; +import { processError } from "../mappers/error-mapper"; +import { assertNotEmpty, validateIso8601Timestamp } from "../utils/validation"; +import { extractCommonIds } from "../utils/common-ids"; import { PostMIRequest, PostMIRequestSchema } from "../contracts/mi"; import { mapToMI } from "../mappers/mi-mapper"; import { Deps } from "../config/deps"; -export function createPostMIHandler(deps: Deps): APIGatewayProxyHandler { - +export default function createPostMIHandler( + deps: Deps, +): APIGatewayProxyHandler { return async (event) => { + const commonIds = extractCommonIds( + event.headers, + event.requestContext, + deps, + ); - const commonHeadersResult = validateCommonHeaders(event.headers, deps); - - if (!commonHeadersResult.ok) { - return mapErrorToResponse(commonHeadersResult.error, commonHeadersResult.correlationId, deps.logger); + if (!commonIds.ok) { + return processError( + commonIds.error, + commonIds.correlationId, + deps.logger, + ); } try { - const body = assertNotEmpty(event.body, new ValidationError(ApiErrorDetail.InvalidRequestMissingBody)); + const body = assertNotEmpty( + event.body, + new ValidationError(ApiErrorDetail.InvalidRequestMissingBody), + ); let postMIRequest: PostMIRequest; try { postMIRequest = PostMIRequestSchema.parse(JSON.parse(body)); } catch (error) { - if (error instanceof Error) { - throw new ValidationError(ApiErrorDetail.InvalidRequestBody, { cause: error}); - } - else throw error; + const typedError = + error instanceof Error + ? new ValidationError(ApiErrorDetail.InvalidRequestBody, { + cause: error, + }) + : error; + throw typedError; } validateIso8601Timestamp(postMIRequest.data.attributes.timestamp); - const result = await postMIOperation(mapToMI(postMIRequest, commonHeadersResult.value.supplierId), deps.miRepo); + const result = await postMIOperation( + mapToMI(postMIRequest, commonIds.value.supplierId), + deps.miRepo, + ); return { statusCode: 201, - body: JSON.stringify(result, null, 2) + body: JSON.stringify(result, null, 2), }; - } catch (error) { - return mapErrorToResponse(error, commonHeadersResult.value.correlationId, deps.logger); + return processError(error, commonIds.value.correlationId, deps.logger); } - } -}; + }; +} diff --git a/lambdas/api-handler/src/index.ts b/lambdas/api-handler/src/index.ts index 38328703..3a006e48 100644 --- a/lambdas/api-handler/src/index.ts +++ b/lambdas/api-handler/src/index.ts @@ -1,9 +1,12 @@ import { createDependenciesContainer } from "./config/deps"; -import { createGetLetterHandler } from "./handlers/get-letter"; -import { createGetLetterDataHandler } from "./handlers/get-letter-data"; -import { createGetLettersHandler } from "./handlers/get-letters"; -import { createPatchLetterHandler } from "./handlers/patch-letter"; -import { createPostMIHandler } from "./handlers/post-mi"; +import createGetLetterHandler from "./handlers/get-letter"; +import createGetLetterDataHandler from "./handlers/get-letter-data"; +import createGetLettersHandler from "./handlers/get-letters"; +import createPatchLetterHandler from "./handlers/patch-letter"; +import createPostLettersHandler from "./handlers/post-letters"; +import createLetterStatusUpdateHandler from "./handlers/letter-status-update"; +import createPostMIHandler from "./handlers/post-mi"; +import createGetStatusHandler from "./handlers/get-status"; const container = createDependenciesContainer(); @@ -11,4 +14,8 @@ export const getLetter = createGetLetterHandler(container); export const getLetterData = createGetLetterDataHandler(container); export const getLetters = createGetLettersHandler(container); export const patchLetter = createPatchLetterHandler(container); +export const letterStatusUpdate = createLetterStatusUpdateHandler(container); +export const postLetters = createPostLettersHandler(container); + export const postMI = createPostMIHandler(container); +export const getStatus = createGetStatusHandler(container); diff --git a/lambdas/api-handler/src/mappers/__tests__/error-mapper.test.ts b/lambdas/api-handler/src/mappers/__tests__/error-mapper.test.ts index 5110e32b..3a41f7d7 100644 --- a/lambdas/api-handler/src/mappers/__tests__/error-mapper.test.ts +++ b/lambdas/api-handler/src/mappers/__tests__/error-mapper.test.ts @@ -1,94 +1,114 @@ -import { mapErrorToResponse } from "../error-mapper"; -import { ValidationError, NotFoundError } from "../../errors"; +import { Logger } from "pino"; +import { processError } from "../error-mapper"; +import NotFoundError from "../../errors/not-found-error"; +import ValidationError from "../../errors/validation-error"; import { ApiErrorDetail } from "../../contracts/errors"; -import { Logger } from 'pino'; -describe("mapErrorToResponse", () => { +describe("processError", () => { it("should map ValidationError to InvalidRequest response", () => { - const err = new ValidationError(ApiErrorDetail.InvalidRequestLetterIdsMismatch); + const err = new ValidationError( + ApiErrorDetail.InvalidRequestLetterIdsMismatch, + ); - const res = mapErrorToResponse(err, 'correlationId', { info: jest.fn(), error: jest.fn() } as unknown as Logger); + const res = processError(err, "correlationId", { + info: jest.fn(), + error: jest.fn(), + } as unknown as Logger); expect(res.statusCode).toEqual(400); expect(JSON.parse(res.body)).toEqual({ - "errors": [ + errors: [ { - "code": "NOTIFY_INVALID_REQUEST", - "detail": "The letter ID in the request body does not match the letter ID path parameter", - "id": "correlationId", - "links": { - "about": "https://digital.nhs.uk/developer/api-catalogue/nhs-notify-supplier" + code: "NOTIFY_INVALID_REQUEST", + detail: + "The letter ID in the request body does not match the letter ID path parameter", + id: "correlationId", + links: { + about: + "https://digital.nhs.uk/developer/api-catalogue/nhs-notify-supplier", }, - "status": "400", - "title": "Invalid request" - } - ] + status: "400", + title: "Invalid request", + }, + ], }); }); it("should map NotFoundError to NotFound response", () => { const err = new NotFoundError(ApiErrorDetail.NotFoundLetterId); - const res = mapErrorToResponse(err, undefined, { info: jest.fn(), error: jest.fn() } as unknown as Logger); + const res = processError(err, undefined, { + info: jest.fn(), + error: jest.fn(), + } as unknown as Logger); expect(res.statusCode).toEqual(404); expect(JSON.parse(res.body)).toEqual({ - "errors": [ + errors: [ { - "code": "NOTIFY_LETTER_NOT_FOUND", - "detail": "No resource found with that ID", - "id": expect.any(String), - "links": { - "about": "https://digital.nhs.uk/developer/api-catalogue/nhs-notify-supplier" + code: "NOTIFY_LETTER_NOT_FOUND", + detail: "No resource found with that ID", + id: expect.any(String), + links: { + about: + "https://digital.nhs.uk/developer/api-catalogue/nhs-notify-supplier", }, - "status": "404", - "title": "Not found" - } - ] + status: "404", + title: "Not found", + }, + ], }); }); it("should map generic Error to InternalServerError response", () => { - const err = new Error("Something broke"); + const err = new Error("Low level error message"); - const res = mapErrorToResponse(err, 'correlationId', { info: jest.fn(), error: jest.fn() } as unknown as Logger); + const res = processError(err, "correlationId", { + info: jest.fn(), + error: jest.fn(), + } as unknown as Logger); expect(res.statusCode).toEqual(500); expect(JSON.parse(res.body)).toEqual({ - "errors": [ + errors: [ { - "code": "NOTIFY_INTERNAL_SERVER_ERROR", - "detail": "Something broke", - "id": "correlationId", - "links": { - "about": "https://digital.nhs.uk/developer/api-catalogue/nhs-notify-supplier" + code: "NOTIFY_INTERNAL_SERVER_ERROR", + detail: "Unexpected error", + id: "correlationId", + links: { + about: + "https://digital.nhs.uk/developer/api-catalogue/nhs-notify-supplier", }, - "status": "500", - "title": "Internal server error" - } - ] + status: "500", + title: "Internal server error", + }, + ], }); }); it("should map unexpected non-error to InternalServerError response", () => { - const err = 12345; // not an Error + const err = 12_345; // not an Error - const res = mapErrorToResponse(err, 'correlationId', { info: jest.fn(), error: jest.fn() } as unknown as Logger); + const res = processError(err, "correlationId", { + info: jest.fn(), + error: jest.fn(), + } as unknown as Logger); expect(res.statusCode).toEqual(500); expect(JSON.parse(res.body)).toEqual({ - "errors": [ + errors: [ { - "code": "NOTIFY_INTERNAL_SERVER_ERROR", - "detail": "Unexpected error", - "id": "correlationId", - "links": { - "about": "https://digital.nhs.uk/developer/api-catalogue/nhs-notify-supplier" + code: "NOTIFY_INTERNAL_SERVER_ERROR", + detail: "Unexpected error", + id: "correlationId", + links: { + about: + "https://digital.nhs.uk/developer/api-catalogue/nhs-notify-supplier", }, - "status": "500", - "title": "Internal server error" - } - ] + status: "500", + title: "Internal server error", + }, + ], }); }); }); diff --git a/lambdas/api-handler/src/mappers/__tests__/letter-mapper.test.ts b/lambdas/api-handler/src/mappers/__tests__/letter-mapper.test.ts index f6f8e3dc..c19eb972 100644 --- a/lambdas/api-handler/src/mappers/__tests__/letter-mapper.test.ts +++ b/lambdas/api-handler/src/mappers/__tests__/letter-mapper.test.ts @@ -1,177 +1,187 @@ -import { mapToGetLetterResponse, mapToGetLettersResponse, mapToPatchLetterResponse } from '../letter-mapper'; -import { Letter } from '@internal/datastore'; -import { GetLetterResponse, GetLettersResponse, PatchLetterResponse } from '../../contracts/letters'; - -describe('letter-mapper', () => { - it('maps an internal Letter to a PatchLetterResponse', () => { +import { Letter } from "@internal/datastore"; +import { + mapToGetLetterResponse, + mapToGetLettersResponse, + mapToPatchLetterResponse, +} from "../letter-mapper"; +import { + GetLetterResponse, + GetLettersResponse, + PatchLetterResponse, +} from "../../contracts/letters"; + +describe("letter-mapper", () => { + it("maps an internal Letter to a PatchLetterResponse", () => { const letter: Letter = { - id: 'abc123', - status: 'PENDING', - supplierId: 'supplier1', - specificationId: 'spec123', - groupId: 'group123', - url: 'https://example.com/letter/abc123', + id: "abc123", + status: "PENDING", + supplierId: "supplier1", + specificationId: "spec123", + groupId: "group123", + url: "https://example.com/letter/abc123", createdAt: new Date().toISOString(), updatedAt: new Date().toISOString(), - supplierStatus: 'supplier1#PENDING', + supplierStatus: "supplier1#PENDING", supplierStatusSk: Date.now().toString(), - ttl: 123 + ttl: 123, }; const result: PatchLetterResponse = mapToPatchLetterResponse(letter); expect(result).toEqual({ data: { - id: 'abc123', - type: 'Letter', + id: "abc123", + type: "Letter", attributes: { - specificationId: 'spec123', - status: 'PENDING', - groupId: 'group123' - } - } + specificationId: "spec123", + status: "PENDING", + groupId: "group123", + }, + }, }); }); - it('maps an internal Letter to a PatchLetterResponse with reasonCode and reasonText when present', () => { + it("maps an internal Letter to a PatchLetterResponse with reasonCode and reasonText when present", () => { const letter: Letter = { - id: 'abc123', - status: 'PENDING', - supplierId: 'supplier1', - specificationId: 'spec123', - groupId: 'group123', - url: 'https://example.com/letter/abc123', + id: "abc123", + status: "PENDING", + supplierId: "supplier1", + specificationId: "spec123", + groupId: "group123", + url: "https://example.com/letter/abc123", createdAt: new Date().toISOString(), updatedAt: new Date().toISOString(), - supplierStatus: 'supplier1#PENDING', + supplierStatus: "supplier1#PENDING", supplierStatusSk: Date.now().toString(), ttl: 123, - reasonCode: 123, - reasonText: 'Reason text' + reasonCode: "R01", + reasonText: "Reason text", }; const result: PatchLetterResponse = mapToPatchLetterResponse(letter); expect(result).toEqual({ data: { - id: 'abc123', - type: 'Letter', + id: "abc123", + type: "Letter", attributes: { - specificationId: 'spec123', - status: 'PENDING', - groupId: 'group123', - reasonCode: 123, - reasonText: 'Reason text', - } - } + specificationId: "spec123", + status: "PENDING", + groupId: "group123", + reasonCode: "R01", + reasonText: "Reason text", + }, + }, }); }); - - it('maps an internal Letter to a GetLetterResponse', () => { + it("maps an internal Letter to a GetLetterResponse", () => { const letter: Letter = { - id: 'abc123', - status: 'PENDING', - supplierId: 'supplier1', - specificationId: 'spec123', - groupId: 'group123', - url: 'https://example.com/letter/abc123', + id: "abc123", + status: "PENDING", + supplierId: "supplier1", + specificationId: "spec123", + groupId: "group123", + url: "https://example.com/letter/abc123", createdAt: new Date().toISOString(), updatedAt: new Date().toISOString(), - supplierStatus: 'supplier1#PENDING', + supplierStatus: "supplier1#PENDING", supplierStatusSk: Date.now().toString(), - ttl: 123 + ttl: 123, }; const result: GetLetterResponse = mapToGetLetterResponse(letter); expect(result).toEqual({ data: { - id: 'abc123', - type: 'Letter', + id: "abc123", + type: "Letter", attributes: { - specificationId: 'spec123', - status: 'PENDING', - groupId: 'group123' - } - } + specificationId: "spec123", + status: "PENDING", + groupId: "group123", + }, + }, }); }); - it('maps an internal Letter to a GetLetterResponse with reasonCode and reasonText when present', () => { + it("maps an internal Letter to a GetLetterResponse with reasonCode and reasonText when present", () => { const letter: Letter = { - id: 'abc123', - status: 'PENDING', - supplierId: 'supplier1', - specificationId: 'spec123', - groupId: 'group123', - url: 'https://example.com/letter/abc123', + id: "abc123", + status: "PENDING", + supplierId: "supplier1", + specificationId: "spec123", + groupId: "group123", + url: "https://example.com/letter/abc123", createdAt: new Date().toISOString(), updatedAt: new Date().toISOString(), - supplierStatus: 'supplier1#PENDING', + supplierStatus: "supplier1#PENDING", supplierStatusSk: Date.now().toString(), ttl: 123, - reasonCode: 123, - reasonText: 'Reason text' + reasonCode: "R01", + reasonText: "Reason text", }; const result: GetLetterResponse = mapToGetLetterResponse(letter); expect(result).toEqual({ data: { - id: 'abc123', - type: 'Letter', + id: "abc123", + type: "Letter", attributes: { - specificationId: 'spec123', - status: 'PENDING', - groupId: 'group123', - reasonCode: 123, - reasonText: 'Reason text', - } - } + specificationId: "spec123", + status: "PENDING", + groupId: "group123", + reasonCode: "R01", + reasonText: "Reason text", + }, + }, }); }); - it('maps an internal Letter collection to a GetLettersResponse', () => { + it("maps an internal Letter collection to a GetLettersResponse", () => { const letter: Letter = { - id: 'abc123', - status: 'PENDING', - supplierId: 'supplier1', - specificationId: 'spec123', - groupId: 'group123', - url: 'https://example.com/letter/abc123', + id: "abc123", + status: "PENDING", + supplierId: "supplier1", + specificationId: "spec123", + groupId: "group123", + url: "https://example.com/letter/abc123", createdAt: new Date().toISOString(), updatedAt: new Date().toISOString(), - supplierStatus: 'supplier1#PENDING', + supplierStatus: "supplier1#PENDING", supplierStatusSk: Date.now().toString(), ttl: 123, - reasonCode: 123, - reasonText: 'Reason text' + reasonCode: "R01", + reasonText: "Reason text", }; - const result: GetLettersResponse = mapToGetLettersResponse([letter, letter]); + const result: GetLettersResponse = mapToGetLettersResponse([ + letter, + letter, + ]); expect(result).toEqual({ data: [ { - id: 'abc123', - type: 'Letter', + id: "abc123", + type: "Letter", attributes: { - specificationId: 'spec123', - status: 'PENDING', - groupId: 'group123' - } + specificationId: "spec123", + status: "PENDING", + groupId: "group123", + }, }, { - id: 'abc123', - type: 'Letter', + id: "abc123", + type: "Letter", attributes: { - specificationId: 'spec123', - status: 'PENDING', - groupId: 'group123' - } - } - ] + specificationId: "spec123", + status: "PENDING", + groupId: "group123", + }, + }, + ], }); }); }); diff --git a/lambdas/api-handler/src/mappers/__tests__/mi-mapper.test.ts b/lambdas/api-handler/src/mappers/__tests__/mi-mapper.test.ts index be1a55c1..10791ba5 100644 --- a/lambdas/api-handler/src/mappers/__tests__/mi-mapper.test.ts +++ b/lambdas/api-handler/src/mappers/__tests__/mi-mapper.test.ts @@ -1,62 +1,62 @@ -import { MIBase } from "../../../../../internal/datastore/src"; +import { MIBase } from "@internal/datastore/src"; import { IncomingMI, PostMIRequest } from "../../contracts/mi"; import { mapToMI, mapToPostMIResponse } from "../mi-mapper"; -describe ('mi-mapper', () => { - it('maps a PostMIRequest to an IncomingMI object', async () => { +describe("mi-mapper", () => { + it("maps a PostMIRequest to an IncomingMI object", async () => { const postMIRequest: PostMIRequest = { data: { - type: 'ManagementInformation', + type: "ManagementInformation", attributes: { - lineItem: 'envelope-business-standard', - timestamp: '2023-11-17T14:27:51.413Z', + lineItem: "envelope-business-standard", + timestamp: "2023-11-17T14:27:51.413Z", quantity: 22, - specificationId: 'spec1', - groupId: 'group1', - stockRemaining: 20000 - } - } + specificationId: "spec1", + groupId: "group1", + stockRemaining: 20_000, + }, + }, }; - const result: IncomingMI = mapToMI(postMIRequest, 'supplier1'); + const result: IncomingMI = mapToMI(postMIRequest, "supplier1"); expect(result).toEqual({ - lineItem: 'envelope-business-standard', - timestamp: '2023-11-17T14:27:51.413Z', + lineItem: "envelope-business-standard", + timestamp: "2023-11-17T14:27:51.413Z", quantity: 22, - specificationId: 'spec1', - groupId: 'group1', - stockRemaining: 20000, - supplierId: 'supplier1' + specificationId: "spec1", + groupId: "group1", + stockRemaining: 20_000, + supplierId: "supplier1", }); }); - it('maps an internal MIBase object to a PostMIResponse', async() => { + it("maps an internal MIBase object to a PostMIResponse", async () => { const mi: MIBase = { - id: 'id1', - lineItem: 'envelope-business-standard', - timestamp: '2023-11-17T14:27:51.413Z', + id: "id1", + lineItem: "envelope-business-standard", + timestamp: "2023-11-17T14:27:51.413Z", quantity: 22, - specificationId: 'spec1', - groupId: 'group1', - stockRemaining: 20000 + specificationId: "spec1", + groupId: "group1", + stockRemaining: 20_000, }; const result = mapToPostMIResponse(mi); expect(result).toEqual({ data: { - id: 'id1', - type: 'ManagementInformation', + id: "id1", + type: "ManagementInformation", attributes: { - lineItem: 'envelope-business-standard', - timestamp: '2023-11-17T14:27:51.413Z', + lineItem: "envelope-business-standard", + timestamp: "2023-11-17T14:27:51.413Z", quantity: 22, - specificationId: 'spec1', - groupId: 'group1', - stockRemaining: 20000 - } - } + specificationId: "spec1", + groupId: "group1", + stockRemaining: 20_000, + }, + }, }); }); }); diff --git a/lambdas/api-handler/src/mappers/error-mapper.ts b/lambdas/api-handler/src/mappers/error-mapper.ts index 99493234..408fe26c 100644 --- a/lambdas/api-handler/src/mappers/error-mapper.ts +++ b/lambdas/api-handler/src/mappers/error-mapper.ts @@ -1,60 +1,120 @@ import { APIGatewayProxyResult } from "aws-lambda"; -import { NotFoundError, ValidationError } from "../errors"; -import { buildApiError, ApiErrorCode, ApiErrorDetail, ApiErrorTitle, ApiError, ApiErrorStatus } from "../contracts/errors"; -import { v4 as uuid } from 'uuid'; +import { randomUUID } from "node:crypto"; import { Logger } from "pino"; +import NotFoundError from "../errors/not-found-error"; +import ValidationError from "../errors/validation-error"; +import { + ApiError, + ApiErrorCode, + ApiErrorStatus, + ApiErrorTitle, + ErrorResponse, + buildApiError, +} from "../contracts/errors"; -export interface ErrorResponse { - errors: ApiError[]; +function codeToTitle(code: ApiErrorCode): ApiErrorTitle { + switch (code) { + case ApiErrorCode.InvalidRequest: { + return ApiErrorTitle.InvalidRequest; + } + case ApiErrorCode.NotFound: { + return ApiErrorTitle.NotFound; + } + default: { + return ApiErrorTitle.InternalServerError; + } + } } -export function mapErrorToResponse(error: unknown, correlationId: string | undefined, logger: Logger): APIGatewayProxyResult { - if (error instanceof ValidationError) { - logger.info({ err: error }, `Validation error correlationId=${correlationId}`); - return buildResponseFromErrorCode(ApiErrorCode.InvalidRequest, error.detail, correlationId); - } else if (error instanceof NotFoundError) { - logger.info({ err: error }, `Not found error correlationId=${correlationId}`); - return buildResponseFromErrorCode(ApiErrorCode.NotFound, error.detail, correlationId); - } else if (error instanceof Error) { - logger.error({ err: error }, `Internal server error correlationId=${correlationId}`); - return buildResponseFromErrorCode(ApiErrorCode.InternalServerError, error.message, correlationId); - } else { - logger.error({ err: error }, `Internal server error (non-Error thrown) correlationId=${correlationId}`); - return buildResponseFromErrorCode(ApiErrorCode.InternalServerError, "Unexpected error", correlationId); +function codeToStatus(code: ApiErrorCode): ApiErrorStatus { + switch (code) { + case ApiErrorCode.InvalidRequest: { + return ApiErrorStatus.InvalidRequest; + } + case ApiErrorCode.NotFound: { + return ApiErrorStatus.NotFound; + } + default: { + return ApiErrorStatus.InternalServerError; + } } } -function buildResponseFromErrorCode(code: ApiErrorCode, detail: string, correlationId: string | undefined): APIGatewayProxyResult { - const id = correlationId ? correlationId : uuid(); - const responseError = buildApiError({ +function buildErrorResponseFromError(error: ApiError): ErrorResponse { + return { errors: [error] }; +} + +function mapToErrorResponse(apiError: ApiError) { + return { + statusCode: +apiError.status, + body: JSON.stringify(buildErrorResponseFromError(apiError), null, 2), + }; +} + +function mapToApiError( + code: ApiErrorCode, + detail: string, + correlationId: string | undefined, +): ApiError { + const id = correlationId || randomUUID(); + return buildApiError({ id, code, status: codeToStatus(code), title: codeToTitle(code), - detail + detail, }); - return { - statusCode: +responseError.status, - body: JSON.stringify(buildErrorResponseFromError(responseError), null, 2) - }; } -function codeToStatus(code: ApiErrorCode): ApiErrorStatus { - switch(code) { - case ApiErrorCode.InvalidRequest: return ApiErrorStatus.InvalidRequest; - case ApiErrorCode.NotFound: return ApiErrorStatus.NotFound; - default: return ApiErrorStatus.InternalServerError; +export function logAndMapToApiError( + error: unknown, + correlationId: string | undefined, + logger: Logger, +): ApiError { + if (error instanceof ValidationError) { + logger.info( + { err: error }, + `Validation error correlationId=${correlationId}`, + ); + return mapToApiError( + ApiErrorCode.InvalidRequest, + error.detail, + correlationId, + ); } -} - -function codeToTitle(code: ApiErrorCode): ApiErrorTitle { - switch(code) { - case ApiErrorCode.InvalidRequest: return ApiErrorTitle.InvalidRequest; - case ApiErrorCode.NotFound: return ApiErrorTitle.NotFound; - default: return ApiErrorTitle.InternalServerError; + if (error instanceof NotFoundError) { + logger.info( + { err: error }, + `Not found error correlationId=${correlationId}`, + ); + return mapToApiError(ApiErrorCode.NotFound, error.detail, correlationId); + } + if (error instanceof Error) { + logger.error( + { err: error }, + `Internal server error correlationId=${correlationId}`, + ); + return mapToApiError( + ApiErrorCode.InternalServerError, + "Unexpected error", + correlationId, + ); } + logger.error( + { err: error }, + `Internal server error (non-Error thrown) correlationId=${correlationId}`, + ); + return mapToApiError( + ApiErrorCode.InternalServerError, + "Unexpected error", + correlationId, + ); } -function buildErrorResponseFromError(error: ApiError): ErrorResponse { - return { errors: [error] }; +export function processError( + error: unknown, + correlationId: string | undefined, + logger: Logger, +): APIGatewayProxyResult { + return mapToErrorResponse(logAndMapToApiError(error, correlationId, logger)); } diff --git a/lambdas/api-handler/src/mappers/letter-mapper.ts b/lambdas/api-handler/src/mappers/letter-mapper.ts index e0f73b93..3b13f905 100644 --- a/lambdas/api-handler/src/mappers/letter-mapper.ts +++ b/lambdas/api-handler/src/mappers/letter-mapper.ts @@ -1,7 +1,47 @@ import { LetterBase, LetterStatus } from "@internal/datastore"; -import { GetLetterResponse, GetLetterResponseSchema, GetLettersResponse, GetLettersResponseSchema, LetterDto, PatchLetterRequest, PatchLetterResponse, PatchLetterResponseSchema } from '../contracts/letters'; +import { + GetLetterResponse, + GetLetterResponseSchema, + GetLettersResponse, + GetLettersResponseSchema, + LetterDto, + PatchLetterRequest, + PatchLetterResponse, + PatchLetterResponseSchema, + PostLettersRequest, + PostLettersRequestResource, +} from "../contracts/letters"; -export function mapToLetterDto(request: PatchLetterRequest, supplierId: string): LetterDto { +function letterToResourceResponse(letter: LetterBase) { + return { + id: letter.id, + type: "Letter", + attributes: { + status: letter.status, + specificationId: letter.specificationId, + groupId: letter.groupId, + ...(letter.reasonCode != null && { reasonCode: letter.reasonCode }), + ...(letter.reasonText != null && { reasonText: letter.reasonText }), + }, + }; +} + +function letterToGetLettersResourceResponse(letter: LetterBase) { + return { + id: letter.id, + type: "Letter", + attributes: { + status: letter.status, + specificationId: letter.specificationId, + groupId: letter.groupId, + }, + }; +} + +export function mapPatchLetterToDto( + request: PatchLetterRequest, + supplierId: string, +): LetterDto { return { id: request.data.id, supplierId, @@ -11,46 +51,37 @@ export function mapToLetterDto(request: PatchLetterRequest, supplierId: string): }; } -export function mapToPatchLetterResponse(letter: LetterBase): PatchLetterResponse { +export function mapPostLettersToDtoArray( + request: PostLettersRequest, + supplierId: string, +): LetterDto[] { + return request.data.map((letterToUpdate: PostLettersRequestResource) => ({ + id: letterToUpdate.id, + supplierId, + status: LetterStatus.parse(letterToUpdate.attributes.status), + reasonCode: letterToUpdate.attributes.reasonCode, + reasonText: letterToUpdate.attributes.reasonText, + })); +} + +export function mapToPatchLetterResponse( + letter: LetterBase, +): PatchLetterResponse { return PatchLetterResponseSchema.parse({ - data: letterToResourceResponse(letter) + data: letterToResourceResponse(letter), }); } -export function mapToGetLettersResponse(letters: LetterBase[]): GetLettersResponse { +export function mapToGetLettersResponse( + letters: LetterBase[], +): GetLettersResponse { return GetLettersResponseSchema.parse({ - data: letters.map(letterToGetLettersResourceResponse) + data: letters.map((letter) => letterToGetLettersResourceResponse(letter)), }); } export function mapToGetLetterResponse(letter: LetterBase): GetLetterResponse { return GetLetterResponseSchema.parse({ - data:letterToResourceResponse(letter) + data: letterToResourceResponse(letter), }); } - -function letterToResourceResponse(letter: LetterBase) { - return { - id: letter.id, - type: 'Letter', - attributes: { - status: letter.status, - specificationId: letter.specificationId, - groupId: letter.groupId, - ...(letter.reasonCode != null && { reasonCode: letter.reasonCode }), - ...(letter.reasonText != null && { reasonText: letter.reasonText }) - } - }; -}; - -function letterToGetLettersResourceResponse(letter: LetterBase) { - return { - id: letter.id, - type: 'Letter', - attributes: { - status: letter.status, - specificationId: letter.specificationId, - groupId: letter.groupId - } - }; -}; diff --git a/lambdas/api-handler/src/mappers/mi-mapper.ts b/lambdas/api-handler/src/mappers/mi-mapper.ts index 83848e1a..d20d19b6 100644 --- a/lambdas/api-handler/src/mappers/mi-mapper.ts +++ b/lambdas/api-handler/src/mappers/mi-mapper.ts @@ -1,10 +1,18 @@ -import { MIBase } from "../../../../internal/datastore/src"; -import { IncomingMI, PostMIRequest as PostMIRequest, PostMIResponse, PostMIResponseSchema } from "../contracts/mi"; +import { MIBase } from "@internal/datastore/src"; +import { + IncomingMI, + PostMIRequest, + PostMIResponse, + PostMIResponseSchema, +} from "../contracts/mi"; -export function mapToMI(request: PostMIRequest, supplierId: string): IncomingMI { - return { - supplierId: supplierId, - ...request.data.attributes +export function mapToMI( + request: PostMIRequest, + supplierId: string, +): IncomingMI { + return { + supplierId, + ...request.data.attributes, }; } @@ -12,15 +20,15 @@ export function mapToPostMIResponse(mi: MIBase): PostMIResponse { return PostMIResponseSchema.parse({ data: { id: mi.id, - type: 'ManagementInformation', + type: "ManagementInformation", attributes: { - lineItem: mi.lineItem, - timestamp: mi.timestamp, - quantity: mi.quantity, - specificationId: mi.specificationId, - groupId: mi.groupId, - stockRemaining: mi.stockRemaining - } - } + lineItem: mi.lineItem, + timestamp: mi.timestamp, + quantity: mi.quantity, + specificationId: mi.specificationId, + groupId: mi.groupId, + stockRemaining: mi.stockRemaining, + }, + }, }); } diff --git a/lambdas/api-handler/src/services/__tests__/letter-operations.test.ts b/lambdas/api-handler/src/services/__tests__/letter-operations.test.ts index 1016061c..550fbeca 100644 --- a/lambdas/api-handler/src/services/__tests__/letter-operations.test.ts +++ b/lambdas/api-handler/src/services/__tests__/letter-operations.test.ts @@ -1,24 +1,47 @@ -import { Letter, LetterRepository } from '@internal/datastore'; -import { Deps } from '../../config/deps'; -import { LetterDto } from '../../contracts/letters'; -import { getLetterById, getLetterDataUrl, getLettersForSupplier, patchLetterStatus } from '../letter-operations'; -import pino from 'pino'; - -jest.mock('@aws-sdk/s3-request-presigner', () => ({ +import { Letter, LetterRepository } from "@internal/datastore"; +import pino from "pino"; +import { getSignedUrl } from "@aws-sdk/s3-request-presigner"; +import { S3Client } from "@aws-sdk/client-s3"; +import { SQSClient } from "@aws-sdk/client-sqs"; +import { + enqueueLetterUpdateRequests, + getLetterById, + getLetterDataUrl, + getLettersForSupplier, +} from "../letter-operations"; +import { LetterDto } from "../../contracts/letters"; +import { Deps } from "../../config/deps"; + +jest.mock("@aws-sdk/s3-request-presigner", () => ({ getSignedUrl: jest.fn(), })); -import { getSignedUrl } from '@aws-sdk/s3-request-presigner'; -jest.mock('@aws-sdk/client-s3', () => { - const originalModule = jest.requireActual('@aws-sdk/client-s3'); +jest.mock("@aws-sdk/client-s3", () => { + jest.requireActual("@aws-sdk/client-s3"); return { GetObjectCommand: jest.fn().mockImplementation((input) => ({ input })), }; }); -import { S3Client, GetObjectCommand } from '@aws-sdk/client-s3'; -describe("getLetterIdsForSupplier", () => { +function makeLetter(id: string, status: Letter["status"]): Letter { + return { + id, + status, + supplierId: "supplier1", + specificationId: "spec123", + groupId: "group123", + url: `s3://letterDataBucket/${id}.pdf`, + createdAt: new Date().toISOString(), + updatedAt: new Date().toISOString(), + supplierStatus: `supplier1#${status}`, + supplierStatusSk: Date.now().toString(), + ttl: 123, + reasonCode: "R01", + reasonText: "Reason text", + }; +} +describe("getLetterIdsForSupplier", () => { beforeEach(() => { jest.clearAllMocks(); }); @@ -51,179 +74,256 @@ describe("getLetterIdsForSupplier", () => { }); describe("getLetterById", () => { - - const testLetter = { id: "id1", status: "PENDING", specificationId: "s1", groupId: "g1", }; + const testLetter = { + id: "id1", + status: "PENDING", + specificationId: "s1", + groupId: "g1", + }; it("returns letter from the repository", async () => { - const mockRepo = { getLetterById: jest.fn().mockResolvedValue(testLetter), }; - const result = await getLetterById( - "supplier1", - "id1", - mockRepo as any, - ); - - expect(mockRepo.getLetterById).toHaveBeenCalledWith( - "supplier1", - "id1", - ); - expect(result).toEqual({ id: 'id1', status: 'PENDING', specificationId: 's1', groupId: 'g1' }); - }); - - it('should throw notFoundError when letter does not exist', async () => { - const mockRepo = { - getLetterById: jest.fn().mockRejectedValue(new Error('Letter with id l1 not found for supplier s1')) - }; - - await expect(getLetterById('supplierid', 'letter1', mockRepo as any)).rejects.toThrow('No resource found with that ID'); - }); - - it('should throw unexpected error', async () => { - - const mockRepo = { - getLetterById: jest.fn().mockRejectedValue(new Error('unexpected error')) - }; - - await expect(getLetterById('supplierid', 'letter1', mockRepo as any)).rejects.toThrow("unexpected error"); - }); - -}); - -describe('patchLetterStatus function', () => { - - beforeEach(() => { - jest.clearAllMocks(); - }); - - const updatedLetterDto: LetterDto = { - id: 'letter1', - supplierId: 'supplier1', - status: 'REJECTED', - reasonCode: 123, - reasonText: 'Reason text' - }; - - const updatedLetter = makeLetter("letter1", "REJECTED"); - - it('should update the letter status successfully', async () => { - const mockRepo = { - updateLetterStatus: jest.fn().mockResolvedValue(updatedLetter) - }; - - const result = await patchLetterStatus(updatedLetterDto, 'letter1', mockRepo as any); + const result = await getLetterById("supplier1", "id1", mockRepo as any); + expect(mockRepo.getLetterById).toHaveBeenCalledWith("supplier1", "id1"); expect(result).toEqual({ - data: - { - id: 'letter1', - type: 'Letter', - attributes: { - status: 'REJECTED', - reasonCode: updatedLetter.reasonCode, - reasonText: updatedLetter.reasonText, - specificationId: updatedLetter.specificationId, - groupId: updatedLetter.groupId - }, - } + id: "id1", + status: "PENDING", + specificationId: "s1", + groupId: "g1", }); }); - it('should throw validationError when letterIds differ', async () => { - await expect(patchLetterStatus(updatedLetterDto, 'letter2', {} as any)).rejects.toThrow("The letter ID in the request body does not match the letter ID path parameter"); - }); - - it('should throw notFoundError when letter does not exist', async () => { + it("should throw notFoundError when letter does not exist", async () => { const mockRepo = { - updateLetterStatus: jest.fn().mockRejectedValue(new Error('Letter with id l1 not found for supplier s1')) + getLetterById: jest + .fn() + .mockRejectedValue( + new Error("Letter with id l1 not found for supplier s1"), + ), }; - await expect(patchLetterStatus(updatedLetterDto, 'letter1', mockRepo as any)).rejects.toThrow("No resource found with that ID"); + await expect( + getLetterById("supplierid", "letter1", mockRepo as any), + ).rejects.toThrow("No resource found with that ID"); }); - it('should throw unexpected error', async () => { - + it("should throw unexpected error", async () => { const mockRepo = { - updateLetterStatus: jest.fn().mockRejectedValue(new Error('unexpected error')) + getLetterById: jest.fn().mockRejectedValue(new Error("unexpected error")), }; - await expect(patchLetterStatus(updatedLetterDto, 'letter1', mockRepo as any)).rejects.toThrow("unexpected error"); + await expect( + getLetterById("supplierid", "letter1", mockRepo as any), + ).rejects.toThrow("unexpected error"); }); }); -describe('getLetterDataUrl function', () => { - +describe("getLetterDataUrl function", () => { beforeEach(() => { jest.clearAllMocks(); }); - const mockedGetSignedUrl = getSignedUrl as jest.MockedFunction; - const MockedGetObjectCommand = GetObjectCommand as unknown as jest.Mock; + const mockedGetSignedUrl = getSignedUrl as jest.MockedFunction< + typeof getSignedUrl + >; const updatedLetter = makeLetter("letter1", "REJECTED"); const s3Client = { send: jest.fn() } as unknown as S3Client; const letterRepo = { - getLetterById: jest.fn().mockResolvedValue(updatedLetter) + getLetterById: jest.fn().mockResolvedValue(updatedLetter), } as unknown as LetterRepository; - const logger = jest.fn() as unknown as pino.Logger;; + const logger = jest.fn() as unknown as pino.Logger; const env = { - LETTERS_TABLE_NAME: 'LettersTable', - LETTER_TTL_HOURS: 12960, - SUPPLIER_ID_HEADER: 'nhsd-supplier-id', - APIM_CORRELATION_HEADER: 'nhsd-correlation-id', - DOWNLOAD_URL_TTL_SECONDS: 60 + LETTERS_TABLE_NAME: "LettersTable", + LETTER_TTL_HOURS: 12_960, + SUPPLIER_ID_HEADER: "nhsd-supplier-id", + APIM_CORRELATION_HEADER: "nhsd-correlation-id", + DOWNLOAD_URL_TTL_SECONDS: 60, }; const deps: Deps = { s3Client, letterRepo, logger, env } as Deps; - it('should return pre signed url successfully', async () => { + it("should return pre signed url successfully", async () => { + mockedGetSignedUrl.mockResolvedValue("https://somePreSignedUrl.com"); - mockedGetSignedUrl.mockResolvedValue('http://somePreSignedUrl.com'); - - const result = await getLetterDataUrl('supplier1', 'letter1', deps); + const result = await getLetterDataUrl("supplier1", "letter1", deps); const expectedCommandInput = { - Bucket: 'letterDataBucket', - Key: 'letter1.pdf' + Bucket: "letterDataBucket", + Key: "letter1.pdf", }; - expect(mockedGetSignedUrl).toHaveBeenCalledWith(s3Client, { input: expectedCommandInput}, { expiresIn: 60}); - expect(result).toEqual('http://somePreSignedUrl.com'); + expect(mockedGetSignedUrl).toHaveBeenCalledWith( + s3Client, + { input: expectedCommandInput }, + { expiresIn: 60 }, + ); + expect(result).toEqual("https://somePreSignedUrl.com"); }); - it('should throw notFoundError when letter does not exist', async () => { + it("should throw notFoundError when letter does not exist", async () => { deps.letterRepo = { - getLetterById: jest.fn().mockRejectedValue(new Error('Letter with id l1 not found for supplier s1')) + getLetterById: jest + .fn() + .mockRejectedValue( + new Error("Letter with id l1 not found for supplier s1"), + ), } as unknown as LetterRepository; - await expect(getLetterDataUrl('supplier1', 'letter42', deps)).rejects.toThrow("No resource found with that ID"); + await expect( + getLetterDataUrl("supplier1", "letter42", deps), + ).rejects.toThrow("No resource found with that ID"); }); - it('should throw unexpected error', async () => { - + it("should throw unexpected error", async () => { deps.letterRepo = { - getLetterById: jest.fn().mockRejectedValue(new Error('unexpected error')) + getLetterById: jest.fn().mockRejectedValue(new Error("unexpected error")), } as unknown as LetterRepository; - await expect(getLetterDataUrl('supplier1', 'letter1', deps)).rejects.toThrow("unexpected error"); + await expect( + getLetterDataUrl("supplier1", "letter1", deps), + ).rejects.toThrow("unexpected error"); }); }); -function makeLetter(id: string, status: Letter['status']) : Letter { - return { - id, - status, - supplierId: 'supplier1', - specificationId: 'spec123', - groupId: 'group123', - url: `s3://letterDataBucket/${id}.pdf`, - createdAt: new Date().toISOString(), - updatedAt: new Date().toISOString(), - supplierStatus: `supplier1#${status}`, - supplierStatusSk: Date.now().toString(), - ttl: 123, - reasonCode: 123, - reasonText: "Reason text" - }; -} +describe("enqueueLetterUpdateRequests function", () => { + beforeEach(() => { + jest.clearAllMocks(); + }); + + const lettersToUpdate: LetterDto[] = [ + { + id: "id1", + status: "REJECTED", + supplierId: "s1", + specificationId: "spec1", + groupId: "g1", + reasonCode: "123", + reasonText: "Reason text", + }, + { + id: "id2", + status: "ACCEPTED", + supplierId: "s1", + }, + ]; + + it("should update the letter status successfully", async () => { + const sqsClient = { send: jest.fn() } as unknown as SQSClient; + const logger = { error: jest.fn() } as unknown as pino.Logger; + const env = { + QUEUE_URL: "sqsUrl", + }; + const deps: Deps = { sqsClient, logger, env } as Deps; + + const result = await enqueueLetterUpdateRequests( + lettersToUpdate, + "correlationId1", + deps, + ); + + expect(result).toBeUndefined(); + + expect(deps.sqsClient.send).toHaveBeenNthCalledWith( + 1, + expect.objectContaining({ + input: { + QueueUrl: deps.env.QUEUE_URL, + MessageAttributes: { + CorrelationId: { + DataType: "String", + StringValue: "correlationId1", + }, + }, + MessageBody: JSON.stringify({ + id: lettersToUpdate[0].id, + status: lettersToUpdate[0].status, + supplierId: lettersToUpdate[0].supplierId, + specificationId: lettersToUpdate[0].specificationId, + groupId: lettersToUpdate[0].groupId, + reasonCode: lettersToUpdate[0].reasonCode, + reasonText: lettersToUpdate[0].reasonText, + }), + }, + }), + ); + + expect(deps.sqsClient.send).toHaveBeenNthCalledWith( + 2, + expect.objectContaining({ + input: { + QueueUrl: deps.env.QUEUE_URL, + MessageAttributes: { + CorrelationId: { + DataType: "String", + StringValue: "correlationId1", + }, + }, + MessageBody: JSON.stringify({ + id: lettersToUpdate[1].id, + status: lettersToUpdate[1].status, + supplierId: lettersToUpdate[1].supplierId, + }), + }, + }), + ); + }); + + it("should log error if enqueueing fails", async () => { + const mockError = new Error("error"); + const sqsClient = { + send: jest + .fn() + .mockRejectedValueOnce(mockError) + .mockResolvedValueOnce({ MessageId: "m1" }), + } as unknown as SQSClient; + const logger = { error: jest.fn() } as unknown as pino.Logger; + const env = { + QUEUE_URL: "sqsUrl", + }; + const deps: Deps = { sqsClient, logger, env } as Deps; + + const result = await enqueueLetterUpdateRequests( + lettersToUpdate, + "correlationId1", + deps, + ); + + expect(result).toBeUndefined(); + + expect(deps.sqsClient.send).toHaveBeenNthCalledWith( + 2, + expect.objectContaining({ + input: { + QueueUrl: deps.env.QUEUE_URL, + MessageAttributes: { + CorrelationId: { + DataType: "String", + StringValue: "correlationId1", + }, + }, + MessageBody: JSON.stringify({ + id: lettersToUpdate[1].id, + status: lettersToUpdate[1].status, + supplierId: lettersToUpdate[1].supplierId, + }), + }, + }), + ); + + expect(deps.logger.error).toHaveBeenCalledTimes(1); + expect(deps.logger.error).toHaveBeenCalledWith( + { + err: mockError, + correlationId: "correlationId1", + letterId: lettersToUpdate[0].id, + letterStatus: lettersToUpdate[0].status, + supplierId: lettersToUpdate[0].supplierId, + }, + "Error enqueuing letter status update", + ); + }); +}); diff --git a/lambdas/api-handler/src/services/__tests__/mi-operations.test.ts b/lambdas/api-handler/src/services/__tests__/mi-operations.test.ts index 6dd245de..f831c406 100644 --- a/lambdas/api-handler/src/services/__tests__/mi-operations.test.ts +++ b/lambdas/api-handler/src/services/__tests__/mi-operations.test.ts @@ -1,40 +1,39 @@ import { IncomingMI } from "../../contracts/mi"; -import { postMI } from "../mi-operations"; - -describe('postMI function', () => { +import postMI from "../mi-operations"; +describe("postMI function", () => { const incomingMi: IncomingMI = { - lineItem: 'envelope-business-standard', - timestamp: '2023-11-17T14:27:51.413Z', - quantity: 22, - specificationId: 'spec1', - groupId: 'group1', - stockRemaining: 20000, - supplierId: 'supplier1' + lineItem: "envelope-business-standard", + timestamp: "2023-11-17T14:27:51.413Z", + quantity: 22, + specificationId: "spec1", + groupId: "group1", + stockRemaining: 20_000, + supplierId: "supplier1", }; - it('creates the MI in the repository', async () => { - const persistedMi = {id: 'id1', ...incomingMi}; + it("creates the MI in the repository", async () => { + const persistedMi = { id: "id1", ...incomingMi }; const mockRepo = { - putMI: jest.fn().mockResolvedValue(persistedMi) + putMI: jest.fn().mockResolvedValue(persistedMi), }; const result = await postMI(incomingMi, mockRepo as any); expect(result).toEqual({ data: { - id: 'id1', - type: 'ManagementInformation', + id: "id1", + type: "ManagementInformation", attributes: { - lineItem: 'envelope-business-standard', - timestamp: '2023-11-17T14:27:51.413Z', + lineItem: "envelope-business-standard", + timestamp: "2023-11-17T14:27:51.413Z", quantity: 22, - specificationId: 'spec1', - groupId: 'group1', - stockRemaining: 20000 - } - } + specificationId: "spec1", + groupId: "group1", + stockRemaining: 20_000, + }, + }, }); }); }); diff --git a/lambdas/api-handler/src/services/letter-operations.ts b/lambdas/api-handler/src/services/letter-operations.ts index 035aba2a..c94e76b5 100644 --- a/lambdas/api-handler/src/services/letter-operations.ts +++ b/lambdas/api-handler/src/services/letter-operations.ts @@ -1,20 +1,50 @@ -import { LetterBase, LetterRepository } from '@internal/datastore' -import { NotFoundError, ValidationError } from '../errors'; -import { LetterDto, PatchLetterResponse } from '../contracts/letters'; -import { mapToPatchLetterResponse } from '../mappers/letter-mapper'; -import { ApiErrorDetail } from '../contracts/errors'; -import { getSignedUrl } from '@aws-sdk/s3-request-presigner'; -import { S3Client, GetObjectCommand } from '@aws-sdk/client-s3'; -import { Deps } from '../config/deps'; +import { LetterBase, LetterRepository } from "@internal/datastore"; +import { getSignedUrl } from "@aws-sdk/s3-request-presigner"; +import { GetObjectCommand, S3Client } from "@aws-sdk/client-s3"; +import { SendMessageCommand } from "@aws-sdk/client-sqs"; +import NotFoundError from "../errors/not-found-error"; +import { LetterDto } from "../contracts/letters"; +import { ApiErrorDetail } from "../contracts/errors"; +import { Deps } from "../config/deps"; +function isNotFoundError(error: any) { + return ( + error instanceof Error && + /^Letter with id \w+ not found for supplier \w+$/.test(error.message) + ); +} -export const getLettersForSupplier = async (supplierId: string, status: string, limit: number, letterRepo: LetterRepository): Promise => { +async function getDownloadUrl( + s3Uri: string, + s3Client: S3Client, + expiry: number, +) { + const url = new URL(s3Uri); // works for s3:// URIs + const bucket = url.hostname; + const key = url.pathname.slice(1); // remove leading '/' - return await letterRepo.getLettersBySupplier(supplierId, status, limit); -} + const command = new GetObjectCommand({ + Bucket: bucket, + Key: key, + }); -export const getLetterById = async (supplierId: string, letterId: string, letterRepo: LetterRepository): Promise => { + return getSignedUrl(s3Client, command, { expiresIn: expiry }); +} +export const getLettersForSupplier = async ( + supplierId: string, + status: string, + limit: number, + letterRepo: LetterRepository, +): Promise => { + return letterRepo.getLettersBySupplier(supplierId, status, limit); +}; + +export const getLetterById = async ( + supplierId: string, + letterId: string, + letterRepo: LetterRepository, +): Promise => { let letter; try { @@ -27,56 +57,58 @@ export const getLetterById = async (supplierId: string, letterId: string, letter } return letter; -} - -export const patchLetterStatus = async (letterToUpdate: LetterDto, letterId: string, letterRepo: LetterRepository): Promise => { - - if (letterToUpdate.id !== letterId) { - throw new ValidationError(ApiErrorDetail.InvalidRequestLetterIdsMismatch); - } - - let updatedLetter; - - try { - updatedLetter = await letterRepo.updateLetterStatus(letterToUpdate); - } catch (error) { - if (isNotFoundError(error)) { - throw new NotFoundError(ApiErrorDetail.NotFoundLetterId); - } - throw error; - } - - return mapToPatchLetterResponse(updatedLetter); -} -function isNotFoundError(error: any) { - return error instanceof Error && /^Letter with id \w+ not found for supplier \w+$/.test(error.message); -} - -export const getLetterDataUrl = async (supplierId: string, letterId: string, deps: Deps): Promise => { +}; +export const getLetterDataUrl = async ( + supplierId: string, + letterId: string, + deps: Deps, +): Promise => { let letter; try { letter = await deps.letterRepo.getLetterById(supplierId, letterId); - return await getDownloadUrl(letter.url, deps.s3Client, deps.env.DOWNLOAD_URL_TTL_SECONDS); + return await getDownloadUrl( + letter.url, + deps.s3Client, + deps.env.DOWNLOAD_URL_TTL_SECONDS, + ); } catch (error) { - if (error instanceof Error && /^Letter with id \w+ not found for supplier \w+$/.test(error.message)) { + if (isNotFoundError(error)) { throw new NotFoundError(ApiErrorDetail.NotFoundLetterId); } throw error; } -} - -async function getDownloadUrl(s3Uri: string, s3Client: S3Client, expiry: number) { - - const url = new URL(s3Uri); // works for s3:// URIs - const bucket = url.hostname; - const key = url.pathname.slice(1); // remove leading '/' - - const command = new GetObjectCommand({ - Bucket: bucket, - Key: key, +}; + +export async function enqueueLetterUpdateRequests( + updateRequests: LetterDto[], + correlationId: string, + deps: Deps, +) { + const tasks = updateRequests.map(async (request: LetterDto) => { + try { + const command = new SendMessageCommand({ + QueueUrl: deps.env.QUEUE_URL, + MessageAttributes: { + CorrelationId: { DataType: "String", StringValue: correlationId }, + }, + MessageBody: JSON.stringify(request), + }); + await deps.sqsClient.send(command); + } catch (error) { + deps.logger.error( + { + err: error, + correlationId, + letterId: request.id, + letterStatus: request.status, + supplierId: request.supplierId, + }, + "Error enqueuing letter status update", + ); + } }); - return await getSignedUrl(s3Client, command, { expiresIn: expiry }); + await Promise.all(tasks); } diff --git a/lambdas/api-handler/src/services/mi-operations.ts b/lambdas/api-handler/src/services/mi-operations.ts index 2c574b67..a6b791e4 100644 --- a/lambdas/api-handler/src/services/mi-operations.ts +++ b/lambdas/api-handler/src/services/mi-operations.ts @@ -1,7 +1,12 @@ -import { MIRepository } from "../../../../internal/datastore/src/mi-repository"; +import { MIRepository } from "@internal/datastore/src/mi-repository"; import { IncomingMI, PostMIResponse } from "../contracts/mi"; import { mapToPostMIResponse } from "../mappers/mi-mapper"; -export const postMI = async (incomingMi: IncomingMI, miRepo: MIRepository): Promise => { +const postMI = async ( + incomingMi: IncomingMI, + miRepo: MIRepository, +): Promise => { return mapToPostMIResponse(await miRepo.putMI(incomingMi)); -} +}; + +export default postMI; diff --git a/lambdas/api-handler/src/utils/__tests__/common-ids.test.ts b/lambdas/api-handler/src/utils/__tests__/common-ids.test.ts new file mode 100644 index 00000000..53b2f78b --- /dev/null +++ b/lambdas/api-handler/src/utils/__tests__/common-ids.test.ts @@ -0,0 +1,113 @@ +import { APIGatewayProxyEvent } from "aws-lambda"; +import { extractCommonIds } from "../common-ids"; + +const mockDeps = { + env: { + APIM_CORRELATION_HEADER: "x-correlation-id", + SUPPLIER_ID_HEADER: "x-supplier-id", + }, +} as any; + +const mockContext = {} as APIGatewayProxyEvent["requestContext"]; + +describe("extractCommonIds", () => { + it("returns error if headers are missing", () => { + expect(extractCommonIds({}, mockContext, mockDeps)).toEqual({ + ok: false, + error: expect.any(Error), + }); + }); + + it("returns error if correlation id is missing", () => { + const headers = { "x-supplier-id": "SUP123", "x-request-id": "REQ123" }; + expect(extractCommonIds(headers, mockContext, mockDeps)).toEqual({ + ok: false, + error: expect.any(Error), + }); + }); + + it("returns error if request id is missing", () => { + const headers = { + "x-correlation-id": "CORR123", + "x-supplier-id": "SUP123", + }; + expect(extractCommonIds(headers, mockContext, mockDeps)).toEqual({ + ok: false, + error: expect.any(Error), + correlationId: "CORR123", + }); + }); + + it("returns error if supplier id is missing", () => { + const headers = { "x-correlation-id": "CORR123", "x-request-id": "REQ123" }; + expect(extractCommonIds(headers, mockContext, mockDeps)).toEqual({ + ok: false, + error: expect.any(Error), + correlationId: "CORR123", + }); + }); + + it("returns ok and ids if all present", () => { + const headers = { + "x-correlation-id": "CORR123", + "x-request-id": "REQ123", + "x-supplier-id": "SUP123", + }; + expect(extractCommonIds(headers, mockContext, mockDeps)).toEqual({ + ok: true, + value: { + correlationId: "CORR123", + supplierId: "SUP123", + }, + }); + }); + + it("handles mixed case header names", () => { + const headers = { + "X-Correlation-Id": "CORR123", + "X-Request-Id": "REQ123", + "X-Supplier-Id": "SUP123", + }; + expect(extractCommonIds(headers, mockContext, mockDeps)).toEqual({ + ok: true, + value: { + correlationId: "CORR123", + supplierId: "SUP123", + }, + }); + }); + + it("uses the supplier id from the authorizer if present", () => { + const headers = { + "x-correlation-id": "CORR123", + "x-supplier-id": "SUP123", + "x-request-id": "REQ123", + }; + const context = { + authorizer: { principalId: "SUP456" }, + } as unknown as APIGatewayProxyEvent["requestContext"]; + expect(extractCommonIds(headers, context, mockDeps)).toEqual({ + ok: true, + value: { + correlationId: "CORR123", + supplierId: "SUP456", + }, + }); + }); + + it("refuses to use the supplier id from the header if authorizer is present", () => { + const headers = { + "x-correlation-id": "CORR123", + "x-supplier-id": "SUP123", + "x-request-id": "REQ123", + }; + const context = { + authorizer: {}, + } as unknown as APIGatewayProxyEvent["requestContext"]; + expect(extractCommonIds(headers, context, mockDeps)).toEqual({ + ok: false, + error: expect.any(Error), + correlationId: "CORR123", + }); + }); +}); diff --git a/lambdas/api-handler/src/utils/__tests__/validation.test.ts b/lambdas/api-handler/src/utils/__tests__/validation.test.ts index 1a95330c..676defab 100644 --- a/lambdas/api-handler/src/utils/__tests__/validation.test.ts +++ b/lambdas/api-handler/src/utils/__tests__/validation.test.ts @@ -1,8 +1,12 @@ -import { ValidationError } from "../../errors"; -import { assertNotEmpty, lowerCaseKeys, validateIso8601Timestamp } from "../validation"; +import ValidationError from "../../errors/validation-error"; +import { + assertNotEmpty, + lowerCaseKeys, + validateIso8601Timestamp, +} from "../validation"; describe("assertNotEmpty", () => { - const error = new Error(); + const error = new Error("unexpected error"); it("throws for null", () => { expect(() => assertNotEmpty(null, error)).toThrow(Error); @@ -20,10 +24,9 @@ describe("assertNotEmpty", () => { expect(() => assertNotEmpty(" ", error)).toThrow(Error); }); - it("does not throw for empty array", () => { + it("does not throw for empty array", () => { const arr: any[] = []; expect(() => assertNotEmpty(arr, error)).toThrow(Error); - }); it("does not throw for empty object", () => { @@ -56,9 +59,9 @@ describe("assertNotEmpty", () => { describe("lowerCaseKeys", () => { it("lowers case on header keys", () => { - const headers: Record = {'Aa_Bb-Cc':1, 'b':2}; + const headers: Record = { "Aa_Bb-Cc": 1, b: 2 }; const result = lowerCaseKeys(headers); - expect(result).toEqual({'aa_bb-cc':1, 'b':2}); + expect(result).toEqual({ "aa_bb-cc": 1, b: 2 }); }); it("handles empty input", () => { @@ -67,15 +70,22 @@ describe("lowerCaseKeys", () => { }); }); -describe('validateIso8601Timestamp', () => { - it.each([['2025-10-16T00:00:00.000Z'], ['2025-10-16T00:00:00Z'], ['2025-10-16T00:00:00.0Z'], ['2025-10-16T00:00:00.999999Z']]) - ('permits valid timestamps', (timestamp: string) => { +describe("validateIso8601Timestamp", () => { + it.each([ + ["2025-10-16T00:00:00.000Z"], + ["2025-10-16T00:00:00Z"], + ["2025-10-16T00:00:00.0Z"], + ["2025-10-16T00:00:00.999999Z"], + ])("permits valid timestamps", (timestamp: string) => { validateIso8601Timestamp(timestamp); }); - it.each([['not a date string'], ['2025-10-16T00:00:00'], ['2025-16-10T00:00:00Z'], ['2025-02-31T00:00:00Z']]) - ('rejects invalid timestamps', (timestamp: string) => { + it.each([ + ["not a date string"], + ["2025-10-16T00:00:00"], + ["2025-16-10T00:00:00Z"], + ["2025-02-31T00:00:00Z"], + ])("rejects invalid timestamps", (timestamp: string) => { expect(() => validateIso8601Timestamp(timestamp)).toThrow(ValidationError); }); - }); diff --git a/lambdas/api-handler/src/utils/common-ids.ts b/lambdas/api-handler/src/utils/common-ids.ts new file mode 100644 index 00000000..d3db2c5c --- /dev/null +++ b/lambdas/api-handler/src/utils/common-ids.ts @@ -0,0 +1,53 @@ +import { APIGatewayProxyEvent, APIGatewayProxyEventHeaders } from "aws-lambda"; +import { Deps } from "../config/deps"; +import { lowerCaseKeys } from "./validation"; + +export function extractCommonIds( + headers: APIGatewayProxyEventHeaders, + context: APIGatewayProxyEvent["requestContext"], + deps: Deps, +): + | { ok: true; value: { correlationId: string; supplierId: string } } + | { ok: false; error: Error; correlationId?: string } { + if (!headers || Object.keys(headers).length === 0) { + return { ok: false, error: new Error("The request headers are empty") }; + } + + const lowerCasedHeaders = lowerCaseKeys(headers); + + const correlationId = lowerCasedHeaders[deps.env.APIM_CORRELATION_HEADER]; + if (!correlationId) { + return { + ok: false, + error: new Error( + "The request headers don't contain the APIM correlation id", + ), + }; + } + + const requestId = lowerCasedHeaders["x-request-id"]; + if (!requestId) { + return { + ok: false, + error: new Error("The request headers don't contain the x-request-id"), + correlationId, + }; + } + + // In normal API usage, we expect the authorizer to provide the supplier ID. When the lambda is invoked directly, for instance + // in the AWS console, then fall back to using the header. + + const supplierId = context.authorizer + ? context.authorizer.principalId + : lowerCasedHeaders[deps.env.SUPPLIER_ID_HEADER]; + + if (!supplierId) { + return { + ok: false, + error: new Error("The supplier ID is missing from the request"), + correlationId, + }; + } + + return { ok: true, value: { correlationId, supplierId } }; +} diff --git a/lambdas/api-handler/src/utils/validation.ts b/lambdas/api-handler/src/utils/validation.ts index f86b4fff..42e63ab5 100644 --- a/lambdas/api-handler/src/utils/validation.ts +++ b/lambdas/api-handler/src/utils/validation.ts @@ -1,21 +1,30 @@ -import { APIGatewayProxyEventHeaders } from 'aws-lambda'; -import { ValidationError } from '../errors'; -import { ApiErrorDetail } from '../contracts/errors'; -import { Deps } from '../config/deps'; +import ValidationError from "../errors/validation-error"; +import { ApiErrorDetail } from "../contracts/errors"; +import { EnvVars } from "../config/env"; + +function normalisePrecision([ + _, + mainPart, + fractionalPart = ".000", +]: string[]): string { + return fractionalPart.length < 4 + ? `${mainPart + fractionalPart + "0".repeat(4 - fractionalPart.length)}Z` + : `${mainPart + fractionalPart.slice(0, 4)}Z`; +} export function assertNotEmpty( value: T | null | undefined, - error: Error + error: Error, ): T { if (value == null) { throw error; } - if (typeof value === 'string' && value.trim() === '') { + if (typeof value === "string" && value.trim() === "") { throw error; } - if (typeof value === 'object' && Object.keys(value).length === 0) { + if (typeof value === "object" && Object.keys(value).length === 0) { throw error; } @@ -23,56 +32,15 @@ export function assertNotEmpty( } export function lowerCaseKeys(obj: Record): Record { - return Object.fromEntries(Object.entries(obj).map(([k, v]) => [k.toLowerCase(), v])); -} - -export function validateCommonHeaders(headers: APIGatewayProxyEventHeaders, deps: Deps -): { ok: true; value: {correlationId: string, supplierId: string } } | { ok: false; error: Error; correlationId?: string } { - - if (!headers || Object.keys(headers).length === 0) { - return { ok: false, error: new Error('The request headers are empty') }; - } - - const lowerCasedHeaders = lowerCaseKeys(headers); - - const correlationId = lowerCasedHeaders[deps.env.APIM_CORRELATION_HEADER]; - if (!correlationId) { - return { ok: false, error: new Error("The request headers don't contain the APIM correlation id") }; - } - - const requestId = lowerCasedHeaders['x-request-id']; - if (!requestId) { - return { - ok: false, - error: new Error("The request headers don't contain the x-request-id"), - correlationId - }; - } - - const supplierId = lowerCasedHeaders[deps.env.SUPPLIER_ID_HEADER]; - if (!supplierId) { - return { - ok: false, - error: new Error('The supplier ID is missing from the request'), - correlationId - }; - } - - return { ok: true, value: { correlationId, supplierId } }; + return Object.fromEntries( + Object.entries(obj).map(([k, v]) => [k.toLowerCase(), v]), + ); } export function validateIso8601Timestamp(timestamp: string) { - - function normalisePrecision([_, mainPart, fractionalPart='.000']: string[]) : string { - if (fractionalPart.length < 4) { - return mainPart + fractionalPart + '0'.repeat(4 - fractionalPart.length) + 'Z'; - } else { - return mainPart + fractionalPart.slice(0, 4) + 'Z'; - } - } - - const groups = timestamp.match(/(\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2})(.\d+)?Z/); - if (!groups) { + const regex = /(\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2})(.\d+)?Z/; + const groups = regex.exec(timestamp); + if (!groups) { throw new ValidationError(ApiErrorDetail.InvalidRequestTimestamp); } const date = new Date(timestamp); @@ -80,7 +48,21 @@ export function validateIso8601Timestamp(timestamp: string) { // An invalid day of month (e.g. '2025-02-31T00:00:00Z') will roll over into the following month, but we can // detect that by comparing date.toISOString() with the original timestamp string. We need to normalise the // original string to millisecond precision to make this work. - if (Number.isNaN(new Date(timestamp).valueOf()) || date.toISOString() != normalisePrecision(groups)) { - throw new ValidationError(ApiErrorDetail.InvalidRequestTimestamp); + if ( + Number.isNaN(new Date(timestamp).valueOf()) || + date.toISOString() !== normalisePrecision(groups) + ) { + throw new ValidationError(ApiErrorDetail.InvalidRequestTimestamp); } } + +export function requireEnvVar( + envs: EnvVars, + name: T, +): NonNullable { + if (!(name in envs)) { + throw new Error(`Missing required environment variable: ${name}`); + } + + return envs[name] as NonNullable; +} diff --git a/lambdas/api-handler/tsconfig.json b/lambdas/api-handler/tsconfig.json index 24902365..528c0c8b 100644 --- a/lambdas/api-handler/tsconfig.json +++ b/lambdas/api-handler/tsconfig.json @@ -1,5 +1,10 @@ { - "compilerOptions": {}, + "compilerOptions": { + "types": [ + "jest", + "node" + ] + }, "extends": "../../tsconfig.base.json", "include": [ "src/**/*", diff --git a/lambdas/authorizer/jest.config.ts b/lambdas/authorizer/jest.config.ts index d30f4cd1..f68246e6 100644 --- a/lambdas/authorizer/jest.config.ts +++ b/lambdas/authorizer/jest.config.ts @@ -1,7 +1,11 @@ -import type { Config } from 'jest'; - -export const baseJestConfig: Config = { - preset: 'ts-jest', +export const baseJestConfig = { + preset: "ts-jest", + extensionsToTreatAsEsm: [".ts"], + transform: { + "^.+\\.ts$": ["ts-jest", { + useESM: true + }] + }, // Automatically clear mock calls, instances, contexts and results before every test clearMocks: true, @@ -10,10 +14,10 @@ export const baseJestConfig: Config = { collectCoverage: true, // The directory where Jest should output its coverage files - coverageDirectory: './.reports/unit/coverage', + coverageDirectory: "./.reports/unit/coverage", // Indicates which provider should be used to instrument code for coverage - coverageProvider: 'babel', + coverageProvider: "babel", coverageThreshold: { global: { @@ -24,36 +28,35 @@ export const baseJestConfig: Config = { }, }, - coveragePathIgnorePatterns: ['/__tests__/'], - transform: { '^.+\\.ts$': 'ts-jest' }, - testPathIgnorePatterns: ['.build'], - testMatch: ['**/?(*.)+(spec|test).[jt]s?(x)'], + coveragePathIgnorePatterns: ["/__tests__/"], + testPathIgnorePatterns: [".build"], + testMatch: ["**/?(*.)+(spec|test).[jt]s?(x)"], // Use this configuration option to add custom reporters to Jest reporters: [ - 'default', + "default", [ - 'jest-html-reporter', + "jest-html-reporter", { - pageTitle: 'Test Report', - outputPath: './.reports/unit/test-report.html', + pageTitle: "Test Report", + outputPath: "./.reports/unit/test-report.html", includeFailureMsg: true, }, ], ], // The test environment that will be used for testing - testEnvironment: 'jsdom', + testEnvironment: "jsdom", }; const utilsJestConfig = { ...baseJestConfig, - testEnvironment: 'node', + testEnvironment: "node", coveragePathIgnorePatterns: [ ...(baseJestConfig.coveragePathIgnorePatterns ?? []), - 'zod-validators.ts', + "zod-validators.ts", ], }; diff --git a/lambdas/authorizer/package.json b/lambdas/authorizer/package.json index 026072f7..76c674c5 100644 --- a/lambdas/authorizer/package.json +++ b/lambdas/authorizer/package.json @@ -1,6 +1,11 @@ { "dependencies": { - "esbuild": "^0.25.11" + "@aws-sdk/client-dynamodb": "^3.858.0", + "@aws-sdk/lib-dynamodb": "^3.858.0", + "@internal/datastore": "*", + "esbuild": "^0.25.11", + "pino": "^9.7.0", + "zod": "^4.1.11" }, "devDependencies": { "@tsconfig/node22": "^22.0.2", @@ -8,7 +13,8 @@ "@types/jest": "^30.0.0", "jest": "^30.2.0", "jest-mock-extended": "^4.0.0", - "typescript": "^5.8.3" + "ts-jest": "^29.4.0", + "typescript": "^5.9.3" }, "name": "nhs-notify-supplier-authorizer", "private": true, diff --git a/lambdas/authorizer/src/__tests__/index.test.ts b/lambdas/authorizer/src/__tests__/index.test.ts index 71bde62d..e7567c70 100644 --- a/lambdas/authorizer/src/__tests__/index.test.ts +++ b/lambdas/authorizer/src/__tests__/index.test.ts @@ -1,104 +1,285 @@ -import { APIGatewayRequestAuthorizerEvent, Callback, Context } from 'aws-lambda'; -import { handler } from '../index'; +import { + APIGatewayEventClientCertificate, + APIGatewayRequestAuthorizerEvent, + Callback, + Context, +} from "aws-lambda"; +import pino from "pino"; +import { Deps } from "../deps"; +import { EnvVars } from "../env"; +import createAuthorizerHandler from "../authorizer"; -describe('Authorizer Lambda Function', () => { +const mockedDeps: jest.Mocked = { + logger: { info: jest.fn(), error: jest.fn() } as unknown as pino.Logger, + env: { + CLOUDWATCH_NAMESPACE: "cloudwatch-namespace", + CLIENT_CERTIFICATE_EXPIRATION_ALERT_DAYS: 14, + APIM_SUPPLIER_ID_HEADER: "NHSD-Supplier-ID", + } as unknown as EnvVars, + supplierRepo: { + getSupplierByApimId: jest.fn(), + } as any, +} as Deps; + +const buildCertWithExpiry = ( + expiry: string, +): APIGatewayEventClientCertificate => { + return { + subjectDN: "CN=test-subject", + validity: { + notAfter: expiry, + } as APIGatewayEventClientCertificate["validity"], + } as APIGatewayEventClientCertificate; +}; + +describe("Authorizer Lambda Function", () => { let mockEvent: APIGatewayRequestAuthorizerEvent; let mockContext: Context; - let mockCallback: jest.MockedFunction>; + let mockCallback: jest.MockedFunction; beforeEach(() => { mockEvent = { - type: 'REQUEST', - methodArn: 'arn:aws:execute-api:region:account-id:api-id/stage/GET/resource', + type: "REQUEST", + methodArn: + "arn:aws:execute-api:region:account-id:api-id/stage/GET/resource", headers: {}, - pathParameters: {} + pathParameters: {}, + requestContext: { identity: { clientCert: null } }, } as APIGatewayRequestAuthorizerEvent; mockContext = {} as Context; mockCallback = jest.fn(); }); - it('Should allow access when headers match', () => { - mockEvent.headers = { headerauth1: 'headervalue1' }; + describe("Certificate expiry check", () => { + beforeEach(() => { + jest + .useFakeTimers({ doNotFake: ["nextTick"] }) + .setSystemTime(new Date("2025-11-03T14:19:00Z")); + }); - handler(mockEvent, mockContext, mockCallback); + afterEach(() => { + jest.useRealTimers(); + }); - expect(mockCallback).toHaveBeenCalledWith(null, expect.objectContaining({ - policyDocument: expect.objectContaining({ - Statement: expect.arrayContaining([ - expect.objectContaining({ - Effect: 'Allow', - }), - ]), - }), - })); + it("Should not log CloudWatch metric when certificate is null", async () => { + mockEvent.requestContext.identity.clientCert = null; + + const handler = createAuthorizerHandler(mockedDeps); + handler(mockEvent, mockContext, mockCallback); + await new Promise(process.nextTick); + + const mockedInfo = mockedDeps.logger.info as jest.Mock; + expect(mockedInfo.mock.calls).not.toContainEqual( + expect.stringContaining("CloudWatchMetrics"), + ); + }); + + it("Should log CloudWatch metric when the certificate expiry threshold is reached", async () => { + mockEvent.requestContext.identity.clientCert = buildCertWithExpiry( + "2025-11-17T14:19:00Z", + ); + + const handler = createAuthorizerHandler(mockedDeps); + handler(mockEvent, mockContext, mockCallback); + await new Promise(process.nextTick); + + const mockedInfo = mockedDeps.logger.info as jest.Mock; + expect(mockedInfo.mock.calls.map((call) => call[0])).toContain( + JSON.stringify({ + _aws: { + Timestamp: 1_762_179_540_000, + CloudWatchMetrics: [ + { + Namespace: "cloudwatch-namespace", + Dimensions: ["SUBJECT_DN", "NOT_AFTER"], + Metrics: [ + { + Name: "apim-client-certificate-near-expiry", + Unit: "Count", + Value: 1, + }, + ], + }, + ], + }, + SUBJECT_DN: "CN=test-subject", + NOT_AFTER: "2025-11-17T14:19:00Z", + "apim-client-certificate-near-expiry": 1, + }), + ); + }); + + it("Should not log CloudWatch metric when the certificate expiry threshold is not yet reached", async () => { + mockEvent.requestContext.identity.clientCert = buildCertWithExpiry( + "2025-11-18T14:19:00Z", + ); + + const handler = createAuthorizerHandler(mockedDeps); + handler(mockEvent, mockContext, mockCallback); + await new Promise(process.nextTick); + + const mockedInfo = mockedDeps.logger.info as jest.Mock; + expect(mockedInfo.mock.calls).not.toContainEqual( + expect.stringContaining("CloudWatchMetrics"), + ); + }); }); - it('Should deny access when headers do not match', () => { - mockEvent.headers = { headerauth1: 'wrongValue' }; + describe("Supplier ID lookup", () => { + it("Should deny the request when no headers are present", async () => { + mockEvent.headers = null; - handler(mockEvent, mockContext, mockCallback); + const handler = createAuthorizerHandler(mockedDeps); + handler(mockEvent, mockContext, mockCallback); + await new Promise(process.nextTick); - expect(mockCallback).toHaveBeenCalledWith(null, expect.objectContaining({ - policyDocument: expect.objectContaining({ - Statement: expect.arrayContaining([ - expect.objectContaining({ - Effect: 'Deny', + expect(mockCallback).toHaveBeenCalledWith( + null, + expect.objectContaining({ + policyDocument: expect.objectContaining({ + Statement: [ + expect.objectContaining({ + Effect: "Deny", + }), + ], }), - ]), - }), - })); - }); + }), + ); + }); - it('Should handle null headers gracefully', () => { - mockEvent.headers = null; + it("Should deny the request when the Supplier ID header is absent", async () => { + mockEvent.headers = { "x-apim-correlation-id": "correlation-id" }; - handler(mockEvent, mockContext, mockCallback); + const handler = createAuthorizerHandler(mockedDeps); + handler(mockEvent, mockContext, mockCallback); + await new Promise(process.nextTick); - expect(mockCallback).toHaveBeenCalledWith(null, expect.objectContaining({ - policyDocument: expect.objectContaining({ - Statement: expect.arrayContaining([ - expect.objectContaining({ - Effect: 'Deny', + expect(mockCallback).toHaveBeenCalledWith( + null, + expect.objectContaining({ + policyDocument: expect.objectContaining({ + Statement: [ + expect.objectContaining({ + Effect: "Deny", + }), + ], }), - ]), - }), - })); - }); + }), + ); + }); - it('Should handle defined headers correctly', () => { - mockEvent.headers = { headerauth1: 'headervalue1' }; + it("Should deny the request when no supplier ID is found", async () => { + mockEvent.headers = { "NHSD-Supplier-ID": "unknown-apim-id" }; + ( + mockedDeps.supplierRepo.getSupplierByApimId as jest.Mock + ).mockRejectedValue(new Error("Supplier not found")); - handler(mockEvent, mockContext, mockCallback); + const handler = createAuthorizerHandler(mockedDeps); + handler(mockEvent, mockContext, mockCallback); + await new Promise(process.nextTick); - expect(mockCallback).toHaveBeenCalledWith(null, expect.objectContaining({ - policyDocument: expect.objectContaining({ - Statement: expect.arrayContaining([ - expect.objectContaining({ - Effect: 'Allow', + expect(mockCallback).toHaveBeenCalledWith( + null, + expect.objectContaining({ + policyDocument: expect.objectContaining({ + Statement: [ + expect.objectContaining({ + Effect: "Deny", + }), + ], }), - ]), - }), - })); + }), + ); + }); + + it("Should allow the request when the supplier ID is found", async () => { + mockEvent.headers = { "NHSD-Supplier-ID": "valid-apim-id" }; + ( + mockedDeps.supplierRepo.getSupplierByApimId as jest.Mock + ).mockResolvedValue({ + id: "supplier-123", + apimApplicationId: "valid-apim-id", + name: "Test Supplier", + status: "ENABLED", + }); + + const handler = createAuthorizerHandler(mockedDeps); + handler(mockEvent, mockContext, mockCallback); + await new Promise(process.nextTick); + + expect(mockCallback).toHaveBeenCalledWith( + null, + expect.objectContaining({ + policyDocument: expect.objectContaining({ + Statement: [ + expect.objectContaining({ + Effect: "Allow", + }), + ], + }), + principalId: "supplier-123", + }), + ); + }); + + it("Should allow the request when the supplier ID key case mismatches", async () => { + mockEvent.headers = { "nhsd-supplier-id": "Valid-Apim-Id" }; + ( + mockedDeps.supplierRepo.getSupplierByApimId as jest.Mock + ).mockResolvedValue({ + id: "supplier-123", + apimApplicationId: "valid-apim-id", + name: "Test Supplier", + status: "ENABLED", + }); + + const handler = createAuthorizerHandler(mockedDeps); + handler(mockEvent, mockContext, mockCallback); + await new Promise(process.nextTick); + + expect(mockCallback).toHaveBeenCalledWith( + null, + expect.objectContaining({ + policyDocument: expect.objectContaining({ + Statement: [ + expect.objectContaining({ + Effect: "Allow", + }), + ], + }), + principalId: "supplier-123", + }), + ); + }); }); - it('Should handle additional headers correctly', () => { - mockEvent.headers = { - headerauth1: 'headervalue1' , - otherheader1: 'headervalue2', - otherheader2: 'headervalue3' - }; + it("Should deny the request the supplier is disabled", async () => { + mockEvent.headers = { "NHSD-Supplier-ID": "unknown-apim-id" }; + ( + mockedDeps.supplierRepo.getSupplierByApimId as jest.Mock + ).mockResolvedValue({ + id: "supplier-123", + apimApplicationId: "valid-apim-id", + name: "Test Supplier", + status: "DISABLED", + }); + const handler = createAuthorizerHandler(mockedDeps); handler(mockEvent, mockContext, mockCallback); + await new Promise(process.nextTick); - expect(mockCallback).toHaveBeenCalledWith(null, expect.objectContaining({ - policyDocument: expect.objectContaining({ - Statement: expect.arrayContaining([ - expect.objectContaining({ - Effect: 'Allow', - }), - ]), + expect(mockCallback).toHaveBeenCalledWith( + null, + expect.objectContaining({ + policyDocument: expect.objectContaining({ + Statement: [ + expect.objectContaining({ + Effect: "Deny", + }), + ], + }), }), - })); + ); }); }); diff --git a/lambdas/authorizer/src/authorizer.ts b/lambdas/authorizer/src/authorizer.ts new file mode 100644 index 00000000..73c8fef5 --- /dev/null +++ b/lambdas/authorizer/src/authorizer.ts @@ -0,0 +1,149 @@ +import { + APIGatewayAuthorizerResult, + APIGatewayEventClientCertificate, + APIGatewayRequestAuthorizerEvent, + APIGatewayRequestAuthorizerEventHeaders, + APIGatewayRequestAuthorizerHandler, + Callback, + Context, +} from "aws-lambda"; +import { Supplier } from "@internal/datastore"; +import { Deps } from "./deps"; + +export default function createAuthorizerHandler( + deps: Deps, +): APIGatewayRequestAuthorizerHandler { + return ( + event: APIGatewayRequestAuthorizerEvent, + context: Context, + callback: Callback, + ): void => { + deps.logger.info(event, "Received event"); + + checkCertificateExpiry(event.requestContext.identity.clientCert, deps); + + getSupplier(event.headers, deps) + .then((supplier: Supplier) => { + deps.logger.info("Allow event"); + callback(null, generateAllow(event.methodArn, supplier.id)); + }) + .catch((error) => { + deps.logger.info(error, "Deny event"); + callback(null, generateDeny(event.methodArn)); + }); + }; +} + +async function getSupplier( + headers: APIGatewayRequestAuthorizerEventHeaders | null, + deps: Deps, +): Promise { + const apimId = Object.entries(headers || {}).find( + ([headerName, _]) => + headerName.toLowerCase() === + deps.env.APIM_SUPPLIER_ID_HEADER.toLowerCase(), + )?.[1] as string; + + if (!apimId) { + throw new Error("No APIM application ID found in header"); + } + const supplier = await deps.supplierRepo.getSupplierByApimId(apimId); + if (supplier.status === "DISABLED") { + throw new Error(`Supplier ${supplier.id} is disabled`); + } + return supplier; +} + +function generatePolicy( + principalId: string, + effect: "Allow" | "Deny", + resource: string, +): APIGatewayAuthorizerResult { + const authResponse: APIGatewayAuthorizerResult = { + principalId, + policyDocument: { + Version: "2012-10-17", + Statement: [ + { + Action: "execute-api:Invoke", + Effect: effect, + Resource: resource, + }, + ], + }, + }; + return authResponse; +} + +function generateAllow( + resource: string, + supplierId: string, +): APIGatewayAuthorizerResult { + return generatePolicy(supplierId, "Allow", resource); +} + +function generateDeny(resource: string): APIGatewayAuthorizerResult { + return generatePolicy("invalid-user", "Deny", resource); +} + +function getCertificateExpiryInDays( + certificate: APIGatewayEventClientCertificate, +): number { + const now = Date.now(); + const expiry = new Date(certificate.validity.notAfter).getTime(); + return (expiry - now) / (1000 * 60 * 60 * 24); +} + +function buildCloudWatchMetric( + namespace: string, + certificate: APIGatewayEventClientCertificate, +) { + return { + _aws: { + Timestamp: Date.now(), + CloudWatchMetrics: [ + { + Namespace: namespace, + Dimensions: ["SUBJECT_DN", "NOT_AFTER"], + Metrics: [ + { + Name: "apim-client-certificate-near-expiry", + Unit: "Count", + Value: 1, + }, + ], + }, + ], + }, + SUBJECT_DN: certificate.subjectDN, + NOT_AFTER: certificate.validity.notAfter, + "apim-client-certificate-near-expiry": 1, + }; +} + +async function checkCertificateExpiry( + certificate: APIGatewayEventClientCertificate | null, + deps: Deps, +): Promise { + deps.logger.info({ + description: "Client certificate details", + issuerDN: certificate?.issuerDN, + subjectDN: certificate?.subjectDN, + validity: certificate?.validity, + }); + + if (!certificate) { + // In a real production environment, we won't have got this far if there wasn't a cert + return; + } + + const expiry = getCertificateExpiryInDays(certificate); + + if (expiry <= deps.env.CLIENT_CERTIFICATE_EXPIRATION_ALERT_DAYS) { + deps.logger.info( + JSON.stringify( + buildCloudWatchMetric(deps.env.CLOUDWATCH_NAMESPACE, certificate), + ), + ); + } +} diff --git a/lambdas/authorizer/src/deps.ts b/lambdas/authorizer/src/deps.ts new file mode 100644 index 00000000..9f2ce0ed --- /dev/null +++ b/lambdas/authorizer/src/deps.ts @@ -0,0 +1,42 @@ +import { DynamoDBClient } from "@aws-sdk/client-dynamodb"; +import { DynamoDBDocumentClient } from "@aws-sdk/lib-dynamodb"; +import pino from "pino"; +import { SupplierRepository } from "@internal/datastore"; +import { EnvVars, envVars } from "./env"; + +export type Deps = { + supplierRepo: SupplierRepository; + logger: pino.Logger; + env: EnvVars; +}; + +function createDocumentClient(): DynamoDBDocumentClient { + const ddbClient = new DynamoDBClient({}); + return DynamoDBDocumentClient.from(ddbClient); +} + +function createSupplierRepository( + documentClient: DynamoDBDocumentClient, + log: pino.Logger, + suppliersTableName: string, +): SupplierRepository { + const config = { + suppliersTableName, + }; + + return new SupplierRepository(documentClient, log, config); +} + +export function createDependenciesContainer(): Deps { + const log = pino(); + + return { + supplierRepo: createSupplierRepository( + createDocumentClient(), + log, + envVars.SUPPLIERS_TABLE_NAME, + ), + logger: log, + env: envVars, + }; +} diff --git a/lambdas/authorizer/src/env.ts b/lambdas/authorizer/src/env.ts new file mode 100644 index 00000000..40a56917 --- /dev/null +++ b/lambdas/authorizer/src/env.ts @@ -0,0 +1,12 @@ +import { z } from "zod"; + +const EnvVarsSchema = z.object({ + SUPPLIERS_TABLE_NAME: z.string(), + CLOUDWATCH_NAMESPACE: z.string(), + APIM_SUPPLIER_ID_HEADER: z.string(), + CLIENT_CERTIFICATE_EXPIRATION_ALERT_DAYS: z.coerce.number().int(), +}); + +export type EnvVars = z.infer; + +export const envVars = EnvVarsSchema.parse(process.env); diff --git a/lambdas/authorizer/src/index.ts b/lambdas/authorizer/src/index.ts index 9797d5d2..e5ef1f0e 100644 --- a/lambdas/authorizer/src/index.ts +++ b/lambdas/authorizer/src/index.ts @@ -1,74 +1,6 @@ -// A simple request-based authorizer example to demonstrate how to use request -// parameters to allow or deny a request. In this example, a request is -// authorized if the client-supplied HeaderAuth1 header and stage variable of StageVar1 -// both match specified values of 'headerValue1' and 'stageValue1', respectively. -// -// Example curl request (replace and as appropriate): -// -// curl -H "HeaderAuth1: headerValue1" \ -// "//your-resource" -// +import createAuthorizerHandler from "./authorizer"; +import { createDependenciesContainer } from "./deps"; -// See https://docs.aws.amazon.com/apigateway/latest/developerguide/apigateway-use-lambda-authorizer.html for the original JS documentation +const container = createDependenciesContainer(); -import { APIGatewayAuthorizerResult, APIGatewayRequestAuthorizerEvent, Callback, Context } from 'aws-lambda'; -import pino from 'pino'; - -export const handler = ( - event: APIGatewayRequestAuthorizerEvent, - context: Context, - callback: Callback, - log = pino() -): void => { - log.info(event, 'Received event'); - - const headers = event.headers || {}; - - // Perform authorization to return the Allow policy for correct parameters and - // the 'Unauthorized' error, otherwise. - if ( - headers['headerauth1'] === 'headervalue1' - ) { - log.info('Allow event'); - callback(null, generateAllow('me', event.methodArn)); - } else { - log.info('Deny event'); - callback(null, generateDeny('me', event.methodArn)); - } -}; - -// Helper function to generate an IAM policy -function generatePolicy( - principalId: string, - effect: 'Allow' | 'Deny', - resource: string -): APIGatewayAuthorizerResult { - // Required output: - const authResponse: APIGatewayAuthorizerResult = { - principalId, - policyDocument: { - Version: '2012-10-17', - Statement: [ - { - Action: 'execute-api:Invoke', - Effect: effect, - Resource: resource, - }, - ], - }, - context: { - stringKey: 'stringval', - numberKey: 123, - booleanKey: true, - }, - }; - return authResponse; -} - -function generateAllow(principalId: string, resource: string): APIGatewayAuthorizerResult { - return generatePolicy(principalId, 'Allow', resource); -} - -function generateDeny(principalId: string, resource: string): APIGatewayAuthorizerResult { - return generatePolicy(principalId, 'Deny', resource); -} +export const handler = createAuthorizerHandler(container); diff --git a/lambdas/authorizer/tsconfig.json b/lambdas/authorizer/tsconfig.json index ea37d696..528c0c8b 100644 --- a/lambdas/authorizer/tsconfig.json +++ b/lambdas/authorizer/tsconfig.json @@ -1,5 +1,11 @@ { - "extends": "@tsconfig/node22/tsconfig.json", + "compilerOptions": { + "types": [ + "jest", + "node" + ] + }, + "extends": "../../tsconfig.base.json", "include": [ "src/**/*", "jest.config.ts" diff --git a/scripts/test-data/.eslintignore b/lambdas/letter-updates-transformer/.eslintignore similarity index 100% rename from scripts/test-data/.eslintignore rename to lambdas/letter-updates-transformer/.eslintignore diff --git a/scripts/test-data/.gitignore b/lambdas/letter-updates-transformer/.gitignore similarity index 100% rename from scripts/test-data/.gitignore rename to lambdas/letter-updates-transformer/.gitignore diff --git a/lambdas/letter-updates-transformer/jest.config.ts b/lambdas/letter-updates-transformer/jest.config.ts new file mode 100644 index 00000000..f68246e6 --- /dev/null +++ b/lambdas/letter-updates-transformer/jest.config.ts @@ -0,0 +1,63 @@ +export const baseJestConfig = { + preset: "ts-jest", + extensionsToTreatAsEsm: [".ts"], + transform: { + "^.+\\.ts$": ["ts-jest", { + useESM: true + }] + }, + + // Automatically clear mock calls, instances, contexts and results before every test + clearMocks: true, + + // Indicates whether the coverage information should be collected while executing the test + collectCoverage: true, + + // The directory where Jest should output its coverage files + coverageDirectory: "./.reports/unit/coverage", + + // Indicates which provider should be used to instrument code for coverage + coverageProvider: "babel", + + coverageThreshold: { + global: { + branches: 100, + functions: 100, + lines: 100, + statements: -10, + }, + }, + + coveragePathIgnorePatterns: ["/__tests__/"], + testPathIgnorePatterns: [".build"], + testMatch: ["**/?(*.)+(spec|test).[jt]s?(x)"], + + // Use this configuration option to add custom reporters to Jest + reporters: [ + "default", + [ + "jest-html-reporter", + { + pageTitle: "Test Report", + outputPath: "./.reports/unit/test-report.html", + includeFailureMsg: true, + }, + ], + ], + + // The test environment that will be used for testing + testEnvironment: "jsdom", +}; + +const utilsJestConfig = { + ...baseJestConfig, + + testEnvironment: "node", + + coveragePathIgnorePatterns: [ + ...(baseJestConfig.coveragePathIgnorePatterns ?? []), + "zod-validators.ts", + ], +}; + +export default utilsJestConfig; diff --git a/lambdas/letter-updates-transformer/package.json b/lambdas/letter-updates-transformer/package.json new file mode 100644 index 00000000..3b136cfc --- /dev/null +++ b/lambdas/letter-updates-transformer/package.json @@ -0,0 +1,32 @@ +{ + "dependencies": { + "@aws-sdk/client-sns": "^3.943.0", + "@aws-sdk/util-dynamodb": "^3.943.0", + "@internal/datastore": "^0.1.0", + "@internal/helpers": "^0.1.0", + "@nhsdigital/nhs-notify-event-schemas-supplier-api": "*", + "aws-lambda": "^1.0.7", + "esbuild": "^0.24.0", + "pino": "^10.1.0", + "zod": "^4.1.13" + }, + "devDependencies": { + "@tsconfig/node22": "^22.0.2", + "@types/aws-lambda": "^8.10.148", + "@types/jest": "^30.0.0", + "jest": "^30.2.0", + "jest-mock-extended": "^4.0.0", + "ts-jest": "^29.4.0", + "typescript": "^5.8.3" + }, + "name": "nhs-notify-supplier-api-letter-updates-transformer", + "private": true, + "scripts": { + "lambda-build": "rm -rf dist && npx esbuild --bundle --minify --sourcemap --target=es2020 --platform=node --loader:.node=file --entry-names=[name] --outdir=dist src/index.ts", + "lint": "eslint .", + "lint:fix": "eslint . --fix", + "test:unit": "jest", + "typecheck": "tsc --noEmit" + }, + "version": "0.0.1" +} diff --git a/lambdas/letter-updates-transformer/src/__tests__/letter-updates-transformer.test.ts b/lambdas/letter-updates-transformer/src/__tests__/letter-updates-transformer.test.ts new file mode 100644 index 00000000..17c271a0 --- /dev/null +++ b/lambdas/letter-updates-transformer/src/__tests__/letter-updates-transformer.test.ts @@ -0,0 +1,312 @@ +import { SNSClient } from "@aws-sdk/client-sns"; +import * as pino from "pino"; +import { + Context, + DynamoDBRecord, + KinesisStreamEvent, + KinesisStreamRecordPayload, +} from "aws-lambda"; +import { mockDeep } from "jest-mock-extended"; +import { LetterBase } from "@internal/datastore"; +import createHandler from "../letter-updates-transformer"; +import { Deps } from "../deps"; +import { EnvVars } from "../env"; +import mapLetterToCloudEvent from "../mappers/letter-mapper"; +import { LetterStatus } from "../../../api-handler/src/contracts/letters"; +import { LetterForEventPub } from "../types"; + +// Make crypto return consistent values, since we"re calling it in both prod and test code and comparing the values +const realCrypto = jest.requireActual("crypto"); +const randomBytes: Record = { + "8": realCrypto.randomBytes(8), + "16": realCrypto.randomBytes(16), +}; +jest.mock("crypto", () => ({ + randomUUID: () => "4616b2d9-b7a5-45aa-8523-fa7419626b69", + randomBytes: (size: number) => randomBytes[String(size)], +})); + +describe("letter-updates-transformer Lambda", () => { + const mockedDeps: jest.Mocked = { + snsClient: { send: jest.fn() } as unknown as SNSClient, + logger: { info: jest.fn(), error: jest.fn() } as unknown as pino.Logger, + env: { + EVENTPUB_SNS_TOPIC_ARN: "arn:aws:sns:region:account:topic", + } as unknown as EnvVars, + } as Deps; + + beforeEach(() => { + jest.useFakeTimers(); + }); + + afterEach(() => { + jest.useRealTimers(); + }); + + describe("filtering", () => { + it("processes status changes and publishes them to SNS", async () => { + const handler = createHandler(mockedDeps); + const oldLetter = generateLetter("ACCEPTED"); + const newLetter = generateLetter("PRINTED"); + const expectedEntries = [ + expect.objectContaining({ + Message: JSON.stringify(mapLetterToCloudEvent(newLetter)), + }), + ]; + + const testData = generateKinesisEvent([ + generateModifyRecord(oldLetter, newLetter), + ]); + await handler(testData, mockDeep(), jest.fn()); + + expect(mockedDeps.snsClient.send).toHaveBeenCalledWith( + expect.objectContaining({ + input: expect.objectContaining({ + TopicArn: "arn:aws:sns:region:account:topic", + PublishBatchRequestEntries: expectedEntries, + }), + }), + ); + }); + + it("publishes an event if a reason code is added", async () => { + const handler = createHandler(mockedDeps); + const oldLetter = generateLetter("ACCEPTED"); + const newLetter = generateLetter("ACCEPTED"); + newLetter.reasonCode = "R1"; + const expectedEntries = [ + expect.objectContaining({ + Message: JSON.stringify(mapLetterToCloudEvent(newLetter)), + }), + ]; + + const testData = generateKinesisEvent([ + generateModifyRecord(oldLetter, newLetter), + ]); + await handler(testData, mockDeep(), jest.fn()); + + expect(mockedDeps.snsClient.send).toHaveBeenCalledWith( + expect.objectContaining({ + input: expect.objectContaining({ + TopicArn: "arn:aws:sns:region:account:topic", + PublishBatchRequestEntries: expectedEntries, + }), + }), + ); + }); + + it("publishes an event if a reason code is changed", async () => { + const handler = createHandler(mockedDeps); + const oldLetter = generateLetter("ACCEPTED"); + const newLetter = generateLetter("ACCEPTED"); + oldLetter.reasonCode = "R1"; + newLetter.reasonCode = "R2"; + const expectedEntries = [ + expect.objectContaining({ + Message: JSON.stringify(mapLetterToCloudEvent(newLetter)), + }), + ]; + + const testData = generateKinesisEvent([ + generateModifyRecord(oldLetter, newLetter), + ]); + await handler(testData, mockDeep(), jest.fn()); + + expect(mockedDeps.snsClient.send).toHaveBeenCalledWith( + expect.objectContaining({ + input: expect.objectContaining({ + TopicArn: "arn:aws:sns:region:account:topic", + PublishBatchRequestEntries: expectedEntries, + }), + }), + ); + }); + + it("does not publish an event if neither status nor reason code changed", async () => { + const handler = createHandler(mockedDeps); + const oldLetter = generateLetter("ACCEPTED"); + const newLetter = generateLetter("ACCEPTED"); + + const testData = generateKinesisEvent([ + generateModifyRecord(oldLetter, newLetter), + ]); + await handler(testData, mockDeep(), jest.fn()); + + expect(mockedDeps.snsClient.send).not.toHaveBeenCalled(); + }); + + it("does not publish non-modify events", async () => { + const handler = createHandler(mockedDeps); + const newLetter = generateLetter("ACCEPTED"); + + const testData = generateKinesisEvent([generateInsertRecord(newLetter)]); + await handler(testData, mockDeep(), jest.fn()); + + expect(mockedDeps.snsClient.send).not.toHaveBeenCalled(); + }); + + it("does not publish invalid letter data", async () => { + const handler = createHandler(mockedDeps); + const oldLetter = generateLetter("ACCEPTED"); + const newLetter = { id: oldLetter.id } as LetterForEventPub; + + const testData = generateKinesisEvent([ + generateModifyRecord(oldLetter, newLetter), + ]); + await expect( + handler(testData, mockDeep(), jest.fn()), + ).rejects.toThrow(); + + expect(mockedDeps.snsClient.send).not.toHaveBeenCalled(); + }); + }); + + describe("Batching", () => { + it("batches mutiple records into a single call to SNS", async () => { + const handler = createHandler(mockedDeps); + const oldLetters = generateLetters(10, "ACCEPTED"); + const newLetters = generateLetters(10, "PRINTED"); + const expectedEntries = newLetters.map((letter) => + expect.objectContaining({ + Message: JSON.stringify(mapLetterToCloudEvent(letter)), + }), + ); + + const testData = generateKinesisEvent( + oldLetters.map((oldLetter, i) => + generateModifyRecord(oldLetter, newLetters[i]), + ), + ); + await handler(testData, mockDeep(), jest.fn()); + + expect(mockedDeps.snsClient.send).toHaveBeenCalledWith( + expect.objectContaining({ + input: expect.objectContaining({ + TopicArn: "arn:aws:sns:region:account:topic", + PublishBatchRequestEntries: expectedEntries, + }), + }), + ); + }); + + it("respects SNS's maximumum batch size of 10", async () => { + const handler = createHandler(mockedDeps); + const oldLetters = generateLetters(21, "ACCEPTED"); + const newLetters = generateLetters(21, "PRINTED"); + const expectedEntries = [ + newLetters.slice(0, 10).map((letter, index) => + expect.objectContaining({ + Id: expect.stringMatching(new RegExp(`-${index}$`)), + Message: JSON.stringify(mapLetterToCloudEvent(letter)), + }), + ), + newLetters.slice(10, 20).map((letter, index) => + expect.objectContaining({ + Id: expect.stringMatching(new RegExp(`-${index}$`)), + Message: JSON.stringify(mapLetterToCloudEvent(letter)), + }), + ), + newLetters.slice(20).map((letter, index) => + expect.objectContaining({ + Id: expect.stringMatching(new RegExp(`-${index}$`)), + Message: JSON.stringify(mapLetterToCloudEvent(letter)), + }), + ), + ]; + + const testData = generateKinesisEvent( + oldLetters.map((oldLetter, i) => + generateModifyRecord(oldLetter, newLetters[i]), + ), + ); + await handler(testData, mockDeep(), jest.fn()); + + expect(mockedDeps.snsClient.send).toHaveBeenNthCalledWith( + 1, + expect.objectContaining({ + input: expect.objectContaining({ + TopicArn: "arn:aws:sns:region:account:topic", + PublishBatchRequestEntries: expectedEntries[0], + }), + }), + ); + expect(mockedDeps.snsClient.send).toHaveBeenNthCalledWith( + 2, + expect.objectContaining({ + input: expect.objectContaining({ + TopicArn: "arn:aws:sns:region:account:topic", + PublishBatchRequestEntries: expectedEntries[1], + }), + }), + ); + expect(mockedDeps.snsClient.send).toHaveBeenNthCalledWith( + 3, + expect.objectContaining({ + input: expect.objectContaining({ + TopicArn: "arn:aws:sns:region:account:topic", + PublishBatchRequestEntries: expectedEntries[2], + }), + }), + ); + }); + }); +}); + +function generateLetter(status: LetterStatus, id?: string): LetterForEventPub { + return { + id: id || "1", + status, + specificationId: "spec1", + supplierId: "supplier1", + groupId: "group1", + updatedAt: "2025-12-10T11:13:54Z", + }; +} + +function generateLetters( + numLetters: number, + status: LetterStatus, +): LetterForEventPub[] { + const letters: LetterForEventPub[] = Array.from({ length: numLetters }); + for (let i = 0; i < numLetters; i++) { + letters[i] = generateLetter(status, String(i + 1)); + } + return letters; +} + +function generateModifyRecord( + oldLetter: LetterForEventPub, + newLetter: LetterForEventPub, +): DynamoDBRecord { + const oldImage = Object.fromEntries( + Object.entries(oldLetter).map(([key, value]) => [key, { S: value }]), + ); + const newImage = Object.fromEntries( + Object.entries(newLetter).map(([key, value]) => [key, { S: value }]), + ); + return { + eventName: "MODIFY", + dynamodb: { OldImage: oldImage, NewImage: newImage }, + }; +} + +function generateInsertRecord(newLetter: LetterBase): DynamoDBRecord { + const newImage = Object.fromEntries( + Object.entries(newLetter).map(([key, value]) => [key, { S: value }]), + ); + return { + eventName: "INSERT", + dynamodb: { NewImage: newImage }, + }; +} + +function generateKinesisEvent(letterEvents: object[]): KinesisStreamEvent { + const records = letterEvents + .map((letter) => Buffer.from(JSON.stringify(letter)).toString("base64")) + .map( + (data) => + ({ kinesis: { data } }) as unknown as KinesisStreamRecordPayload, + ); + + return { Records: records } as unknown as KinesisStreamEvent; +} diff --git a/lambdas/letter-updates-transformer/src/deps.ts b/lambdas/letter-updates-transformer/src/deps.ts new file mode 100644 index 00000000..9332410e --- /dev/null +++ b/lambdas/letter-updates-transformer/src/deps.ts @@ -0,0 +1,23 @@ +import pino from "pino"; +import { SNSClient } from "@aws-sdk/client-sns"; +import { EnvVars, envVars } from "./env"; + +export type Deps = { + snsClient: SNSClient; + logger: pino.Logger; + env: EnvVars; +}; + +function createSNSClient(): SNSClient { + return new SNSClient({}); +} + +export function createDependenciesContainer(): Deps { + const log = pino(); + + return { + snsClient: createSNSClient(), + logger: log, + env: envVars, + }; +} diff --git a/lambdas/letter-updates-transformer/src/env.ts b/lambdas/letter-updates-transformer/src/env.ts new file mode 100644 index 00000000..f93bbf39 --- /dev/null +++ b/lambdas/letter-updates-transformer/src/env.ts @@ -0,0 +1,9 @@ +import { z } from "zod"; + +const EnvVarsSchema = z.object({ + EVENTPUB_SNS_TOPIC_ARN: z.string(), +}); + +export type EnvVars = z.infer; + +export const envVars = EnvVarsSchema.parse(process.env); diff --git a/lambdas/letter-updates-transformer/src/index.ts b/lambdas/letter-updates-transformer/src/index.ts new file mode 100644 index 00000000..447df830 --- /dev/null +++ b/lambdas/letter-updates-transformer/src/index.ts @@ -0,0 +1,7 @@ +import createHandler from "./letter-updates-transformer"; +import { createDependenciesContainer } from "./deps"; + +const container = createDependenciesContainer(); + +// eslint-disable-next-line import-x/prefer-default-export +export const handler = createHandler(container); diff --git a/lambdas/letter-updates-transformer/src/letter-updates-transformer.ts b/lambdas/letter-updates-transformer/src/letter-updates-transformer.ts new file mode 100644 index 00000000..85c6d2c9 --- /dev/null +++ b/lambdas/letter-updates-transformer/src/letter-updates-transformer.ts @@ -0,0 +1,88 @@ +import { + DynamoDBRecord, + Handler, + KinesisStreamEvent, + KinesisStreamRecord, +} from "aws-lambda"; +import { unmarshall } from "@aws-sdk/util-dynamodb"; +import { + PublishBatchCommand, + PublishBatchRequestEntry, +} from "@aws-sdk/client-sns"; +import { LetterEvent } from "@nhsdigital/nhs-notify-event-schemas-supplier-api/src"; +import mapLetterToCloudEvent from "./mappers/letter-mapper"; +import { Deps } from "./deps"; +import { LetterForEventPub, LetterSchemaForEventPub } from "./types"; + +// SNS PublishBatchCommand supports up to 10 messages per batch +const BATCH_SIZE = 10; + +export default function createHandler(deps: Deps): Handler { + return async (streamEvent: KinesisStreamEvent) => { + deps.logger.info({ description: "Received event", streamEvent }); + + const cloudEvents: LetterEvent[] = streamEvent.Records.map((record) => + extractPayload(record, deps), + ) + .filter((record) => record.eventName === "MODIFY") + .filter( + (record) => + isChanged(record, "status") || isChanged(record, "reasonCode"), + ) + .map((element) => extractNewLetter(element)) + .map((element) => mapLetterToCloudEvent(element)); + + for (const batch of generateBatches(cloudEvents)) { + deps.logger.info({ + description: "Publishing batch", + size: batch.length, + letterEvents: batch, + }); + await deps.snsClient.send( + new PublishBatchCommand({ + TopicArn: deps.env.EVENTPUB_SNS_TOPIC_ARN, + PublishBatchRequestEntries: batch.map((element, index) => + buildMessage(element, index), + ), + }), + ); + } + }; +} + +function extractPayload( + record: KinesisStreamRecord, + deps: Deps, +): DynamoDBRecord { + // Kinesis data is base64 encoded + const payload = Buffer.from(record.kinesis.data, "base64").toString("utf8"); + deps.logger.info({ description: "Extracted dynamoDBRecord", payload }); + return JSON.parse(payload); +} + +function isChanged(record: DynamoDBRecord, property: string): boolean { + const oldValue = record.dynamodb?.OldImage![property]; + const newValue = record.dynamodb?.NewImage![property]; + return oldValue?.S !== newValue?.S; +} + +function extractNewLetter(record: DynamoDBRecord): LetterForEventPub { + const newImage = record.dynamodb?.NewImage!; + return LetterSchemaForEventPub.parse(unmarshall(newImage as any)); +} + +function* generateBatches(events: LetterEvent[]) { + for (let i = 0; i < events.length; i += BATCH_SIZE) { + yield events.slice(i, i + BATCH_SIZE); + } +} + +function buildMessage( + event: LetterEvent, + index: number, +): PublishBatchRequestEntry { + return { + Id: `${event.id}-${index}`, + Message: JSON.stringify(event), + }; +} diff --git a/lambdas/letter-updates-transformer/src/mappers/__tests__/letter-mapper.test.ts b/lambdas/letter-updates-transformer/src/mappers/__tests__/letter-mapper.test.ts new file mode 100644 index 00000000..9499ef95 --- /dev/null +++ b/lambdas/letter-updates-transformer/src/mappers/__tests__/letter-mapper.test.ts @@ -0,0 +1,45 @@ +import { $LetterEvent } from "@nhsdigital/nhs-notify-event-schemas-supplier-api/src"; +import { Letter } from "@internal/datastore"; +import mapLetterToCloudEvent from "../letter-mapper"; + +describe("letter-mapper", () => { + it("maps a letter to a letter event", async () => { + const letter = { + id: "id1", + specificationId: "spec1", + supplierId: "supplier1", + groupId: "group1", + status: "PRINTED", + reasonCode: "R02", + reasonText: "Reason text", + updatedAt: "2025-11-24T15:55:18.000Z", + } as Letter; + const event = mapLetterToCloudEvent(letter); + + // Check it conforms to the letter event schema - parse will throw an error if not + $LetterEvent.parse(event); + expect(event.type).toBe("uk.nhs.notify.supplier-api.letter.PRINTED.v1"); + expect(event.dataschema).toBe( + `https://notify.nhs.uk/cloudevents/schemas/supplier-api/letter.PRINTED.${event.dataschemaversion}.schema.json`, + ); + expect(event.dataschemaversion).toBe("1.0.6"); + expect(event.subject).toBe("letter-origin/supplier-api/letter/id1"); + expect(event.time).toBe("2025-11-24T15:55:18.000Z"); + expect(event.recordedtime).toBe("2025-11-24T15:55:18.000Z"); + expect(event.data).toEqual({ + domainId: "id1", + status: "PRINTED", + specificationId: "spec1", + supplierId: "supplier1", + groupId: "group1", + reasonCode: "R02", + reasonText: "Reason text", + origin: { + domain: "supplier-api", + source: "/data-plane/supplier-api/letters", + subject: "letter-origin/supplier-api/letter/id1", + event: event.id, + }, + }); + }); +}); diff --git a/lambdas/letter-updates-transformer/src/mappers/letter-mapper.ts b/lambdas/letter-updates-transformer/src/mappers/letter-mapper.ts new file mode 100644 index 00000000..759f5f0f --- /dev/null +++ b/lambdas/letter-updates-transformer/src/mappers/letter-mapper.ts @@ -0,0 +1,43 @@ +import { LetterEvent } from "@nhsdigital/nhs-notify-event-schemas-supplier-api/src"; +import { randomBytes, randomUUID } from "node:crypto"; +import eventSchemaPackage from "@nhsdigital/nhs-notify-event-schemas-supplier-api/package.json"; +import { LetterForEventPub } from "../types"; + +export default function mapLetterToCloudEvent( + letter: LetterForEventPub, +): LetterEvent { + const eventId = randomUUID(); + const dataschemaversion = eventSchemaPackage.version; + return { + specversion: "1.0", + id: eventId, + type: `uk.nhs.notify.supplier-api.letter.${letter.status}.v1`, + plane: "data", + dataschema: `https://notify.nhs.uk/cloudevents/schemas/supplier-api/letter.${letter.status}.${dataschemaversion}.schema.json`, + dataschemaversion, + source: "/data-plane/supplier-api/letters", + subject: `letter-origin/supplier-api/letter/${letter.id}`, + + data: { + domainId: letter.id as LetterEvent["data"]["domainId"], + status: letter.status, + specificationId: letter.specificationId, + supplierId: letter.supplierId, + groupId: letter.groupId, + reasonCode: letter.reasonCode, + reasonText: letter.reasonText, + origin: { + domain: "supplier-api", + source: "/data-plane/supplier-api/letters", + subject: `letter-origin/supplier-api/letter/${letter.id}`, + event: eventId, + }, + }, + time: letter.updatedAt, + datacontenttype: "application/json", + traceparent: `00-${randomBytes(16).toString("hex")}-${randomBytes(8).toString("hex")}-01`, + recordedtime: letter.updatedAt, + severitynumber: 2, + severitytext: "INFO", + }; +} diff --git a/lambdas/letter-updates-transformer/src/types.ts b/lambdas/letter-updates-transformer/src/types.ts new file mode 100644 index 00000000..b1b7f4c7 --- /dev/null +++ b/lambdas/letter-updates-transformer/src/types.ts @@ -0,0 +1,10 @@ +import { LetterSchemaBase, SupplierSchema } from "@internal/datastore"; +import { idRef } from "@internal/helpers"; +import { z } from "zod"; + +export const LetterSchemaForEventPub = LetterSchemaBase.extend({ + supplierId: idRef(SupplierSchema, "id"), + updatedAt: z.string(), +}); + +export type LetterForEventPub = z.infer; diff --git a/lambdas/letter-updates-transformer/tsconfig.json b/lambdas/letter-updates-transformer/tsconfig.json new file mode 100644 index 00000000..f3fa0970 --- /dev/null +++ b/lambdas/letter-updates-transformer/tsconfig.json @@ -0,0 +1,11 @@ +{ + "compilerOptions": { + "esModuleInterop": true, + "resolveJsonModule": true + }, + "extends": "../../tsconfig.base.json", + "include": [ + "src/**/*", + "jest.config.ts" + ] +} diff --git a/lambdas/upsert-letter/.eslintignore b/lambdas/upsert-letter/.eslintignore new file mode 100644 index 00000000..1521c8b7 --- /dev/null +++ b/lambdas/upsert-letter/.eslintignore @@ -0,0 +1 @@ +dist diff --git a/lambdas/upsert-letter/.gitignore b/lambdas/upsert-letter/.gitignore new file mode 100644 index 00000000..80323f7c --- /dev/null +++ b/lambdas/upsert-letter/.gitignore @@ -0,0 +1,4 @@ +coverage +node_modules +dist +.reports diff --git a/lambdas/upsert-letter/jest.config.ts b/lambdas/upsert-letter/jest.config.ts new file mode 100644 index 00000000..f68246e6 --- /dev/null +++ b/lambdas/upsert-letter/jest.config.ts @@ -0,0 +1,63 @@ +export const baseJestConfig = { + preset: "ts-jest", + extensionsToTreatAsEsm: [".ts"], + transform: { + "^.+\\.ts$": ["ts-jest", { + useESM: true + }] + }, + + // Automatically clear mock calls, instances, contexts and results before every test + clearMocks: true, + + // Indicates whether the coverage information should be collected while executing the test + collectCoverage: true, + + // The directory where Jest should output its coverage files + coverageDirectory: "./.reports/unit/coverage", + + // Indicates which provider should be used to instrument code for coverage + coverageProvider: "babel", + + coverageThreshold: { + global: { + branches: 100, + functions: 100, + lines: 100, + statements: -10, + }, + }, + + coveragePathIgnorePatterns: ["/__tests__/"], + testPathIgnorePatterns: [".build"], + testMatch: ["**/?(*.)+(spec|test).[jt]s?(x)"], + + // Use this configuration option to add custom reporters to Jest + reporters: [ + "default", + [ + "jest-html-reporter", + { + pageTitle: "Test Report", + outputPath: "./.reports/unit/test-report.html", + includeFailureMsg: true, + }, + ], + ], + + // The test environment that will be used for testing + testEnvironment: "jsdom", +}; + +const utilsJestConfig = { + ...baseJestConfig, + + testEnvironment: "node", + + coveragePathIgnorePatterns: [ + ...(baseJestConfig.coveragePathIgnorePatterns ?? []), + "zod-validators.ts", + ], +}; + +export default utilsJestConfig; diff --git a/lambdas/upsert-letter/package.json b/lambdas/upsert-letter/package.json new file mode 100644 index 00000000..9444e714 --- /dev/null +++ b/lambdas/upsert-letter/package.json @@ -0,0 +1,24 @@ +{ + "dependencies": { + "esbuild": "^0.24.0" + }, + "devDependencies": { + "@tsconfig/node22": "^22.0.2", + "@types/aws-lambda": "^8.10.148", + "@types/jest": "^30.0.0", + "jest": "^30.2.0", + "jest-mock-extended": "^4.0.0", + "ts-jest": "^29.4.0", + "typescript": "^5.8.3" + }, + "name": "nhs-notify-supplier-api-upsert-letter", + "private": true, + "scripts": { + "lambda-build": "rm -rf dist && npx esbuild --bundle --minify --sourcemap --target=es2020 --platform=node --loader:.node=file --entry-names=[name] --outdir=dist src/index.ts", + "lint": "eslint .", + "lint:fix": "eslint . --fix", + "test:unit": "jest", + "typecheck": "tsc --noEmit" + }, + "version": "0.0.1" +} diff --git a/lambdas/upsert-letter/src/__tests__/index.test.ts b/lambdas/upsert-letter/src/__tests__/index.test.ts new file mode 100644 index 00000000..d29b864b --- /dev/null +++ b/lambdas/upsert-letter/src/__tests__/index.test.ts @@ -0,0 +1,17 @@ +import type { Context } from "aws-lambda"; +import { mockDeep } from "jest-mock-extended"; +import handler from ".."; + +describe("event-logging Lambda", () => { + it("logs the input event and returns 200", async () => { + const event = { foo: "bar" }; + const context = mockDeep(); + const callback = jest.fn(); + const result = await handler(event, context, callback); + + expect(result).toEqual({ + statusCode: 200, + body: "Event logged", + }); + }); +}); diff --git a/lambdas/upsert-letter/src/index.ts b/lambdas/upsert-letter/src/index.ts new file mode 100644 index 00000000..a165560c --- /dev/null +++ b/lambdas/upsert-letter/src/index.ts @@ -0,0 +1,12 @@ +// Replace me with the actual code for your Lambda function +import { Handler } from "aws-lambda"; + +const handler: Handler = async (event) => { + console.log("Received event:", event); + return { + statusCode: 200, + body: "Event logged", + }; +}; + +export default handler; diff --git a/lambdas/upsert-letter/tsconfig.json b/lambdas/upsert-letter/tsconfig.json new file mode 100644 index 00000000..528c0c8b --- /dev/null +++ b/lambdas/upsert-letter/tsconfig.json @@ -0,0 +1,13 @@ +{ + "compilerOptions": { + "types": [ + "jest", + "node" + ] + }, + "extends": "../../tsconfig.base.json", + "include": [ + "src/**/*", + "jest.config.ts" + ] +} diff --git a/package-lock.json b/package-lock.json index fc7a73a8..340acc85 100644 --- a/package-lock.json +++ b/package-lock.json @@ -6,25 +6,29 @@ "": { "name": "nhs-notify-supplier-api", "workspaces": [ - "lambdas/*", + "docs", "internal/*", - "scripts/test-data", - "docs" + "lambdas/*", + "pact-contracts", + "scripts/utilities/*", + "tests" ], "dependencies": { "@aws-sdk/client-api-gateway": "^3.906.0", - "@playwright/test": "^1.55.1", + "@aws-sdk/client-kinesis": "^3.939.0", + "@aws-sdk/client-s3": "^3.925.0", + "@aws-sdk/client-sns": "^3.936.0", + "@playwright/test": "^1.57.0", "ajv": "^8.17.1", - "js-yaml": "^4.1.0", + "get-east-asian-width": "^1.4.0", "openapi-response-validator": "^12.1.3", "serve": "^14.2.4" }, "devDependencies": { "@openapitools/openapi-generator-cli": "^2.21.4", - "@redocly/cli": "^1.34.5", + "@redocly/cli": "^2.11.1", "@tsconfig/node22": "^22.0.2", "@types/jest": "^30.0.0", - "@types/js-yaml": "^4.0.9", "@typescript-eslint/eslint-plugin": "^8.46.2", "@typescript-eslint/parser": "^8.27.0", "esbuild": "^0.25.11", @@ -54,7 +58,9 @@ "ts-jest": "^29.4.0", "ts-node": "^10.9.2", "tsx": "^4.20.6", - "typescript": "^5.8.3" + "typescript": "^5.9.3", + "typescript-eslint": "^8.27.0", + "yaml": "^2.6.1" } }, "docs": { @@ -62,7 +68,7 @@ "version": "1.0.0", "license": "ISC", "dependencies": { - "nhsuk-frontend": "^8.1.1" + "nhsuk-frontend": "^10.1.0" }, "devDependencies": {} }, @@ -75,7 +81,8 @@ "@aws-sdk/lib-dynamodb": "^3.858.0", "@internal/helpers": "*", "pino": "^9.7.0", - "zod": "^4.1.11" + "zod": "^4.1.11", + "zod-mermaid": "^1.0.9" }, "devDependencies": { "@stylistic/eslint-plugin": "^3.1.0", @@ -90,15 +97,52 @@ "testcontainers": "^11.4.0", "ts-jest": "^29.4.0", "ts-node": "^10.9.2", - "typescript": "^5.8.3", - "zod-mermaid": "^1.0.9" + "typescript": "^5.9.3" } }, - "internal/datastore/node_modules/zod": { - "version": "4.1.12", + "internal/datastore/node_modules/pino": { + "version": "9.14.0", + "resolved": "https://registry.npmjs.org/pino/-/pino-9.14.0.tgz", + "integrity": "sha512-8OEwKp5juEvb/MjpIc4hjqfgCNysrS94RIOMXYvpYCdm/jglrKEiAYmiumbmGhCvs+IcInsphYDFwqrjr7398w==", "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/colinhacks" + "dependencies": { + "@pinojs/redact": "^0.4.0", + "atomic-sleep": "^1.0.0", + "on-exit-leak-free": "^2.1.0", + "pino-abstract-transport": "^2.0.0", + "pino-std-serializers": "^7.0.0", + "process-warning": "^5.0.0", + "quick-format-unescaped": "^4.0.3", + "real-require": "^0.2.0", + "safe-stable-stringify": "^2.3.1", + "sonic-boom": "^4.0.1", + "thread-stream": "^3.0.0" + }, + "bin": { + "pino": "bin.js" + } + }, + "internal/events": { + "name": "@nhsdigital/nhs-notify-event-schemas-supplier-api", + "version": "1.0.6", + "license": "MIT", + "dependencies": { + "@asyncapi/bundler": "^0.6.4", + "zod": "^4.1.11" + }, + "devDependencies": { + "@stoplight/spectral-cli": "^6.15.0", + "@stylistic/eslint-plugin": "^3.1.0", + "@tsconfig/node22": "^22.0.2", + "@types/jest": "^30.0.0", + "@typescript-eslint/eslint-plugin": "^8.27.0", + "@typescript-eslint/parser": "^8.27.0", + "eslint": "^9.27.0", + "eslint-plugin-jest": "^29.0.1", + "jest": "^30.2.0", + "ts-jest": "^29.4.0", + "ts-node": "^10.9.2", + "typescript": "^5.9.3" } }, "internal/helpers": { @@ -111,10904 +155,7737 @@ "devDependencies": { "@stylistic/eslint-plugin": "^3.1.0", "@tsconfig/node22": "^22.0.2", - "@types/jest": "^29.5.14", + "@types/jest": "^30.0.0", "@typescript-eslint/eslint-plugin": "^8.27.0", "@typescript-eslint/parser": "^8.27.0", "eslint": "^9.27.0", "eslint-plugin-jest": "^29.0.1", - "jest": "^30.1.3", + "jest": "^30.2.0", "ts-jest": "^29.4.0", "ts-node": "^10.9.2", - "typescript": "^5.8.3" + "typescript": "^5.9.3" } }, - "internal/helpers/node_modules/@jest/core": { - "version": "29.7.0", - "dev": true, - "license": "MIT", + "lambdas/api-handler": { + "name": "nhs-notify-supplier-api-handler", + "version": "0.0.1", "dependencies": { - "@jest/console": "^29.7.0", - "@jest/reporters": "^29.7.0", - "@jest/test-result": "^29.7.0", - "@jest/transform": "^29.7.0", - "@jest/types": "^29.6.3", - "@types/node": "*", - "ansi-escapes": "^4.2.1", - "chalk": "^4.0.0", - "ci-info": "^3.2.0", - "exit": "^0.1.2", - "graceful-fs": "^4.2.9", - "jest-changed-files": "^29.7.0", - "jest-config": "^29.7.0", - "jest-haste-map": "^29.7.0", - "jest-message-util": "^29.7.0", - "jest-regex-util": "^29.6.3", - "jest-resolve": "^29.7.0", - "jest-resolve-dependencies": "^29.7.0", - "jest-runner": "^29.7.0", - "jest-runtime": "^29.7.0", - "jest-snapshot": "^29.7.0", - "jest-util": "^29.7.0", - "jest-validate": "^29.7.0", - "jest-watcher": "^29.7.0", - "micromatch": "^4.0.4", - "pretty-format": "^29.7.0", - "slash": "^3.0.0", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - }, - "peerDependencies": { - "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" + "@aws-sdk/client-dynamodb": "^3.925.0", + "@aws-sdk/client-s3": "^3.925.0", + "@aws-sdk/client-sqs": "^3.925.0", + "@aws-sdk/lib-dynamodb": "^3.925.0", + "@aws-sdk/s3-request-presigner": "^3.925.0", + "@internal/datastore": "*", + "@internal/helpers": "*", + "esbuild": "^0.25.11", + "pino": "^9.7.0", + "zod": "^4.1.11" }, - "peerDependenciesMeta": { - "node-notifier": { - "optional": true - } + "devDependencies": { + "@tsconfig/node22": "^22.0.2", + "@types/aws-lambda": "^8.10.148", + "@types/jest": "^30.0.0", + "jest": "^30.2.0", + "jest-mock-extended": "^4.0.0", + "ts-jest": "^29.4.0", + "typescript": "^5.9.3" } }, - "internal/helpers/node_modules/@jest/environment": { - "version": "29.7.0", - "dev": true, + "lambdas/api-handler/node_modules/pino": { + "version": "9.14.0", + "resolved": "https://registry.npmjs.org/pino/-/pino-9.14.0.tgz", + "integrity": "sha512-8OEwKp5juEvb/MjpIc4hjqfgCNysrS94RIOMXYvpYCdm/jglrKEiAYmiumbmGhCvs+IcInsphYDFwqrjr7398w==", "license": "MIT", "dependencies": { - "@jest/fake-timers": "^29.7.0", - "@jest/types": "^29.6.3", - "@types/node": "*", - "jest-mock": "^29.7.0" + "@pinojs/redact": "^0.4.0", + "atomic-sleep": "^1.0.0", + "on-exit-leak-free": "^2.1.0", + "pino-abstract-transport": "^2.0.0", + "pino-std-serializers": "^7.0.0", + "process-warning": "^5.0.0", + "quick-format-unescaped": "^4.0.3", + "real-require": "^0.2.0", + "safe-stable-stringify": "^2.3.1", + "sonic-boom": "^4.0.1", + "thread-stream": "^3.0.0" }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + "bin": { + "pino": "bin.js" } }, - "internal/helpers/node_modules/@jest/expect": { - "version": "29.7.0", - "dev": true, - "license": "MIT", + "lambdas/authorizer": { + "name": "nhs-notify-supplier-authorizer", + "version": "0.0.1", "dependencies": { - "expect": "^29.7.0", - "jest-snapshot": "^29.7.0" + "@aws-sdk/client-dynamodb": "^3.858.0", + "@aws-sdk/lib-dynamodb": "^3.858.0", + "@internal/datastore": "*", + "esbuild": "^0.25.11", + "pino": "^9.7.0", + "zod": "^4.1.11" }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + "devDependencies": { + "@tsconfig/node22": "^22.0.2", + "@types/aws-lambda": "^8.10.148", + "@types/jest": "^30.0.0", + "jest": "^30.2.0", + "jest-mock-extended": "^4.0.0", + "ts-jest": "^29.4.0", + "typescript": "^5.9.3" } }, - "internal/helpers/node_modules/@jest/fake-timers": { - "version": "29.7.0", - "dev": true, + "lambdas/authorizer/node_modules/pino": { + "version": "9.14.0", + "resolved": "https://registry.npmjs.org/pino/-/pino-9.14.0.tgz", + "integrity": "sha512-8OEwKp5juEvb/MjpIc4hjqfgCNysrS94RIOMXYvpYCdm/jglrKEiAYmiumbmGhCvs+IcInsphYDFwqrjr7398w==", "license": "MIT", "dependencies": { - "@jest/types": "^29.6.3", - "@sinonjs/fake-timers": "^10.0.2", - "@types/node": "*", - "jest-message-util": "^29.7.0", - "jest-mock": "^29.7.0", - "jest-util": "^29.7.0" + "@pinojs/redact": "^0.4.0", + "atomic-sleep": "^1.0.0", + "on-exit-leak-free": "^2.1.0", + "pino-abstract-transport": "^2.0.0", + "pino-std-serializers": "^7.0.0", + "process-warning": "^5.0.0", + "quick-format-unescaped": "^4.0.3", + "real-require": "^0.2.0", + "safe-stable-stringify": "^2.3.1", + "sonic-boom": "^4.0.1", + "thread-stream": "^3.0.0" }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + "bin": { + "pino": "bin.js" } }, - "internal/helpers/node_modules/@jest/globals": { - "version": "29.7.0", - "dev": true, - "license": "MIT", + "lambdas/letter-updates-transformer": { + "name": "nhs-notify-supplier-api-letter-updates-transformer", + "version": "0.0.1", "dependencies": { - "@jest/environment": "^29.7.0", - "@jest/expect": "^29.7.0", - "@jest/types": "^29.6.3", - "jest-mock": "^29.7.0" + "@aws-sdk/client-sns": "^3.943.0", + "@aws-sdk/util-dynamodb": "^3.943.0", + "@internal/datastore": "^0.1.0", + "@internal/helpers": "^0.1.0", + "@nhsdigital/nhs-notify-event-schemas-supplier-api": "*", + "aws-lambda": "^1.0.7", + "esbuild": "^0.24.0", + "pino": "^10.1.0", + "zod": "^4.1.13" }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + "devDependencies": { + "@tsconfig/node22": "^22.0.2", + "@types/aws-lambda": "^8.10.148", + "@types/jest": "^30.0.0", + "jest": "^30.2.0", + "jest-mock-extended": "^4.0.0", + "ts-jest": "^29.4.0", + "typescript": "^5.8.3" } }, - "internal/helpers/node_modules/@jest/reporters": { - "version": "29.7.0", - "dev": true, + "lambdas/letter-updates-transformer/node_modules/esbuild": { + "version": "0.24.2", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.24.2.tgz", + "integrity": "sha512-+9egpBW8I3CD5XPe0n6BfT5fxLzxrlDzqydF3aviG+9ni1lDC/OvMHcxqEFV0+LANZG5R1bFMWfUrjVsdwxJvA==", + "hasInstallScript": true, "license": "MIT", - "dependencies": { - "@bcoe/v8-coverage": "^0.2.3", - "@jest/console": "^29.7.0", - "@jest/test-result": "^29.7.0", - "@jest/transform": "^29.7.0", - "@jest/types": "^29.6.3", - "@jridgewell/trace-mapping": "^0.3.18", - "@types/node": "*", - "chalk": "^4.0.0", - "collect-v8-coverage": "^1.0.0", - "exit": "^0.1.2", - "glob": "^7.1.3", - "graceful-fs": "^4.2.9", - "istanbul-lib-coverage": "^3.0.0", - "istanbul-lib-instrument": "^6.0.0", - "istanbul-lib-report": "^3.0.0", - "istanbul-lib-source-maps": "^4.0.0", - "istanbul-reports": "^3.1.3", - "jest-message-util": "^29.7.0", - "jest-util": "^29.7.0", - "jest-worker": "^29.7.0", - "slash": "^3.0.0", - "string-length": "^4.0.1", - "strip-ansi": "^6.0.0", - "v8-to-istanbul": "^9.0.1" + "bin": { + "esbuild": "bin/esbuild" }, "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + "node": ">=18" }, - "peerDependencies": { - "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.24.2", + "@esbuild/android-arm": "0.24.2", + "@esbuild/android-arm64": "0.24.2", + "@esbuild/android-x64": "0.24.2", + "@esbuild/darwin-arm64": "0.24.2", + "@esbuild/darwin-x64": "0.24.2", + "@esbuild/freebsd-arm64": "0.24.2", + "@esbuild/freebsd-x64": "0.24.2", + "@esbuild/linux-arm": "0.24.2", + "@esbuild/linux-arm64": "0.24.2", + "@esbuild/linux-ia32": "0.24.2", + "@esbuild/linux-loong64": "0.24.2", + "@esbuild/linux-mips64el": "0.24.2", + "@esbuild/linux-ppc64": "0.24.2", + "@esbuild/linux-riscv64": "0.24.2", + "@esbuild/linux-s390x": "0.24.2", + "@esbuild/linux-x64": "0.24.2", + "@esbuild/netbsd-arm64": "0.24.2", + "@esbuild/netbsd-x64": "0.24.2", + "@esbuild/openbsd-arm64": "0.24.2", + "@esbuild/openbsd-x64": "0.24.2", + "@esbuild/sunos-x64": "0.24.2", + "@esbuild/win32-arm64": "0.24.2", + "@esbuild/win32-ia32": "0.24.2", + "@esbuild/win32-x64": "0.24.2" + } + }, + "lambdas/upsert-letter": { + "name": "nhs-notify-supplier-api-upsert-letter", + "version": "0.0.1", + "dependencies": { + "esbuild": "^0.24.0" }, - "peerDependenciesMeta": { - "node-notifier": { - "optional": true - } + "devDependencies": { + "@tsconfig/node22": "^22.0.2", + "@types/aws-lambda": "^8.10.148", + "@types/jest": "^30.0.0", + "jest": "^30.2.0", + "jest-mock-extended": "^4.0.0", + "ts-jest": "^29.4.0", + "typescript": "^5.8.3" } }, - "internal/helpers/node_modules/@jest/source-map": { - "version": "29.6.3", - "dev": true, + "lambdas/upsert-letter/node_modules/esbuild": { + "version": "0.24.2", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.24.2.tgz", + "integrity": "sha512-+9egpBW8I3CD5XPe0n6BfT5fxLzxrlDzqydF3aviG+9ni1lDC/OvMHcxqEFV0+LANZG5R1bFMWfUrjVsdwxJvA==", + "hasInstallScript": true, "license": "MIT", - "dependencies": { - "@jridgewell/trace-mapping": "^0.3.18", - "callsites": "^3.0.0", - "graceful-fs": "^4.2.9" + "bin": { + "esbuild": "bin/esbuild" }, "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "internal/helpers/node_modules/@jest/test-sequencer": { - "version": "29.7.0", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/test-result": "^29.7.0", - "graceful-fs": "^4.2.9", - "jest-haste-map": "^29.7.0", - "slash": "^3.0.0" + "node": ">=18" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.24.2", + "@esbuild/android-arm": "0.24.2", + "@esbuild/android-arm64": "0.24.2", + "@esbuild/android-x64": "0.24.2", + "@esbuild/darwin-arm64": "0.24.2", + "@esbuild/darwin-x64": "0.24.2", + "@esbuild/freebsd-arm64": "0.24.2", + "@esbuild/freebsd-x64": "0.24.2", + "@esbuild/linux-arm": "0.24.2", + "@esbuild/linux-arm64": "0.24.2", + "@esbuild/linux-ia32": "0.24.2", + "@esbuild/linux-loong64": "0.24.2", + "@esbuild/linux-mips64el": "0.24.2", + "@esbuild/linux-ppc64": "0.24.2", + "@esbuild/linux-riscv64": "0.24.2", + "@esbuild/linux-s390x": "0.24.2", + "@esbuild/linux-x64": "0.24.2", + "@esbuild/netbsd-arm64": "0.24.2", + "@esbuild/netbsd-x64": "0.24.2", + "@esbuild/openbsd-arm64": "0.24.2", + "@esbuild/openbsd-x64": "0.24.2", + "@esbuild/sunos-x64": "0.24.2", + "@esbuild/win32-arm64": "0.24.2", + "@esbuild/win32-ia32": "0.24.2", + "@esbuild/win32-x64": "0.24.2" + } + }, + "node_modules/@apidevtools/json-schema-ref-parser": { + "version": "11.9.3", + "resolved": "https://registry.npmjs.org/@apidevtools/json-schema-ref-parser/-/json-schema-ref-parser-11.9.3.tgz", + "integrity": "sha512-60vepv88RwcJtSHrD6MjIL6Ta3SOYbgfnkHb+ppAVK+o9mXprRtulx7VlRl3lN3bbvysAfCS7WMVfhUYemB0IQ==", + "license": "MIT", + "dependencies": { + "@jsdevtools/ono": "^7.1.3", + "@types/json-schema": "^7.0.15", + "js-yaml": "^4.1.0" }, "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + "node": ">= 16" + }, + "funding": { + "url": "https://github.com/sponsors/philsturgeon" } }, - "internal/helpers/node_modules/@jest/transform": { - "version": "29.7.0", + "node_modules/@asamuzakjp/css-color": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/@asamuzakjp/css-color/-/css-color-3.2.0.tgz", + "integrity": "sha512-K1A6z8tS3XsmCMM86xoWdn7Fkdn9m6RSVtocUrJYIwZnFVkng/PvkEoWtOWmP+Scc6saYWHWZYbndEEXxl24jw==", "dev": true, "license": "MIT", "dependencies": { - "@babel/core": "^7.11.6", - "@jest/types": "^29.6.3", - "@jridgewell/trace-mapping": "^0.3.18", - "babel-plugin-istanbul": "^6.1.1", - "chalk": "^4.0.0", - "convert-source-map": "^2.0.0", - "fast-json-stable-stringify": "^2.1.0", - "graceful-fs": "^4.2.9", - "jest-haste-map": "^29.7.0", - "jest-regex-util": "^29.6.3", - "jest-util": "^29.7.0", - "micromatch": "^4.0.4", - "pirates": "^4.0.4", - "slash": "^3.0.0", - "write-file-atomic": "^4.0.2" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + "@csstools/css-calc": "^2.1.3", + "@csstools/css-color-parser": "^3.0.9", + "@csstools/css-parser-algorithms": "^3.0.4", + "@csstools/css-tokenizer": "^3.0.3", + "lru-cache": "^10.4.3" } }, - "internal/helpers/node_modules/@sinonjs/fake-timers": { - "version": "10.3.0", + "node_modules/@asamuzakjp/css-color/node_modules/lru-cache": { + "version": "10.4.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", + "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", "dev": true, - "license": "BSD-3-Clause", + "license": "ISC" + }, + "node_modules/@asyncapi/bundler": { + "version": "0.6.4", + "resolved": "https://registry.npmjs.org/@asyncapi/bundler/-/bundler-0.6.4.tgz", + "integrity": "sha512-lKZo2FF2TKt4n6Qm8vP/JOEEGE04gdH/D9oHmBt/NfOylMaw8XoFsI+k+IJyzpVMzREjZfxGf9gNzfW0CWRf5g==", + "license": "Apache-2.0", "dependencies": { - "@sinonjs/commons": "^3.0.0" + "@apidevtools/json-schema-ref-parser": "^11.5.4", + "@types/json-schema": "^7.0.11", + "@ungap/structured-clone": "^1.2.0", + "js-yaml": "^4.1.0", + "lodash": "^4.17.21" } }, - "internal/helpers/node_modules/@types/jest": { - "version": "29.5.14", + "node_modules/@asyncapi/specs": { + "version": "6.10.0", + "resolved": "https://registry.npmjs.org/@asyncapi/specs/-/specs-6.10.0.tgz", + "integrity": "sha512-vB5oKLsdrLUORIZ5BXortZTlVyGWWMC1Nud/0LtgxQ3Yn2738HigAD6EVqScvpPsDUI/bcLVsYEXN4dtXQHVng==", "dev": true, - "license": "MIT", + "license": "Apache-2.0", "dependencies": { - "expect": "^29.0.0", - "pretty-format": "^29.0.0" + "@types/json-schema": "^7.0.11" } }, - "internal/helpers/node_modules/babel-jest": { - "version": "29.7.0", - "dev": true, - "license": "MIT", + "node_modules/@aws-crypto/crc32": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/crc32/-/crc32-5.2.0.tgz", + "integrity": "sha512-nLbCWqQNgUiwwtFsen1AdzAtvuLRsQS8rYgMuxCrdKf9kOssamGLuPwyTY9wyYblNr9+1XM8v6zoDTPPSIeANg==", + "license": "Apache-2.0", "dependencies": { - "@jest/transform": "^29.7.0", - "@types/babel__core": "^7.1.14", - "babel-plugin-istanbul": "^6.1.1", - "babel-preset-jest": "^29.6.3", - "chalk": "^4.0.0", - "graceful-fs": "^4.2.9", - "slash": "^3.0.0" + "@aws-crypto/util": "^5.2.0", + "@aws-sdk/types": "^3.222.0", + "tslib": "^2.6.2" }, "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - }, - "peerDependencies": { - "@babel/core": "^7.8.0" + "node": ">=16.0.0" } }, - "internal/helpers/node_modules/babel-plugin-istanbul": { - "version": "6.1.1", - "dev": true, - "license": "BSD-3-Clause", + "node_modules/@aws-crypto/crc32c": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/crc32c/-/crc32c-5.2.0.tgz", + "integrity": "sha512-+iWb8qaHLYKrNvGRbiYRHSdKRWhto5XlZUEBwDjYNf+ly5SVYG6zEoYIdxvf5R3zyeP16w4PLBn3rH1xc74Rag==", + "license": "Apache-2.0", "dependencies": { - "@babel/helper-plugin-utils": "^7.0.0", - "@istanbuljs/load-nyc-config": "^1.0.0", - "@istanbuljs/schema": "^0.1.2", - "istanbul-lib-instrument": "^5.0.4", - "test-exclude": "^6.0.0" - }, - "engines": { - "node": ">=8" + "@aws-crypto/util": "^5.2.0", + "@aws-sdk/types": "^3.222.0", + "tslib": "^2.6.2" } }, - "internal/helpers/node_modules/babel-plugin-istanbul/node_modules/istanbul-lib-instrument": { - "version": "5.2.1", - "dev": true, - "license": "BSD-3-Clause", + "node_modules/@aws-crypto/sha1-browser": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/sha1-browser/-/sha1-browser-5.2.0.tgz", + "integrity": "sha512-OH6lveCFfcDjX4dbAvCFSYUjJZjDr/3XJ3xHtjn3Oj5b9RjojQo8npoLeA/bNwkOkrSQ0wgrHzXk4tDRxGKJeg==", + "license": "Apache-2.0", "dependencies": { - "@babel/core": "^7.12.3", - "@babel/parser": "^7.14.7", - "@istanbuljs/schema": "^0.1.2", - "istanbul-lib-coverage": "^3.2.0", - "semver": "^6.3.0" - }, - "engines": { - "node": ">=8" - } - }, - "internal/helpers/node_modules/babel-plugin-istanbul/node_modules/semver": { - "version": "6.3.1", - "dev": true, - "license": "ISC", - "bin": { - "semver": "bin/semver.js" + "@aws-crypto/supports-web-crypto": "^5.2.0", + "@aws-crypto/util": "^5.2.0", + "@aws-sdk/types": "^3.222.0", + "@aws-sdk/util-locate-window": "^3.0.0", + "@smithy/util-utf8": "^2.0.0", + "tslib": "^2.6.2" } }, - "internal/helpers/node_modules/babel-plugin-jest-hoist": { - "version": "29.6.3", - "dev": true, - "license": "MIT", + "node_modules/@aws-crypto/sha1-browser/node_modules/@smithy/is-array-buffer": { + "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==", + "license": "Apache-2.0", "dependencies": { - "@babel/template": "^7.3.3", - "@babel/types": "^7.3.3", - "@types/babel__core": "^7.1.14", - "@types/babel__traverse": "^7.0.6" + "tslib": "^2.6.2" }, "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + "node": ">=14.0.0" } }, - "internal/helpers/node_modules/babel-preset-jest": { - "version": "29.6.3", - "dev": true, - "license": "MIT", + "node_modules/@aws-crypto/sha1-browser/node_modules/@smithy/util-buffer-from": { + "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==", + "license": "Apache-2.0", "dependencies": { - "babel-plugin-jest-hoist": "^29.6.3", - "babel-preset-current-node-syntax": "^1.0.0" + "@smithy/is-array-buffer": "^2.2.0", + "tslib": "^2.6.2" }, "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" + "node": ">=14.0.0" } }, - "internal/helpers/node_modules/brace-expansion": { - "version": "1.1.12", - "dev": true, - "license": "MIT", + "node_modules/@aws-crypto/sha1-browser/node_modules/@smithy/util-utf8": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/@smithy/util-utf8/-/util-utf8-2.3.0.tgz", + "integrity": "sha512-R8Rdn8Hy72KKcebgLiv8jQcQkXoLMOGGv5uI1/k0l+snqkOzQ1R0ChUBCxWMlBsFMekWjq0wRudIweFs7sKT5A==", + "license": "Apache-2.0", "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "internal/helpers/node_modules/camelcase": { - "version": "6.3.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10" + "@smithy/util-buffer-from": "^2.2.0", + "tslib": "^2.6.2" }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "internal/helpers/node_modules/ci-info": { - "version": "3.9.0", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/sibiraj-s" - } - ], - "license": "MIT", "engines": { - "node": ">=8" + "node": ">=14.0.0" } }, - "internal/helpers/node_modules/cjs-module-lexer": { - "version": "1.4.3", - "dev": true, - "license": "MIT" - }, - "internal/helpers/node_modules/cliui": { - "version": "8.0.1", - "dev": true, - "license": "ISC", + "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", "dependencies": { - "string-width": "^4.2.0", - "strip-ansi": "^6.0.1", - "wrap-ansi": "^7.0.0" - }, - "engines": { - "node": ">=12" + "@aws-crypto/sha256-js": "^5.2.0", + "@aws-crypto/supports-web-crypto": "^5.2.0", + "@aws-crypto/util": "^5.2.0", + "@aws-sdk/types": "^3.222.0", + "@aws-sdk/util-locate-window": "^3.0.0", + "@smithy/util-utf8": "^2.0.0", + "tslib": "^2.6.2" } }, - "internal/helpers/node_modules/glob": { - "version": "7.2.3", - "dev": true, - "license": "ISC", + "node_modules/@aws-crypto/sha256-browser/node_modules/@smithy/is-array-buffer": { + "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==", + "license": "Apache-2.0", "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.1.1", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" + "tslib": "^2.6.2" }, "engines": { - "node": "*" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" + "node": ">=14.0.0" } }, - "internal/helpers/node_modules/istanbul-lib-source-maps": { - "version": "4.0.1", - "dev": true, - "license": "BSD-3-Clause", + "node_modules/@aws-crypto/sha256-browser/node_modules/@smithy/util-buffer-from": { + "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==", + "license": "Apache-2.0", "dependencies": { - "debug": "^4.1.1", - "istanbul-lib-coverage": "^3.0.0", - "source-map": "^0.6.1" + "@smithy/is-array-buffer": "^2.2.0", + "tslib": "^2.6.2" }, "engines": { - "node": ">=10" + "node": ">=14.0.0" } }, - "internal/helpers/node_modules/jest": { - "version": "29.7.0", - "dev": true, - "license": "MIT", + "node_modules/@aws-crypto/sha256-browser/node_modules/@smithy/util-utf8": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/@smithy/util-utf8/-/util-utf8-2.3.0.tgz", + "integrity": "sha512-R8Rdn8Hy72KKcebgLiv8jQcQkXoLMOGGv5uI1/k0l+snqkOzQ1R0ChUBCxWMlBsFMekWjq0wRudIweFs7sKT5A==", + "license": "Apache-2.0", "dependencies": { - "@jest/core": "^29.7.0", - "@jest/types": "^29.6.3", - "import-local": "^3.0.2", - "jest-cli": "^29.7.0" - }, - "bin": { - "jest": "bin/jest.js" + "@smithy/util-buffer-from": "^2.2.0", + "tslib": "^2.6.2" }, "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - }, - "peerDependencies": { - "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" - }, - "peerDependenciesMeta": { - "node-notifier": { - "optional": true - } + "node": ">=14.0.0" } }, - "internal/helpers/node_modules/jest-changed-files": { - "version": "29.7.0", - "dev": true, - "license": "MIT", + "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", "dependencies": { - "execa": "^5.0.0", - "jest-util": "^29.7.0", - "p-limit": "^3.1.0" + "@aws-crypto/util": "^5.2.0", + "@aws-sdk/types": "^3.222.0", + "tslib": "^2.6.2" }, "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + "node": ">=16.0.0" } }, - "internal/helpers/node_modules/jest-circus": { - "version": "29.7.0", - "dev": true, - "license": "MIT", + "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", "dependencies": { - "@jest/environment": "^29.7.0", - "@jest/expect": "^29.7.0", - "@jest/test-result": "^29.7.0", - "@jest/types": "^29.6.3", - "@types/node": "*", - "chalk": "^4.0.0", - "co": "^4.6.0", - "dedent": "^1.0.0", - "is-generator-fn": "^2.0.0", - "jest-each": "^29.7.0", - "jest-matcher-utils": "^29.7.0", - "jest-message-util": "^29.7.0", - "jest-runtime": "^29.7.0", - "jest-snapshot": "^29.7.0", - "jest-util": "^29.7.0", - "p-limit": "^3.1.0", - "pretty-format": "^29.7.0", - "pure-rand": "^6.0.0", - "slash": "^3.0.0", - "stack-utils": "^2.0.3" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + "tslib": "^2.6.2" } }, - "internal/helpers/node_modules/jest-cli": { - "version": "29.7.0", - "dev": true, - "license": "MIT", + "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", "dependencies": { - "@jest/core": "^29.7.0", - "@jest/test-result": "^29.7.0", - "@jest/types": "^29.6.3", - "chalk": "^4.0.0", - "create-jest": "^29.7.0", - "exit": "^0.1.2", - "import-local": "^3.0.2", - "jest-config": "^29.7.0", - "jest-util": "^29.7.0", - "jest-validate": "^29.7.0", - "yargs": "^17.3.1" - }, - "bin": { - "jest": "bin/jest.js" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - }, - "peerDependencies": { - "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" - }, - "peerDependenciesMeta": { - "node-notifier": { - "optional": true - } + "@aws-sdk/types": "^3.222.0", + "@smithy/util-utf8": "^2.0.0", + "tslib": "^2.6.2" } }, - "internal/helpers/node_modules/jest-config": { - "version": "29.7.0", - "dev": true, - "license": "MIT", + "node_modules/@aws-crypto/util/node_modules/@smithy/is-array-buffer": { + "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==", + "license": "Apache-2.0", "dependencies": { - "@babel/core": "^7.11.6", - "@jest/test-sequencer": "^29.7.0", - "@jest/types": "^29.6.3", - "babel-jest": "^29.7.0", - "chalk": "^4.0.0", - "ci-info": "^3.2.0", - "deepmerge": "^4.2.2", - "glob": "^7.1.3", - "graceful-fs": "^4.2.9", - "jest-circus": "^29.7.0", - "jest-environment-node": "^29.7.0", - "jest-get-type": "^29.6.3", - "jest-regex-util": "^29.6.3", - "jest-resolve": "^29.7.0", - "jest-runner": "^29.7.0", - "jest-util": "^29.7.0", - "jest-validate": "^29.7.0", - "micromatch": "^4.0.4", - "parse-json": "^5.2.0", - "pretty-format": "^29.7.0", - "slash": "^3.0.0", - "strip-json-comments": "^3.1.1" + "tslib": "^2.6.2" }, "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - }, - "peerDependencies": { - "@types/node": "*", - "ts-node": ">=9.0.0" - }, - "peerDependenciesMeta": { - "@types/node": { - "optional": true - }, - "ts-node": { - "optional": true - } + "node": ">=14.0.0" } }, - "internal/helpers/node_modules/jest-docblock": { - "version": "29.7.0", - "dev": true, - "license": "MIT", + "node_modules/@aws-crypto/util/node_modules/@smithy/util-buffer-from": { + "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==", + "license": "Apache-2.0", "dependencies": { - "detect-newline": "^3.0.0" + "@smithy/is-array-buffer": "^2.2.0", + "tslib": "^2.6.2" }, "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + "node": ">=14.0.0" } }, - "internal/helpers/node_modules/jest-each": { - "version": "29.7.0", - "dev": true, - "license": "MIT", + "node_modules/@aws-crypto/util/node_modules/@smithy/util-utf8": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/@smithy/util-utf8/-/util-utf8-2.3.0.tgz", + "integrity": "sha512-R8Rdn8Hy72KKcebgLiv8jQcQkXoLMOGGv5uI1/k0l+snqkOzQ1R0ChUBCxWMlBsFMekWjq0wRudIweFs7sKT5A==", + "license": "Apache-2.0", "dependencies": { - "@jest/types": "^29.6.3", - "chalk": "^4.0.0", - "jest-get-type": "^29.6.3", - "jest-util": "^29.7.0", - "pretty-format": "^29.7.0" + "@smithy/util-buffer-from": "^2.2.0", + "tslib": "^2.6.2" }, "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + "node": ">=14.0.0" } }, - "internal/helpers/node_modules/jest-environment-node": { - "version": "29.7.0", - "dev": true, - "license": "MIT", + "node_modules/@aws-sdk/client-api-gateway": { + "version": "3.955.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-api-gateway/-/client-api-gateway-3.955.0.tgz", + "integrity": "sha512-gFEmNfDGm8Jp6ArOcq6n2XDXzmzFkxgvdv6qOsmw+boD4ckfaQYZ3TlxFC6bxLmmlU3CWCsMQE2J/ak5CE/9Ww==", + "license": "Apache-2.0", "dependencies": { - "@jest/environment": "^29.7.0", - "@jest/fake-timers": "^29.7.0", - "@jest/types": "^29.6.3", - "@types/node": "*", - "jest-mock": "^29.7.0", - "jest-util": "^29.7.0" + "@aws-crypto/sha256-browser": "5.2.0", + "@aws-crypto/sha256-js": "5.2.0", + "@aws-sdk/core": "3.954.0", + "@aws-sdk/credential-provider-node": "3.955.0", + "@aws-sdk/middleware-host-header": "3.953.0", + "@aws-sdk/middleware-logger": "3.953.0", + "@aws-sdk/middleware-recursion-detection": "3.953.0", + "@aws-sdk/middleware-sdk-api-gateway": "3.953.0", + "@aws-sdk/middleware-user-agent": "3.954.0", + "@aws-sdk/region-config-resolver": "3.953.0", + "@aws-sdk/types": "3.953.0", + "@aws-sdk/util-endpoints": "3.953.0", + "@aws-sdk/util-user-agent-browser": "3.953.0", + "@aws-sdk/util-user-agent-node": "3.954.0", + "@smithy/config-resolver": "^4.4.4", + "@smithy/core": "^3.19.0", + "@smithy/fetch-http-handler": "^5.3.7", + "@smithy/hash-node": "^4.2.6", + "@smithy/invalid-dependency": "^4.2.6", + "@smithy/middleware-content-length": "^4.2.6", + "@smithy/middleware-endpoint": "^4.4.0", + "@smithy/middleware-retry": "^4.4.16", + "@smithy/middleware-serde": "^4.2.7", + "@smithy/middleware-stack": "^4.2.6", + "@smithy/node-config-provider": "^4.3.6", + "@smithy/node-http-handler": "^4.4.6", + "@smithy/protocol-http": "^5.3.6", + "@smithy/smithy-client": "^4.10.1", + "@smithy/types": "^4.10.0", + "@smithy/url-parser": "^4.2.6", + "@smithy/util-base64": "^4.3.0", + "@smithy/util-body-length-browser": "^4.2.0", + "@smithy/util-body-length-node": "^4.2.1", + "@smithy/util-defaults-mode-browser": "^4.3.15", + "@smithy/util-defaults-mode-node": "^4.2.18", + "@smithy/util-endpoints": "^3.2.6", + "@smithy/util-middleware": "^4.2.6", + "@smithy/util-retry": "^4.2.6", + "@smithy/util-stream": "^4.5.7", + "@smithy/util-utf8": "^4.2.0", + "tslib": "^2.6.2" }, "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + "node": ">=18.0.0" } }, - "internal/helpers/node_modules/jest-haste-map": { - "version": "29.7.0", - "dev": true, - "license": "MIT", + "node_modules/@aws-sdk/client-dynamodb": { + "version": "3.955.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-dynamodb/-/client-dynamodb-3.955.0.tgz", + "integrity": "sha512-Qo1Sv6rKxRCecaKOsdjwbeYvnmlVyueaNP5/DmAzdbxVI7pGsIFUqCXQYA0jwfONwoMoMwf/PsyGaW/+9nGhhQ==", + "license": "Apache-2.0", "dependencies": { - "@jest/types": "^29.6.3", - "@types/graceful-fs": "^4.1.3", - "@types/node": "*", - "anymatch": "^3.0.3", - "fb-watchman": "^2.0.0", - "graceful-fs": "^4.2.9", - "jest-regex-util": "^29.6.3", - "jest-util": "^29.7.0", - "jest-worker": "^29.7.0", - "micromatch": "^4.0.4", - "walker": "^1.0.8" + "@aws-crypto/sha256-browser": "5.2.0", + "@aws-crypto/sha256-js": "5.2.0", + "@aws-sdk/core": "3.954.0", + "@aws-sdk/credential-provider-node": "3.955.0", + "@aws-sdk/dynamodb-codec": "3.954.0", + "@aws-sdk/middleware-endpoint-discovery": "3.953.0", + "@aws-sdk/middleware-host-header": "3.953.0", + "@aws-sdk/middleware-logger": "3.953.0", + "@aws-sdk/middleware-recursion-detection": "3.953.0", + "@aws-sdk/middleware-user-agent": "3.954.0", + "@aws-sdk/region-config-resolver": "3.953.0", + "@aws-sdk/types": "3.953.0", + "@aws-sdk/util-endpoints": "3.953.0", + "@aws-sdk/util-user-agent-browser": "3.953.0", + "@aws-sdk/util-user-agent-node": "3.954.0", + "@smithy/config-resolver": "^4.4.4", + "@smithy/core": "^3.19.0", + "@smithy/fetch-http-handler": "^5.3.7", + "@smithy/hash-node": "^4.2.6", + "@smithy/invalid-dependency": "^4.2.6", + "@smithy/middleware-content-length": "^4.2.6", + "@smithy/middleware-endpoint": "^4.4.0", + "@smithy/middleware-retry": "^4.4.16", + "@smithy/middleware-serde": "^4.2.7", + "@smithy/middleware-stack": "^4.2.6", + "@smithy/node-config-provider": "^4.3.6", + "@smithy/node-http-handler": "^4.4.6", + "@smithy/protocol-http": "^5.3.6", + "@smithy/smithy-client": "^4.10.1", + "@smithy/types": "^4.10.0", + "@smithy/url-parser": "^4.2.6", + "@smithy/util-base64": "^4.3.0", + "@smithy/util-body-length-browser": "^4.2.0", + "@smithy/util-body-length-node": "^4.2.1", + "@smithy/util-defaults-mode-browser": "^4.3.15", + "@smithy/util-defaults-mode-node": "^4.2.18", + "@smithy/util-endpoints": "^3.2.6", + "@smithy/util-middleware": "^4.2.6", + "@smithy/util-retry": "^4.2.6", + "@smithy/util-utf8": "^4.2.0", + "@smithy/util-waiter": "^4.2.6", + "tslib": "^2.6.2" }, "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - }, - "optionalDependencies": { - "fsevents": "^2.3.2" + "node": ">=18.0.0" } }, - "internal/helpers/node_modules/jest-leak-detector": { - "version": "29.7.0", - "dev": true, - "license": "MIT", + "node_modules/@aws-sdk/client-kinesis": { + "version": "3.955.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-kinesis/-/client-kinesis-3.955.0.tgz", + "integrity": "sha512-9bxnHk1t8hO2d5czuJnUrrtWqJKrnvynYoJDRy3vn1fb+SGdHuiyRb2iheolLR1sNhwDs+Z7VLgcgk1N5kUHOQ==", + "license": "Apache-2.0", "dependencies": { - "jest-get-type": "^29.6.3", - "pretty-format": "^29.7.0" + "@aws-crypto/sha256-browser": "5.2.0", + "@aws-crypto/sha256-js": "5.2.0", + "@aws-sdk/core": "3.954.0", + "@aws-sdk/credential-provider-node": "3.955.0", + "@aws-sdk/middleware-host-header": "3.953.0", + "@aws-sdk/middleware-logger": "3.953.0", + "@aws-sdk/middleware-recursion-detection": "3.953.0", + "@aws-sdk/middleware-user-agent": "3.954.0", + "@aws-sdk/region-config-resolver": "3.953.0", + "@aws-sdk/types": "3.953.0", + "@aws-sdk/util-endpoints": "3.953.0", + "@aws-sdk/util-user-agent-browser": "3.953.0", + "@aws-sdk/util-user-agent-node": "3.954.0", + "@smithy/config-resolver": "^4.4.4", + "@smithy/core": "^3.19.0", + "@smithy/eventstream-serde-browser": "^4.2.6", + "@smithy/eventstream-serde-config-resolver": "^4.3.6", + "@smithy/eventstream-serde-node": "^4.2.6", + "@smithy/fetch-http-handler": "^5.3.7", + "@smithy/hash-node": "^4.2.6", + "@smithy/invalid-dependency": "^4.2.6", + "@smithy/middleware-content-length": "^4.2.6", + "@smithy/middleware-endpoint": "^4.4.0", + "@smithy/middleware-retry": "^4.4.16", + "@smithy/middleware-serde": "^4.2.7", + "@smithy/middleware-stack": "^4.2.6", + "@smithy/node-config-provider": "^4.3.6", + "@smithy/node-http-handler": "^4.4.6", + "@smithy/protocol-http": "^5.3.6", + "@smithy/smithy-client": "^4.10.1", + "@smithy/types": "^4.10.0", + "@smithy/url-parser": "^4.2.6", + "@smithy/util-base64": "^4.3.0", + "@smithy/util-body-length-browser": "^4.2.0", + "@smithy/util-body-length-node": "^4.2.1", + "@smithy/util-defaults-mode-browser": "^4.3.15", + "@smithy/util-defaults-mode-node": "^4.2.18", + "@smithy/util-endpoints": "^3.2.6", + "@smithy/util-middleware": "^4.2.6", + "@smithy/util-retry": "^4.2.6", + "@smithy/util-utf8": "^4.2.0", + "@smithy/util-waiter": "^4.2.6", + "tslib": "^2.6.2" }, "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + "node": ">=18.0.0" } }, - "internal/helpers/node_modules/jest-mock": { - "version": "29.7.0", - "dev": true, - "license": "MIT", + "node_modules/@aws-sdk/client-s3": { + "version": "3.955.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-s3/-/client-s3-3.955.0.tgz", + "integrity": "sha512-bFvSM6UB0R5hpWfXzHI3BlKwT2qYHto9JoDtzSr5FxVguTMzJyr+an11VT1Hi5wgO03luXEeXeloURFvaMs6TQ==", + "license": "Apache-2.0", "dependencies": { - "@jest/types": "^29.6.3", - "@types/node": "*", - "jest-util": "^29.7.0" + "@aws-crypto/sha1-browser": "5.2.0", + "@aws-crypto/sha256-browser": "5.2.0", + "@aws-crypto/sha256-js": "5.2.0", + "@aws-sdk/core": "3.954.0", + "@aws-sdk/credential-provider-node": "3.955.0", + "@aws-sdk/middleware-bucket-endpoint": "3.953.0", + "@aws-sdk/middleware-expect-continue": "3.953.0", + "@aws-sdk/middleware-flexible-checksums": "3.954.0", + "@aws-sdk/middleware-host-header": "3.953.0", + "@aws-sdk/middleware-location-constraint": "3.953.0", + "@aws-sdk/middleware-logger": "3.953.0", + "@aws-sdk/middleware-recursion-detection": "3.953.0", + "@aws-sdk/middleware-sdk-s3": "3.954.0", + "@aws-sdk/middleware-ssec": "3.953.0", + "@aws-sdk/middleware-user-agent": "3.954.0", + "@aws-sdk/region-config-resolver": "3.953.0", + "@aws-sdk/signature-v4-multi-region": "3.954.0", + "@aws-sdk/types": "3.953.0", + "@aws-sdk/util-endpoints": "3.953.0", + "@aws-sdk/util-user-agent-browser": "3.953.0", + "@aws-sdk/util-user-agent-node": "3.954.0", + "@smithy/config-resolver": "^4.4.4", + "@smithy/core": "^3.19.0", + "@smithy/eventstream-serde-browser": "^4.2.6", + "@smithy/eventstream-serde-config-resolver": "^4.3.6", + "@smithy/eventstream-serde-node": "^4.2.6", + "@smithy/fetch-http-handler": "^5.3.7", + "@smithy/hash-blob-browser": "^4.2.7", + "@smithy/hash-node": "^4.2.6", + "@smithy/hash-stream-node": "^4.2.6", + "@smithy/invalid-dependency": "^4.2.6", + "@smithy/md5-js": "^4.2.6", + "@smithy/middleware-content-length": "^4.2.6", + "@smithy/middleware-endpoint": "^4.4.0", + "@smithy/middleware-retry": "^4.4.16", + "@smithy/middleware-serde": "^4.2.7", + "@smithy/middleware-stack": "^4.2.6", + "@smithy/node-config-provider": "^4.3.6", + "@smithy/node-http-handler": "^4.4.6", + "@smithy/protocol-http": "^5.3.6", + "@smithy/smithy-client": "^4.10.1", + "@smithy/types": "^4.10.0", + "@smithy/url-parser": "^4.2.6", + "@smithy/util-base64": "^4.3.0", + "@smithy/util-body-length-browser": "^4.2.0", + "@smithy/util-body-length-node": "^4.2.1", + "@smithy/util-defaults-mode-browser": "^4.3.15", + "@smithy/util-defaults-mode-node": "^4.2.18", + "@smithy/util-endpoints": "^3.2.6", + "@smithy/util-middleware": "^4.2.6", + "@smithy/util-retry": "^4.2.6", + "@smithy/util-stream": "^4.5.7", + "@smithy/util-utf8": "^4.2.0", + "@smithy/util-waiter": "^4.2.6", + "tslib": "^2.6.2" }, "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + "node": ">=18.0.0" } }, - "internal/helpers/node_modules/jest-regex-util": { - "version": "29.6.3", - "dev": true, - "license": "MIT", + "node_modules/@aws-sdk/client-sns": { + "version": "3.955.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-sns/-/client-sns-3.955.0.tgz", + "integrity": "sha512-JE74u+Bj7ZYZje/Vph8bFuzUMDkIcPSvXEnNELHgnjuDF4HNRGsCUCjD7/ZBpoUZqL9jc2ySRUWXHu3GHbVSzQ==", + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/sha256-browser": "5.2.0", + "@aws-crypto/sha256-js": "5.2.0", + "@aws-sdk/core": "3.954.0", + "@aws-sdk/credential-provider-node": "3.955.0", + "@aws-sdk/middleware-host-header": "3.953.0", + "@aws-sdk/middleware-logger": "3.953.0", + "@aws-sdk/middleware-recursion-detection": "3.953.0", + "@aws-sdk/middleware-user-agent": "3.954.0", + "@aws-sdk/region-config-resolver": "3.953.0", + "@aws-sdk/types": "3.953.0", + "@aws-sdk/util-endpoints": "3.953.0", + "@aws-sdk/util-user-agent-browser": "3.953.0", + "@aws-sdk/util-user-agent-node": "3.954.0", + "@smithy/config-resolver": "^4.4.4", + "@smithy/core": "^3.19.0", + "@smithy/fetch-http-handler": "^5.3.7", + "@smithy/hash-node": "^4.2.6", + "@smithy/invalid-dependency": "^4.2.6", + "@smithy/middleware-content-length": "^4.2.6", + "@smithy/middleware-endpoint": "^4.4.0", + "@smithy/middleware-retry": "^4.4.16", + "@smithy/middleware-serde": "^4.2.7", + "@smithy/middleware-stack": "^4.2.6", + "@smithy/node-config-provider": "^4.3.6", + "@smithy/node-http-handler": "^4.4.6", + "@smithy/protocol-http": "^5.3.6", + "@smithy/smithy-client": "^4.10.1", + "@smithy/types": "^4.10.0", + "@smithy/url-parser": "^4.2.6", + "@smithy/util-base64": "^4.3.0", + "@smithy/util-body-length-browser": "^4.2.0", + "@smithy/util-body-length-node": "^4.2.1", + "@smithy/util-defaults-mode-browser": "^4.3.15", + "@smithy/util-defaults-mode-node": "^4.2.18", + "@smithy/util-endpoints": "^3.2.6", + "@smithy/util-middleware": "^4.2.6", + "@smithy/util-retry": "^4.2.6", + "@smithy/util-utf8": "^4.2.0", + "tslib": "^2.6.2" + }, "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + "node": ">=18.0.0" } }, - "internal/helpers/node_modules/jest-resolve": { - "version": "29.7.0", - "dev": true, - "license": "MIT", + "node_modules/@aws-sdk/client-sqs": { + "version": "3.955.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-sqs/-/client-sqs-3.955.0.tgz", + "integrity": "sha512-EyIpQ7z0Afxn4xVv9LsgcAwVLyTAHPT8kUBbgq9cBJvzSMXzzo/IN75iCny6SWHDdxzECzUAjLvpadXdZsObFg==", + "license": "Apache-2.0", "dependencies": { - "chalk": "^4.0.0", - "graceful-fs": "^4.2.9", - "jest-haste-map": "^29.7.0", - "jest-pnp-resolver": "^1.2.2", - "jest-util": "^29.7.0", - "jest-validate": "^29.7.0", - "resolve": "^1.20.0", - "resolve.exports": "^2.0.0", - "slash": "^3.0.0" + "@aws-crypto/sha256-browser": "5.2.0", + "@aws-crypto/sha256-js": "5.2.0", + "@aws-sdk/core": "3.954.0", + "@aws-sdk/credential-provider-node": "3.955.0", + "@aws-sdk/middleware-host-header": "3.953.0", + "@aws-sdk/middleware-logger": "3.953.0", + "@aws-sdk/middleware-recursion-detection": "3.953.0", + "@aws-sdk/middleware-sdk-sqs": "3.954.0", + "@aws-sdk/middleware-user-agent": "3.954.0", + "@aws-sdk/region-config-resolver": "3.953.0", + "@aws-sdk/types": "3.953.0", + "@aws-sdk/util-endpoints": "3.953.0", + "@aws-sdk/util-user-agent-browser": "3.953.0", + "@aws-sdk/util-user-agent-node": "3.954.0", + "@smithy/config-resolver": "^4.4.4", + "@smithy/core": "^3.19.0", + "@smithy/fetch-http-handler": "^5.3.7", + "@smithy/hash-node": "^4.2.6", + "@smithy/invalid-dependency": "^4.2.6", + "@smithy/md5-js": "^4.2.6", + "@smithy/middleware-content-length": "^4.2.6", + "@smithy/middleware-endpoint": "^4.4.0", + "@smithy/middleware-retry": "^4.4.16", + "@smithy/middleware-serde": "^4.2.7", + "@smithy/middleware-stack": "^4.2.6", + "@smithy/node-config-provider": "^4.3.6", + "@smithy/node-http-handler": "^4.4.6", + "@smithy/protocol-http": "^5.3.6", + "@smithy/smithy-client": "^4.10.1", + "@smithy/types": "^4.10.0", + "@smithy/url-parser": "^4.2.6", + "@smithy/util-base64": "^4.3.0", + "@smithy/util-body-length-browser": "^4.2.0", + "@smithy/util-body-length-node": "^4.2.1", + "@smithy/util-defaults-mode-browser": "^4.3.15", + "@smithy/util-defaults-mode-node": "^4.2.18", + "@smithy/util-endpoints": "^3.2.6", + "@smithy/util-middleware": "^4.2.6", + "@smithy/util-retry": "^4.2.6", + "@smithy/util-utf8": "^4.2.0", + "tslib": "^2.6.2" }, "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + "node": ">=18.0.0" } }, - "internal/helpers/node_modules/jest-resolve-dependencies": { - "version": "29.7.0", - "dev": true, - "license": "MIT", + "node_modules/@aws-sdk/client-sso": { + "version": "3.955.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-sso/-/client-sso-3.955.0.tgz", + "integrity": "sha512-+nym5boDFt2ksba0fElocMKxCFJbJcd31PI3502hoI1N5VK7HyxkQeBtQJ64JYomvw8eARjWWC13hkB0LtZILw==", + "license": "Apache-2.0", "dependencies": { - "jest-regex-util": "^29.6.3", - "jest-snapshot": "^29.7.0" + "@aws-crypto/sha256-browser": "5.2.0", + "@aws-crypto/sha256-js": "5.2.0", + "@aws-sdk/core": "3.954.0", + "@aws-sdk/middleware-host-header": "3.953.0", + "@aws-sdk/middleware-logger": "3.953.0", + "@aws-sdk/middleware-recursion-detection": "3.953.0", + "@aws-sdk/middleware-user-agent": "3.954.0", + "@aws-sdk/region-config-resolver": "3.953.0", + "@aws-sdk/types": "3.953.0", + "@aws-sdk/util-endpoints": "3.953.0", + "@aws-sdk/util-user-agent-browser": "3.953.0", + "@aws-sdk/util-user-agent-node": "3.954.0", + "@smithy/config-resolver": "^4.4.4", + "@smithy/core": "^3.19.0", + "@smithy/fetch-http-handler": "^5.3.7", + "@smithy/hash-node": "^4.2.6", + "@smithy/invalid-dependency": "^4.2.6", + "@smithy/middleware-content-length": "^4.2.6", + "@smithy/middleware-endpoint": "^4.4.0", + "@smithy/middleware-retry": "^4.4.16", + "@smithy/middleware-serde": "^4.2.7", + "@smithy/middleware-stack": "^4.2.6", + "@smithy/node-config-provider": "^4.3.6", + "@smithy/node-http-handler": "^4.4.6", + "@smithy/protocol-http": "^5.3.6", + "@smithy/smithy-client": "^4.10.1", + "@smithy/types": "^4.10.0", + "@smithy/url-parser": "^4.2.6", + "@smithy/util-base64": "^4.3.0", + "@smithy/util-body-length-browser": "^4.2.0", + "@smithy/util-body-length-node": "^4.2.1", + "@smithy/util-defaults-mode-browser": "^4.3.15", + "@smithy/util-defaults-mode-node": "^4.2.18", + "@smithy/util-endpoints": "^3.2.6", + "@smithy/util-middleware": "^4.2.6", + "@smithy/util-retry": "^4.2.6", + "@smithy/util-utf8": "^4.2.0", + "tslib": "^2.6.2" }, "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + "node": ">=18.0.0" } }, - "internal/helpers/node_modules/jest-runner": { - "version": "29.7.0", - "dev": true, - "license": "MIT", + "node_modules/@aws-sdk/core": { + "version": "3.954.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/core/-/core-3.954.0.tgz", + "integrity": "sha512-5oYO5RP+mvCNXNj8XnF9jZo0EP0LTseYOJVNQYcii1D9DJqzHL3HJWurYh7cXxz7G7eDyvVYA01O9Xpt34TdoA==", + "license": "Apache-2.0", "dependencies": { - "@jest/console": "^29.7.0", - "@jest/environment": "^29.7.0", - "@jest/test-result": "^29.7.0", - "@jest/transform": "^29.7.0", - "@jest/types": "^29.6.3", - "@types/node": "*", - "chalk": "^4.0.0", - "emittery": "^0.13.1", - "graceful-fs": "^4.2.9", - "jest-docblock": "^29.7.0", - "jest-environment-node": "^29.7.0", - "jest-haste-map": "^29.7.0", - "jest-leak-detector": "^29.7.0", - "jest-message-util": "^29.7.0", - "jest-resolve": "^29.7.0", - "jest-runtime": "^29.7.0", - "jest-util": "^29.7.0", - "jest-watcher": "^29.7.0", - "jest-worker": "^29.7.0", - "p-limit": "^3.1.0", - "source-map-support": "0.5.13" + "@aws-sdk/types": "3.953.0", + "@aws-sdk/xml-builder": "3.953.0", + "@smithy/core": "^3.19.0", + "@smithy/node-config-provider": "^4.3.6", + "@smithy/property-provider": "^4.2.6", + "@smithy/protocol-http": "^5.3.6", + "@smithy/signature-v4": "^5.3.6", + "@smithy/smithy-client": "^4.10.1", + "@smithy/types": "^4.10.0", + "@smithy/util-base64": "^4.3.0", + "@smithy/util-middleware": "^4.2.6", + "@smithy/util-utf8": "^4.2.0", + "tslib": "^2.6.2" }, "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + "node": ">=18.0.0" } }, - "internal/helpers/node_modules/jest-runtime": { - "version": "29.7.0", - "dev": true, - "license": "MIT", + "node_modules/@aws-sdk/credential-provider-env": { + "version": "3.954.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-env/-/credential-provider-env-3.954.0.tgz", + "integrity": "sha512-2HNkqBjfsvyoRuPAiFh86JBFMFyaCNhL4VyH6XqwTGKZffjG7hdBmzXPy7AT7G3oFh1k/1Zc27v0qxaKoK7mBA==", + "license": "Apache-2.0", "dependencies": { - "@jest/environment": "^29.7.0", - "@jest/fake-timers": "^29.7.0", - "@jest/globals": "^29.7.0", - "@jest/source-map": "^29.6.3", - "@jest/test-result": "^29.7.0", - "@jest/transform": "^29.7.0", - "@jest/types": "^29.6.3", - "@types/node": "*", - "chalk": "^4.0.0", - "cjs-module-lexer": "^1.0.0", - "collect-v8-coverage": "^1.0.0", - "glob": "^7.1.3", - "graceful-fs": "^4.2.9", - "jest-haste-map": "^29.7.0", - "jest-message-util": "^29.7.0", - "jest-mock": "^29.7.0", - "jest-regex-util": "^29.6.3", - "jest-resolve": "^29.7.0", - "jest-snapshot": "^29.7.0", - "jest-util": "^29.7.0", - "slash": "^3.0.0", - "strip-bom": "^4.0.0" + "@aws-sdk/core": "3.954.0", + "@aws-sdk/types": "3.953.0", + "@smithy/property-provider": "^4.2.6", + "@smithy/types": "^4.10.0", + "tslib": "^2.6.2" }, "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + "node": ">=18.0.0" } }, - "internal/helpers/node_modules/jest-snapshot": { - "version": "29.7.0", - "dev": true, - "license": "MIT", + "node_modules/@aws-sdk/credential-provider-http": { + "version": "3.954.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-http/-/credential-provider-http-3.954.0.tgz", + "integrity": "sha512-CrWD5300+NE1OYRnSVDxoG7G0b5cLIZb7yp+rNQ5Jq/kqnTmyJXpVAsivq+bQIDaGzPXhadzpAMIoo7K/aHaag==", + "license": "Apache-2.0", "dependencies": { - "@babel/core": "^7.11.6", - "@babel/generator": "^7.7.2", - "@babel/plugin-syntax-jsx": "^7.7.2", - "@babel/plugin-syntax-typescript": "^7.7.2", - "@babel/types": "^7.3.3", - "@jest/expect-utils": "^29.7.0", - "@jest/transform": "^29.7.0", - "@jest/types": "^29.6.3", - "babel-preset-current-node-syntax": "^1.0.0", - "chalk": "^4.0.0", - "expect": "^29.7.0", - "graceful-fs": "^4.2.9", - "jest-diff": "^29.7.0", - "jest-get-type": "^29.6.3", - "jest-matcher-utils": "^29.7.0", - "jest-message-util": "^29.7.0", - "jest-util": "^29.7.0", - "natural-compare": "^1.4.0", - "pretty-format": "^29.7.0", - "semver": "^7.5.3" + "@aws-sdk/core": "3.954.0", + "@aws-sdk/types": "3.953.0", + "@smithy/fetch-http-handler": "^5.3.7", + "@smithy/node-http-handler": "^4.4.6", + "@smithy/property-provider": "^4.2.6", + "@smithy/protocol-http": "^5.3.6", + "@smithy/smithy-client": "^4.10.1", + "@smithy/types": "^4.10.0", + "@smithy/util-stream": "^4.5.7", + "tslib": "^2.6.2" }, "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + "node": ">=18.0.0" } }, - "internal/helpers/node_modules/jest-validate": { - "version": "29.7.0", - "dev": true, - "license": "MIT", + "node_modules/@aws-sdk/credential-provider-ini": { + "version": "3.955.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-ini/-/credential-provider-ini-3.955.0.tgz", + "integrity": "sha512-90isLovxsPzaaSx3IIUZuxym6VXrsRetnQ3AuHr2kiTFk2pIzyIwmi+gDcUaLXQ5nNBoSj1Z/4+i1vhxa1n2DQ==", + "license": "Apache-2.0", "dependencies": { - "@jest/types": "^29.6.3", - "camelcase": "^6.2.0", - "chalk": "^4.0.0", - "jest-get-type": "^29.6.3", - "leven": "^3.1.0", - "pretty-format": "^29.7.0" + "@aws-sdk/core": "3.954.0", + "@aws-sdk/credential-provider-env": "3.954.0", + "@aws-sdk/credential-provider-http": "3.954.0", + "@aws-sdk/credential-provider-login": "3.955.0", + "@aws-sdk/credential-provider-process": "3.954.0", + "@aws-sdk/credential-provider-sso": "3.955.0", + "@aws-sdk/credential-provider-web-identity": "3.955.0", + "@aws-sdk/nested-clients": "3.955.0", + "@aws-sdk/types": "3.953.0", + "@smithy/credential-provider-imds": "^4.2.6", + "@smithy/property-provider": "^4.2.6", + "@smithy/shared-ini-file-loader": "^4.4.1", + "@smithy/types": "^4.10.0", + "tslib": "^2.6.2" }, "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + "node": ">=18.0.0" } }, - "internal/helpers/node_modules/jest-watcher": { - "version": "29.7.0", - "dev": true, - "license": "MIT", + "node_modules/@aws-sdk/credential-provider-login": { + "version": "3.955.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-login/-/credential-provider-login-3.955.0.tgz", + "integrity": "sha512-xlkmSvg8oDN5LIxLAq3N1QWK8F8gUAsBWZlp1IX8Lr5XhcKI3GVarIIUcZrvCy1NjzCd/LDXYdNL6MRlNP4bAw==", + "license": "Apache-2.0", "dependencies": { - "@jest/test-result": "^29.7.0", - "@jest/types": "^29.6.3", - "@types/node": "*", - "ansi-escapes": "^4.2.1", - "chalk": "^4.0.0", - "emittery": "^0.13.1", - "jest-util": "^29.7.0", - "string-length": "^4.0.1" + "@aws-sdk/core": "3.954.0", + "@aws-sdk/nested-clients": "3.955.0", + "@aws-sdk/types": "3.953.0", + "@smithy/property-provider": "^4.2.6", + "@smithy/protocol-http": "^5.3.6", + "@smithy/shared-ini-file-loader": "^4.4.1", + "@smithy/types": "^4.10.0", + "tslib": "^2.6.2" }, "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + "node": ">=18.0.0" } }, - "internal/helpers/node_modules/jest-worker": { - "version": "29.7.0", - "dev": true, - "license": "MIT", + "node_modules/@aws-sdk/credential-provider-node": { + "version": "3.955.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-node/-/credential-provider-node-3.955.0.tgz", + "integrity": "sha512-XIL4QB+dPOJA6DRTmYZL52wFcLTslb7V1ydS4FCNT2DVLhkO4ExkPP+pe5YmIpzt/Our1ugS+XxAs3e6BtyFjA==", + "license": "Apache-2.0", "dependencies": { - "@types/node": "*", - "jest-util": "^29.7.0", - "merge-stream": "^2.0.0", - "supports-color": "^8.0.0" + "@aws-sdk/credential-provider-env": "3.954.0", + "@aws-sdk/credential-provider-http": "3.954.0", + "@aws-sdk/credential-provider-ini": "3.955.0", + "@aws-sdk/credential-provider-process": "3.954.0", + "@aws-sdk/credential-provider-sso": "3.955.0", + "@aws-sdk/credential-provider-web-identity": "3.955.0", + "@aws-sdk/types": "3.953.0", + "@smithy/credential-provider-imds": "^4.2.6", + "@smithy/property-provider": "^4.2.6", + "@smithy/shared-ini-file-loader": "^4.4.1", + "@smithy/types": "^4.10.0", + "tslib": "^2.6.2" }, "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + "node": ">=18.0.0" } }, - "internal/helpers/node_modules/minimatch": { - "version": "3.1.2", - "dev": true, - "license": "ISC", + "node_modules/@aws-sdk/credential-provider-process": { + "version": "3.954.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-process/-/credential-provider-process-3.954.0.tgz", + "integrity": "sha512-Y1/0O2LgbKM8iIgcVj/GNEQW6p90LVTCOzF2CI1pouoKqxmZ/1F7F66WHoa6XUOfKaCRj/R6nuMR3om9ThaM5A==", + "license": "Apache-2.0", "dependencies": { - "brace-expansion": "^1.1.7" + "@aws-sdk/core": "3.954.0", + "@aws-sdk/types": "3.953.0", + "@smithy/property-provider": "^4.2.6", + "@smithy/shared-ini-file-loader": "^4.4.1", + "@smithy/types": "^4.10.0", + "tslib": "^2.6.2" }, "engines": { - "node": "*" + "node": ">=18.0.0" } }, - "internal/helpers/node_modules/pure-rand": { - "version": "6.1.0", - "dev": true, - "funding": [ - { - "type": "individual", - "url": "https://github.com/sponsors/dubzzz" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/fast-check" - } - ], - "license": "MIT" - }, - "internal/helpers/node_modules/signal-exit": { - "version": "3.0.7", - "dev": true, - "license": "ISC" - }, - "internal/helpers/node_modules/supports-color": { - "version": "8.1.1", - "dev": true, - "license": "MIT", + "node_modules/@aws-sdk/credential-provider-sso": { + "version": "3.955.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-sso/-/credential-provider-sso-3.955.0.tgz", + "integrity": "sha512-Y99KI73Fn8JnB4RY5Ls6j7rd5jmFFwnY9WLHIWeJdc+vfwL6Bb1uWKW3+m/B9+RC4Xoz2nQgtefBcdWq5Xx8iw==", + "license": "Apache-2.0", "dependencies": { - "has-flag": "^4.0.0" + "@aws-sdk/client-sso": "3.955.0", + "@aws-sdk/core": "3.954.0", + "@aws-sdk/token-providers": "3.955.0", + "@aws-sdk/types": "3.953.0", + "@smithy/property-provider": "^4.2.6", + "@smithy/shared-ini-file-loader": "^4.4.1", + "@smithy/types": "^4.10.0", + "tslib": "^2.6.2" }, "engines": { - "node": ">=10" + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-web-identity": { + "version": "3.955.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-web-identity/-/credential-provider-web-identity-3.955.0.tgz", + "integrity": "sha512-+lFxkZ2Vz3qp/T68ZONKzWVTQvomTu7E6tts1dfAbEcDt62Y/nPCByq/C2hQj+TiN05HrUx+yTJaGHBklhkbqA==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "3.954.0", + "@aws-sdk/nested-clients": "3.955.0", + "@aws-sdk/types": "3.953.0", + "@smithy/property-provider": "^4.2.6", + "@smithy/shared-ini-file-loader": "^4.4.1", + "@smithy/types": "^4.10.0", + "tslib": "^2.6.2" }, - "funding": { - "url": "https://github.com/chalk/supports-color?sponsor=1" + "engines": { + "node": ">=18.0.0" } }, - "internal/helpers/node_modules/wrap-ansi": { - "version": "7.0.0", - "dev": true, - "license": "MIT", + "node_modules/@aws-sdk/dynamodb-codec": { + "version": "3.954.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/dynamodb-codec/-/dynamodb-codec-3.954.0.tgz", + "integrity": "sha512-fNY0L1l9e36pLJef4NY5k7Q1SJnm5rgLSxSRPT8xL+bhQBtEn2E2t4JdBiTqlxgeyuOrafWOawbg8yS3pRPcLw==", + "license": "Apache-2.0", "dependencies": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" + "@aws-sdk/core": "3.954.0", + "@smithy/core": "^3.19.0", + "@smithy/smithy-client": "^4.10.1", + "@smithy/types": "^4.10.0", + "@smithy/util-base64": "^4.3.0", + "tslib": "^2.6.2" }, "engines": { - "node": ">=10" + "node": ">=18.0.0" }, - "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + "peerDependencies": { + "@aws-sdk/client-dynamodb": "^3.954.0" } }, - "internal/helpers/node_modules/write-file-atomic": { - "version": "4.0.2", - "dev": true, - "license": "ISC", + "node_modules/@aws-sdk/endpoint-cache": { + "version": "3.953.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/endpoint-cache/-/endpoint-cache-3.953.0.tgz", + "integrity": "sha512-pz67DoHk5WNmvMuyNDiomUS2xo0mq6Z3TdfLJZlWVbSKi3h8hYxVQchJ2kzgTr6wu6zt3UBbtKV9yY1IBhKMVA==", + "license": "Apache-2.0", "dependencies": { - "imurmurhash": "^0.1.4", - "signal-exit": "^3.0.7" + "mnemonist": "0.38.3", + "tslib": "^2.6.2" }, "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + "node": ">=18.0.0" } }, - "internal/helpers/node_modules/yargs": { - "version": "17.7.2", - "dev": true, - "license": "MIT", + "node_modules/@aws-sdk/lib-dynamodb": { + "version": "3.955.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/lib-dynamodb/-/lib-dynamodb-3.955.0.tgz", + "integrity": "sha512-THm4It0gSWTQZDK+I7J/D1FmoNGXDHRpeYrnJ1uYx2z4TSWc8qqqHN2L/x3W8HtRITMUNUolCZI4tIz9zxdOow==", + "license": "Apache-2.0", "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" + "@aws-sdk/core": "3.954.0", + "@aws-sdk/util-dynamodb": "3.955.0", + "@smithy/core": "^3.19.0", + "@smithy/smithy-client": "^4.10.1", + "@smithy/types": "^4.10.0", + "tslib": "^2.6.2" }, "engines": { - "node": ">=12" + "node": ">=18.0.0" + }, + "peerDependencies": { + "@aws-sdk/client-dynamodb": "^3.955.0" } }, - "internal/helpers/node_modules/zod": { - "version": "4.1.12", - "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/colinhacks" + "node_modules/@aws-sdk/middleware-bucket-endpoint": { + "version": "3.953.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-bucket-endpoint/-/middleware-bucket-endpoint-3.953.0.tgz", + "integrity": "sha512-YHVRIOowtGIl/L2WuS83FgRlm31tU0aL1yryWaFtF+AFjA5BIeiFkxIZqaRGxJpJvFEBdohsyq6Ipv5mgWfezg==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.953.0", + "@aws-sdk/util-arn-parser": "3.953.0", + "@smithy/node-config-provider": "^4.3.6", + "@smithy/protocol-http": "^5.3.6", + "@smithy/types": "^4.10.0", + "@smithy/util-config-provider": "^4.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" } }, - "lambdas/api-handler": { - "name": "nhs-notify-supplier-api-handler", - "version": "0.0.1", + "node_modules/@aws-sdk/middleware-endpoint-discovery": { + "version": "3.953.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-endpoint-discovery/-/middleware-endpoint-discovery-3.953.0.tgz", + "integrity": "sha512-/YKB1/OiWr7TwOfmkqzv8x1xgOpU71yciQTfsq6erB3dTQhdukPADt/CMJOhWFKC6Q1D5cDN8381nsGmnNuBVg==", + "license": "Apache-2.0", "dependencies": { - "@internal/datastore": "*", - "@internal/helpers": "*", - "esbuild": "^0.25.11", - "pino": "^9.7.0" + "@aws-sdk/endpoint-cache": "3.953.0", + "@aws-sdk/types": "3.953.0", + "@smithy/node-config-provider": "^4.3.6", + "@smithy/protocol-http": "^5.3.6", + "@smithy/types": "^4.10.0", + "tslib": "^2.6.2" }, - "devDependencies": { - "@aws-sdk/s3-request-presigner": "^3.901.0", - "@tsconfig/node22": "^22.0.2", - "@types/aws-lambda": "^8.10.148", - "@types/jest": "^29.5.14", - "jest": "^30.2.0", - "jest-mock-extended": "^3.0.7", - "typescript": "^5.8.3", - "zod": "^4.1.11" + "engines": { + "node": ">=18.0.0" } }, - "lambdas/api-handler/node_modules/@jest/core": { - "version": "29.7.0", - "dev": true, - "license": "MIT", + "node_modules/@aws-sdk/middleware-expect-continue": { + "version": "3.953.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-expect-continue/-/middleware-expect-continue-3.953.0.tgz", + "integrity": "sha512-BQTVXrypQ0rbb7au/Hk4IS5GaJZlwk6O44Rjk6Kxb0IvGQhSurNTuesFiJx1sLbf+w+T31saPtODcfQQERqhCQ==", + "license": "Apache-2.0", "dependencies": { - "@jest/console": "^29.7.0", - "@jest/reporters": "^29.7.0", - "@jest/test-result": "^29.7.0", - "@jest/transform": "^29.7.0", - "@jest/types": "^29.6.3", - "@types/node": "*", - "ansi-escapes": "^4.2.1", - "chalk": "^4.0.0", - "ci-info": "^3.2.0", - "exit": "^0.1.2", - "graceful-fs": "^4.2.9", - "jest-changed-files": "^29.7.0", - "jest-config": "^29.7.0", - "jest-haste-map": "^29.7.0", - "jest-message-util": "^29.7.0", - "jest-regex-util": "^29.6.3", - "jest-resolve": "^29.7.0", - "jest-resolve-dependencies": "^29.7.0", - "jest-runner": "^29.7.0", - "jest-runtime": "^29.7.0", - "jest-snapshot": "^29.7.0", - "jest-util": "^29.7.0", - "jest-validate": "^29.7.0", - "jest-watcher": "^29.7.0", - "micromatch": "^4.0.4", - "pretty-format": "^29.7.0", - "slash": "^3.0.0", - "strip-ansi": "^6.0.0" + "@aws-sdk/types": "3.953.0", + "@smithy/protocol-http": "^5.3.6", + "@smithy/types": "^4.10.0", + "tslib": "^2.6.2" }, "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - }, - "peerDependencies": { - "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" - }, - "peerDependenciesMeta": { - "node-notifier": { - "optional": true - } + "node": ">=18.0.0" } }, - "lambdas/api-handler/node_modules/@jest/environment": { - "version": "29.7.0", - "dev": true, - "license": "MIT", + "node_modules/@aws-sdk/middleware-flexible-checksums": { + "version": "3.954.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-flexible-checksums/-/middleware-flexible-checksums-3.954.0.tgz", + "integrity": "sha512-hHOPDJyxucNodkgapLhA0VdwDBwVYN9DX20aA6j+3nwutAlZ5skaV7Bw0W3YC7Fh/ieDKKhcSZulONd4lVTwMg==", + "license": "Apache-2.0", "dependencies": { - "@jest/fake-timers": "^29.7.0", - "@jest/types": "^29.6.3", - "@types/node": "*", - "jest-mock": "^29.7.0" + "@aws-crypto/crc32": "5.2.0", + "@aws-crypto/crc32c": "5.2.0", + "@aws-crypto/util": "5.2.0", + "@aws-sdk/core": "3.954.0", + "@aws-sdk/types": "3.953.0", + "@smithy/is-array-buffer": "^4.2.0", + "@smithy/node-config-provider": "^4.3.6", + "@smithy/protocol-http": "^5.3.6", + "@smithy/types": "^4.10.0", + "@smithy/util-middleware": "^4.2.6", + "@smithy/util-stream": "^4.5.7", + "@smithy/util-utf8": "^4.2.0", + "tslib": "^2.6.2" }, "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + "node": ">=18.0.0" } }, - "lambdas/api-handler/node_modules/@jest/expect": { - "version": "29.7.0", - "dev": true, - "license": "MIT", + "node_modules/@aws-sdk/middleware-host-header": { + "version": "3.953.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-host-header/-/middleware-host-header-3.953.0.tgz", + "integrity": "sha512-jTGhfkONav+r4E6HLOrl5SzBqDmPByUYCkyB/c/3TVb8jX3wAZx8/q9bphKpCh+G5ARi3IdbSisgkZrJYqQ19Q==", + "license": "Apache-2.0", "dependencies": { - "expect": "^29.7.0", - "jest-snapshot": "^29.7.0" + "@aws-sdk/types": "3.953.0", + "@smithy/protocol-http": "^5.3.6", + "@smithy/types": "^4.10.0", + "tslib": "^2.6.2" }, "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + "node": ">=18.0.0" } }, - "lambdas/api-handler/node_modules/@jest/fake-timers": { - "version": "29.7.0", - "dev": true, - "license": "MIT", + "node_modules/@aws-sdk/middleware-location-constraint": { + "version": "3.953.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-location-constraint/-/middleware-location-constraint-3.953.0.tgz", + "integrity": "sha512-h0urrbteIQEybyIISaJfQLZ/+/lJPRzPWAQT4epvzfgv/4MKZI7K83dK7SfTwAooVKFBHiCMok2Cf0iHDt07Kw==", + "license": "Apache-2.0", "dependencies": { - "@jest/types": "^29.6.3", - "@sinonjs/fake-timers": "^10.0.2", - "@types/node": "*", - "jest-message-util": "^29.7.0", - "jest-mock": "^29.7.0", - "jest-util": "^29.7.0" + "@aws-sdk/types": "3.953.0", + "@smithy/types": "^4.10.0", + "tslib": "^2.6.2" }, "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + "node": ">=18.0.0" } }, - "lambdas/api-handler/node_modules/@jest/globals": { - "version": "29.7.0", - "dev": true, - "license": "MIT", + "node_modules/@aws-sdk/middleware-logger": { + "version": "3.953.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-logger/-/middleware-logger-3.953.0.tgz", + "integrity": "sha512-PlWdVYgcuptkIC0ZKqVUhWNtSHXJSx7U9V8J7dJjRmsXC40X7zpEycvrkzDMJjeTDGcCceYbyYAg/4X1lkcIMw==", + "license": "Apache-2.0", "dependencies": { - "@jest/environment": "^29.7.0", - "@jest/expect": "^29.7.0", - "@jest/types": "^29.6.3", - "jest-mock": "^29.7.0" + "@aws-sdk/types": "3.953.0", + "@smithy/types": "^4.10.0", + "tslib": "^2.6.2" }, "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + "node": ">=18.0.0" } }, - "lambdas/api-handler/node_modules/@jest/reporters": { - "version": "29.7.0", - "dev": true, - "license": "MIT", + "node_modules/@aws-sdk/middleware-recursion-detection": { + "version": "3.953.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-recursion-detection/-/middleware-recursion-detection-3.953.0.tgz", + "integrity": "sha512-cmIJx0gWeesUKK4YwgE+VQL3mpACr3/J24fbwnc1Z5tntC86b+HQFzU5vsBDw6lLwyD46dBgWdsXFh1jL+ZaFw==", + "license": "Apache-2.0", "dependencies": { - "@bcoe/v8-coverage": "^0.2.3", - "@jest/console": "^29.7.0", - "@jest/test-result": "^29.7.0", - "@jest/transform": "^29.7.0", - "@jest/types": "^29.6.3", - "@jridgewell/trace-mapping": "^0.3.18", - "@types/node": "*", - "chalk": "^4.0.0", - "collect-v8-coverage": "^1.0.0", - "exit": "^0.1.2", - "glob": "^7.1.3", - "graceful-fs": "^4.2.9", - "istanbul-lib-coverage": "^3.0.0", - "istanbul-lib-instrument": "^6.0.0", - "istanbul-lib-report": "^3.0.0", - "istanbul-lib-source-maps": "^4.0.0", - "istanbul-reports": "^3.1.3", - "jest-message-util": "^29.7.0", - "jest-util": "^29.7.0", - "jest-worker": "^29.7.0", - "slash": "^3.0.0", - "string-length": "^4.0.1", - "strip-ansi": "^6.0.0", - "v8-to-istanbul": "^9.0.1" + "@aws-sdk/types": "3.953.0", + "@aws/lambda-invoke-store": "^0.2.2", + "@smithy/protocol-http": "^5.3.6", + "@smithy/types": "^4.10.0", + "tslib": "^2.6.2" }, "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - }, - "peerDependencies": { - "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" - }, - "peerDependenciesMeta": { - "node-notifier": { - "optional": true - } + "node": ">=18.0.0" } }, - "lambdas/api-handler/node_modules/@jest/source-map": { - "version": "29.6.3", - "dev": true, - "license": "MIT", + "node_modules/@aws-sdk/middleware-sdk-api-gateway": { + "version": "3.953.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-sdk-api-gateway/-/middleware-sdk-api-gateway-3.953.0.tgz", + "integrity": "sha512-4LVl1QJNUIEgj+/PmvtlIJee9jR1JvFugZuZJXHFZYDngWrdIGpGaRD6CKEVHVrjlQjY/jWJJzzZ3Ztvsq68hA==", + "license": "Apache-2.0", "dependencies": { - "@jridgewell/trace-mapping": "^0.3.18", - "callsites": "^3.0.0", - "graceful-fs": "^4.2.9" + "@aws-sdk/types": "3.953.0", + "@smithy/protocol-http": "^5.3.6", + "@smithy/types": "^4.10.0", + "tslib": "^2.6.2" }, "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + "node": ">=18.0.0" } }, - "lambdas/api-handler/node_modules/@jest/test-sequencer": { - "version": "29.7.0", - "dev": true, - "license": "MIT", + "node_modules/@aws-sdk/middleware-sdk-s3": { + "version": "3.954.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-sdk-s3/-/middleware-sdk-s3-3.954.0.tgz", + "integrity": "sha512-274CNmnRjknmfFb2o0Azxic54fnujaA8AYSeRUOho3lN48TVzx85eAFWj2kLgvUJO88pE3jBDPWboKQiQdXeUQ==", + "license": "Apache-2.0", "dependencies": { - "@jest/test-result": "^29.7.0", - "graceful-fs": "^4.2.9", - "jest-haste-map": "^29.7.0", - "slash": "^3.0.0" + "@aws-sdk/core": "3.954.0", + "@aws-sdk/types": "3.953.0", + "@aws-sdk/util-arn-parser": "3.953.0", + "@smithy/core": "^3.19.0", + "@smithy/node-config-provider": "^4.3.6", + "@smithy/protocol-http": "^5.3.6", + "@smithy/signature-v4": "^5.3.6", + "@smithy/smithy-client": "^4.10.1", + "@smithy/types": "^4.10.0", + "@smithy/util-config-provider": "^4.2.0", + "@smithy/util-middleware": "^4.2.6", + "@smithy/util-stream": "^4.5.7", + "@smithy/util-utf8": "^4.2.0", + "tslib": "^2.6.2" }, "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + "node": ">=18.0.0" } }, - "lambdas/api-handler/node_modules/@jest/transform": { - "version": "29.7.0", - "dev": true, - "license": "MIT", + "node_modules/@aws-sdk/middleware-sdk-sqs": { + "version": "3.954.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-sdk-sqs/-/middleware-sdk-sqs-3.954.0.tgz", + "integrity": "sha512-MIrlTd4kiNyKMJP8fQ2U57D672uB4JfZYmm9wnKczaQ9zpIDvZKlQFnPGldiedCHeFke5fU46i+FYtF7UX/EfQ==", + "license": "Apache-2.0", "dependencies": { - "@babel/core": "^7.11.6", - "@jest/types": "^29.6.3", - "@jridgewell/trace-mapping": "^0.3.18", - "babel-plugin-istanbul": "^6.1.1", - "chalk": "^4.0.0", - "convert-source-map": "^2.0.0", - "fast-json-stable-stringify": "^2.1.0", - "graceful-fs": "^4.2.9", - "jest-haste-map": "^29.7.0", - "jest-regex-util": "^29.6.3", - "jest-util": "^29.7.0", - "micromatch": "^4.0.4", - "pirates": "^4.0.4", - "slash": "^3.0.0", - "write-file-atomic": "^4.0.2" + "@aws-sdk/types": "3.953.0", + "@smithy/smithy-client": "^4.10.1", + "@smithy/types": "^4.10.0", + "@smithy/util-hex-encoding": "^4.2.0", + "@smithy/util-utf8": "^4.2.0", + "tslib": "^2.6.2" }, "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + "node": ">=18.0.0" } }, - "lambdas/api-handler/node_modules/@sinonjs/fake-timers": { - "version": "10.3.0", - "dev": true, - "license": "BSD-3-Clause", + "node_modules/@aws-sdk/middleware-ssec": { + "version": "3.953.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-ssec/-/middleware-ssec-3.953.0.tgz", + "integrity": "sha512-OrhG1kcQ9zZh3NS3RovR028N0+UndQ957zF1k5HPLeFLwFwQN1uPOufzzPzAyXIIKtR69ARFsQI4mstZS4DMvw==", + "license": "Apache-2.0", "dependencies": { - "@sinonjs/commons": "^3.0.0" + "@aws-sdk/types": "3.953.0", + "@smithy/types": "^4.10.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" } }, - "lambdas/api-handler/node_modules/@types/jest": { - "version": "29.5.14", - "dev": true, - "license": "MIT", + "node_modules/@aws-sdk/middleware-user-agent": { + "version": "3.954.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-user-agent/-/middleware-user-agent-3.954.0.tgz", + "integrity": "sha512-5PX8JDe3dB2+MqXeGIhmgFnm2rbVsSxhz+Xyuu1oxLtbOn+a9UDA+sNBufEBjt3UxWy5qwEEY1fxdbXXayjlGg==", + "license": "Apache-2.0", "dependencies": { - "expect": "^29.0.0", - "pretty-format": "^29.0.0" + "@aws-sdk/core": "3.954.0", + "@aws-sdk/types": "3.953.0", + "@aws-sdk/util-endpoints": "3.953.0", + "@smithy/core": "^3.19.0", + "@smithy/protocol-http": "^5.3.6", + "@smithy/types": "^4.10.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" } }, - "lambdas/api-handler/node_modules/babel-jest": { - "version": "29.7.0", - "dev": true, - "license": "MIT", + "node_modules/@aws-sdk/nested-clients": { + "version": "3.955.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/nested-clients/-/nested-clients-3.955.0.tgz", + "integrity": "sha512-RBi6CQHbPF09kqXAoiEOOPkVnSoU5YppKoOt/cgsWfoMHwC+7itIrEv+yRD62h14jIjF3KngVIQIrBRbX3o3/Q==", + "license": "Apache-2.0", "dependencies": { - "@jest/transform": "^29.7.0", - "@types/babel__core": "^7.1.14", - "babel-plugin-istanbul": "^6.1.1", - "babel-preset-jest": "^29.6.3", - "chalk": "^4.0.0", - "graceful-fs": "^4.2.9", - "slash": "^3.0.0" + "@aws-crypto/sha256-browser": "5.2.0", + "@aws-crypto/sha256-js": "5.2.0", + "@aws-sdk/core": "3.954.0", + "@aws-sdk/middleware-host-header": "3.953.0", + "@aws-sdk/middleware-logger": "3.953.0", + "@aws-sdk/middleware-recursion-detection": "3.953.0", + "@aws-sdk/middleware-user-agent": "3.954.0", + "@aws-sdk/region-config-resolver": "3.953.0", + "@aws-sdk/types": "3.953.0", + "@aws-sdk/util-endpoints": "3.953.0", + "@aws-sdk/util-user-agent-browser": "3.953.0", + "@aws-sdk/util-user-agent-node": "3.954.0", + "@smithy/config-resolver": "^4.4.4", + "@smithy/core": "^3.19.0", + "@smithy/fetch-http-handler": "^5.3.7", + "@smithy/hash-node": "^4.2.6", + "@smithy/invalid-dependency": "^4.2.6", + "@smithy/middleware-content-length": "^4.2.6", + "@smithy/middleware-endpoint": "^4.4.0", + "@smithy/middleware-retry": "^4.4.16", + "@smithy/middleware-serde": "^4.2.7", + "@smithy/middleware-stack": "^4.2.6", + "@smithy/node-config-provider": "^4.3.6", + "@smithy/node-http-handler": "^4.4.6", + "@smithy/protocol-http": "^5.3.6", + "@smithy/smithy-client": "^4.10.1", + "@smithy/types": "^4.10.0", + "@smithy/url-parser": "^4.2.6", + "@smithy/util-base64": "^4.3.0", + "@smithy/util-body-length-browser": "^4.2.0", + "@smithy/util-body-length-node": "^4.2.1", + "@smithy/util-defaults-mode-browser": "^4.3.15", + "@smithy/util-defaults-mode-node": "^4.2.18", + "@smithy/util-endpoints": "^3.2.6", + "@smithy/util-middleware": "^4.2.6", + "@smithy/util-retry": "^4.2.6", + "@smithy/util-utf8": "^4.2.0", + "tslib": "^2.6.2" }, "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - }, - "peerDependencies": { - "@babel/core": "^7.8.0" + "node": ">=18.0.0" } }, - "lambdas/api-handler/node_modules/babel-plugin-istanbul": { - "version": "6.1.1", - "dev": true, - "license": "BSD-3-Clause", + "node_modules/@aws-sdk/region-config-resolver": { + "version": "3.953.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/region-config-resolver/-/region-config-resolver-3.953.0.tgz", + "integrity": "sha512-5MJgnsc+HLO+le0EK1cy92yrC7kyhGZSpaq8PcQvKs9qtXCXT5Tb6tMdkr5Y07JxYsYOV1omWBynvL6PWh08tQ==", + "license": "Apache-2.0", "dependencies": { - "@babel/helper-plugin-utils": "^7.0.0", - "@istanbuljs/load-nyc-config": "^1.0.0", - "@istanbuljs/schema": "^0.1.2", - "istanbul-lib-instrument": "^5.0.4", - "test-exclude": "^6.0.0" + "@aws-sdk/types": "3.953.0", + "@smithy/config-resolver": "^4.4.4", + "@smithy/node-config-provider": "^4.3.6", + "@smithy/types": "^4.10.0", + "tslib": "^2.6.2" }, "engines": { - "node": ">=8" + "node": ">=18.0.0" } }, - "lambdas/api-handler/node_modules/babel-plugin-istanbul/node_modules/istanbul-lib-instrument": { - "version": "5.2.1", - "dev": true, - "license": "BSD-3-Clause", + "node_modules/@aws-sdk/s3-request-presigner": { + "version": "3.955.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/s3-request-presigner/-/s3-request-presigner-3.955.0.tgz", + "integrity": "sha512-wd2JskXnhT1bf4xoboUSbY0ptbmmgjnDLht+ob5e/P9Zy8Jn3ttLTmiMH+/M+4nLy/IExOo3VE/wgVyf9DlQxg==", + "license": "Apache-2.0", "dependencies": { - "@babel/core": "^7.12.3", - "@babel/parser": "^7.14.7", - "@istanbuljs/schema": "^0.1.2", - "istanbul-lib-coverage": "^3.2.0", - "semver": "^6.3.0" + "@aws-sdk/signature-v4-multi-region": "3.954.0", + "@aws-sdk/types": "3.953.0", + "@aws-sdk/util-format-url": "3.953.0", + "@smithy/middleware-endpoint": "^4.4.0", + "@smithy/protocol-http": "^5.3.6", + "@smithy/smithy-client": "^4.10.1", + "@smithy/types": "^4.10.0", + "tslib": "^2.6.2" }, "engines": { - "node": ">=8" - } - }, - "lambdas/api-handler/node_modules/babel-plugin-istanbul/node_modules/semver": { - "version": "6.3.1", - "dev": true, - "license": "ISC", - "bin": { - "semver": "bin/semver.js" + "node": ">=18.0.0" } }, - "lambdas/api-handler/node_modules/babel-plugin-jest-hoist": { - "version": "29.6.3", - "dev": true, - "license": "MIT", + "node_modules/@aws-sdk/signature-v4-multi-region": { + "version": "3.954.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/signature-v4-multi-region/-/signature-v4-multi-region-3.954.0.tgz", + "integrity": "sha512-GJJbUaSlGrMSRWui3Oz8ByygpQlzDGm195yTKirgGyu4tfYrFr/QWrWT42EUktY/L4Irev1pdHTuLS+AGHO1gw==", + "license": "Apache-2.0", "dependencies": { - "@babel/template": "^7.3.3", - "@babel/types": "^7.3.3", - "@types/babel__core": "^7.1.14", - "@types/babel__traverse": "^7.0.6" + "@aws-sdk/middleware-sdk-s3": "3.954.0", + "@aws-sdk/types": "3.953.0", + "@smithy/protocol-http": "^5.3.6", + "@smithy/signature-v4": "^5.3.6", + "@smithy/types": "^4.10.0", + "tslib": "^2.6.2" }, "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + "node": ">=18.0.0" } }, - "lambdas/api-handler/node_modules/babel-preset-jest": { - "version": "29.6.3", - "dev": true, - "license": "MIT", + "node_modules/@aws-sdk/token-providers": { + "version": "3.955.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/token-providers/-/token-providers-3.955.0.tgz", + "integrity": "sha512-LVpWkxXvMPgZofP2Gc8XBfQhsyecBMVARDHWMvks6vPbCLSTM7dw6H1HI9qbGNCurYcyc2xBRAkEDhChQlbPPg==", + "license": "Apache-2.0", "dependencies": { - "babel-plugin-jest-hoist": "^29.6.3", - "babel-preset-current-node-syntax": "^1.0.0" + "@aws-sdk/core": "3.954.0", + "@aws-sdk/nested-clients": "3.955.0", + "@aws-sdk/types": "3.953.0", + "@smithy/property-provider": "^4.2.6", + "@smithy/shared-ini-file-loader": "^4.4.1", + "@smithy/types": "^4.10.0", + "tslib": "^2.6.2" }, "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" + "node": ">=18.0.0" } }, - "lambdas/api-handler/node_modules/brace-expansion": { - "version": "1.1.12", - "dev": true, - "license": "MIT", + "node_modules/@aws-sdk/types": { + "version": "3.953.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.953.0.tgz", + "integrity": "sha512-M9Iwg9kTyqTErI0vOTVVpcnTHWzS3VplQppy8MuL02EE+mJ0BIwpWfsaAPQW+/XnVpdNpWZTsHcNE29f1+hR8g==", + "license": "Apache-2.0", "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "lambdas/api-handler/node_modules/camelcase": { - "version": "6.3.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10" + "@smithy/types": "^4.10.0", + "tslib": "^2.6.2" }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "lambdas/api-handler/node_modules/ci-info": { - "version": "3.9.0", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/sibiraj-s" - } - ], - "license": "MIT", "engines": { - "node": ">=8" + "node": ">=18.0.0" } }, - "lambdas/api-handler/node_modules/cjs-module-lexer": { - "version": "1.4.3", - "dev": true, - "license": "MIT" - }, - "lambdas/api-handler/node_modules/cliui": { - "version": "8.0.1", - "dev": true, - "license": "ISC", + "node_modules/@aws-sdk/util-arn-parser": { + "version": "3.953.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-arn-parser/-/util-arn-parser-3.953.0.tgz", + "integrity": "sha512-9hqdKkn4OvYzzaLryq2xnwcrPc8ziY34i9szUdgBfSqEC6pBxbY9/lLXmrgzfwMSL2Z7/v2go4Od0p5eukKLMQ==", + "license": "Apache-2.0", "dependencies": { - "string-width": "^4.2.0", - "strip-ansi": "^6.0.1", - "wrap-ansi": "^7.0.0" + "tslib": "^2.6.2" }, "engines": { - "node": ">=12" + "node": ">=18.0.0" } }, - "lambdas/api-handler/node_modules/glob": { - "version": "7.2.3", - "dev": true, - "license": "ISC", + "node_modules/@aws-sdk/util-dynamodb": { + "version": "3.955.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-dynamodb/-/util-dynamodb-3.955.0.tgz", + "integrity": "sha512-X/gSn2fIv9G5JshUzuZmAp30C5IhjdZpfsF/5476wDDse7GnqIe8SfH7Q4MVFcdd08fTT9nPFETEnMc8ga5WHg==", + "license": "Apache-2.0", "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.1.1", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" + "tslib": "^2.6.2" }, "engines": { - "node": "*" + "node": ">=18.0.0" }, - "funding": { - "url": "https://github.com/sponsors/isaacs" + "peerDependencies": { + "@aws-sdk/client-dynamodb": "^3.955.0" } }, - "lambdas/api-handler/node_modules/istanbul-lib-source-maps": { - "version": "4.0.1", - "dev": true, - "license": "BSD-3-Clause", + "node_modules/@aws-sdk/util-endpoints": { + "version": "3.953.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-endpoints/-/util-endpoints-3.953.0.tgz", + "integrity": "sha512-rjaS6jrFksopXvNg6YeN+D1lYwhcByORNlFuYesFvaQNtPOufbE5tJL4GJ3TMXyaY0uFR28N5BHHITPyWWfH/g==", + "license": "Apache-2.0", "dependencies": { - "debug": "^4.1.1", - "istanbul-lib-coverage": "^3.0.0", - "source-map": "^0.6.1" + "@aws-sdk/types": "3.953.0", + "@smithy/types": "^4.10.0", + "@smithy/url-parser": "^4.2.6", + "@smithy/util-endpoints": "^3.2.6", + "tslib": "^2.6.2" }, "engines": { - "node": ">=10" + "node": ">=18.0.0" } }, - "lambdas/api-handler/node_modules/jest": { - "version": "29.7.0", - "dev": true, - "license": "MIT", + "node_modules/@aws-sdk/util-format-url": { + "version": "3.953.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-format-url/-/util-format-url-3.953.0.tgz", + "integrity": "sha512-fs70vtTiBhp/T9ss52OuW2LGJqPoNBbd1+wxqh82CMdzkOvCzI3qa/cK8tR0jCFeIjGeiV74lAskImRxu/V4lg==", + "license": "Apache-2.0", "dependencies": { - "@jest/core": "^29.7.0", - "@jest/types": "^29.6.3", - "import-local": "^3.0.2", - "jest-cli": "^29.7.0" - }, - "bin": { - "jest": "bin/jest.js" + "@aws-sdk/types": "3.953.0", + "@smithy/querystring-builder": "^4.2.6", + "@smithy/types": "^4.10.0", + "tslib": "^2.6.2" }, "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - }, - "peerDependencies": { - "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" - }, - "peerDependenciesMeta": { - "node-notifier": { - "optional": true - } + "node": ">=18.0.0" } }, - "lambdas/api-handler/node_modules/jest-changed-files": { - "version": "29.7.0", - "dev": true, - "license": "MIT", + "node_modules/@aws-sdk/util-locate-window": { + "version": "3.953.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-locate-window/-/util-locate-window-3.953.0.tgz", + "integrity": "sha512-mPxK+I1LcrgC/RSa3G5AMAn8eN2Ay0VOgw8lSRmV1jCtO+iYvNeCqOdxoJUjOW6I5BA4niIRWqVORuRP07776Q==", + "license": "Apache-2.0", "dependencies": { - "execa": "^5.0.0", - "jest-util": "^29.7.0", - "p-limit": "^3.1.0" + "tslib": "^2.6.2" }, "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + "node": ">=18.0.0" } }, - "lambdas/api-handler/node_modules/jest-circus": { - "version": "29.7.0", - "dev": true, - "license": "MIT", + "node_modules/@aws-sdk/util-user-agent-browser": { + "version": "3.953.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-browser/-/util-user-agent-browser-3.953.0.tgz", + "integrity": "sha512-UF5NeqYesWuFao+u7LJvpV1SJCaLml5BtFZKUdTnNNMeN6jvV+dW/eQoFGpXF94RCqguX0XESmRuRRPQp+/rzQ==", + "license": "Apache-2.0", "dependencies": { - "@jest/environment": "^29.7.0", - "@jest/expect": "^29.7.0", - "@jest/test-result": "^29.7.0", - "@jest/types": "^29.6.3", - "@types/node": "*", - "chalk": "^4.0.0", - "co": "^4.6.0", - "dedent": "^1.0.0", - "is-generator-fn": "^2.0.0", - "jest-each": "^29.7.0", - "jest-matcher-utils": "^29.7.0", - "jest-message-util": "^29.7.0", - "jest-runtime": "^29.7.0", - "jest-snapshot": "^29.7.0", - "jest-util": "^29.7.0", - "p-limit": "^3.1.0", - "pretty-format": "^29.7.0", - "pure-rand": "^6.0.0", - "slash": "^3.0.0", - "stack-utils": "^2.0.3" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + "@aws-sdk/types": "3.953.0", + "@smithy/types": "^4.10.0", + "bowser": "^2.11.0", + "tslib": "^2.6.2" } }, - "lambdas/api-handler/node_modules/jest-cli": { - "version": "29.7.0", - "dev": true, - "license": "MIT", + "node_modules/@aws-sdk/util-user-agent-node": { + "version": "3.954.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-node/-/util-user-agent-node-3.954.0.tgz", + "integrity": "sha512-fB5S5VOu7OFkeNzcblQlez4AjO5hgDFaa7phYt7716YWisY3RjAaQPlxgv+G3GltHHDJIfzEC5aRxdf62B9zMg==", + "license": "Apache-2.0", "dependencies": { - "@jest/core": "^29.7.0", - "@jest/test-result": "^29.7.0", - "@jest/types": "^29.6.3", - "chalk": "^4.0.0", - "create-jest": "^29.7.0", - "exit": "^0.1.2", - "import-local": "^3.0.2", - "jest-config": "^29.7.0", - "jest-util": "^29.7.0", - "jest-validate": "^29.7.0", - "yargs": "^17.3.1" - }, - "bin": { - "jest": "bin/jest.js" + "@aws-sdk/middleware-user-agent": "3.954.0", + "@aws-sdk/types": "3.953.0", + "@smithy/node-config-provider": "^4.3.6", + "@smithy/types": "^4.10.0", + "tslib": "^2.6.2" }, "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + "node": ">=18.0.0" }, "peerDependencies": { - "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" + "aws-crt": ">=1.0.0" }, "peerDependenciesMeta": { - "node-notifier": { + "aws-crt": { "optional": true } } }, - "lambdas/api-handler/node_modules/jest-config": { - "version": "29.7.0", - "dev": true, - "license": "MIT", + "node_modules/@aws-sdk/xml-builder": { + "version": "3.953.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/xml-builder/-/xml-builder-3.953.0.tgz", + "integrity": "sha512-Zmrj21jQ2OeOJGr9spPiN00aQvXa/WUqRXcTVENhrMt+OFoSOfDFpYhUj9NQ09QmQ8KMWFoWuWW6iKurNqLvAA==", + "license": "Apache-2.0", "dependencies": { - "@babel/core": "^7.11.6", - "@jest/test-sequencer": "^29.7.0", - "@jest/types": "^29.6.3", - "babel-jest": "^29.7.0", - "chalk": "^4.0.0", - "ci-info": "^3.2.0", - "deepmerge": "^4.2.2", - "glob": "^7.1.3", - "graceful-fs": "^4.2.9", - "jest-circus": "^29.7.0", - "jest-environment-node": "^29.7.0", - "jest-get-type": "^29.6.3", - "jest-regex-util": "^29.6.3", - "jest-resolve": "^29.7.0", - "jest-runner": "^29.7.0", - "jest-util": "^29.7.0", - "jest-validate": "^29.7.0", - "micromatch": "^4.0.4", - "parse-json": "^5.2.0", - "pretty-format": "^29.7.0", - "slash": "^3.0.0", - "strip-json-comments": "^3.1.1" + "@smithy/types": "^4.10.0", + "fast-xml-parser": "5.2.5", + "tslib": "^2.6.2" }, "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - }, - "peerDependencies": { - "@types/node": "*", - "ts-node": ">=9.0.0" - }, - "peerDependenciesMeta": { - "@types/node": { - "optional": true - }, - "ts-node": { - "optional": true - } + "node": ">=18.0.0" } }, - "lambdas/api-handler/node_modules/jest-docblock": { - "version": "29.7.0", - "dev": true, - "license": "MIT", - "dependencies": { - "detect-newline": "^3.0.0" - }, + "node_modules/@aws/lambda-invoke-store": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/@aws/lambda-invoke-store/-/lambda-invoke-store-0.2.2.tgz", + "integrity": "sha512-C0NBLsIqzDIae8HFw9YIrIBsbc0xTiOtt7fAukGPnqQ/+zZNaq+4jhuccltK0QuWHBnNm/a6kLIRA6GFiM10eg==", + "license": "Apache-2.0", "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + "node": ">=18.0.0" } }, - "lambdas/api-handler/node_modules/jest-each": { - "version": "29.7.0", + "node_modules/@babel/code-frame": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.27.1.tgz", + "integrity": "sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg==", "dev": true, "license": "MIT", "dependencies": { - "@jest/types": "^29.6.3", - "chalk": "^4.0.0", - "jest-get-type": "^29.6.3", - "jest-util": "^29.7.0", - "pretty-format": "^29.7.0" + "@babel/helper-validator-identifier": "^7.27.1", + "js-tokens": "^4.0.0", + "picocolors": "^1.1.1" }, "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + "node": ">=6.9.0" } }, - "lambdas/api-handler/node_modules/jest-environment-node": { - "version": "29.7.0", + "node_modules/@babel/compat-data": { + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.28.5.tgz", + "integrity": "sha512-6uFXyCayocRbqhZOB+6XcuZbkMNimwfVGFji8CTZnCzOHVGvDqzvitu1re2AU5LROliz7eQPhB8CpAMvnx9EjA==", "dev": true, "license": "MIT", - "dependencies": { - "@jest/environment": "^29.7.0", - "@jest/fake-timers": "^29.7.0", - "@jest/types": "^29.6.3", - "@types/node": "*", - "jest-mock": "^29.7.0", - "jest-util": "^29.7.0" - }, "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + "node": ">=6.9.0" } }, - "lambdas/api-handler/node_modules/jest-haste-map": { - "version": "29.7.0", + "node_modules/@babel/core": { + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.28.5.tgz", + "integrity": "sha512-e7jT4DxYvIDLk1ZHmU/m/mB19rex9sv0c2ftBtjSBv+kVM/902eh0fINUzD7UwLLNR+jU585GxUJ8/EBfAM5fw==", "dev": true, "license": "MIT", "dependencies": { - "@jest/types": "^29.6.3", - "@types/graceful-fs": "^4.1.3", - "@types/node": "*", - "anymatch": "^3.0.3", - "fb-watchman": "^2.0.0", - "graceful-fs": "^4.2.9", - "jest-regex-util": "^29.6.3", - "jest-util": "^29.7.0", - "jest-worker": "^29.7.0", - "micromatch": "^4.0.4", - "walker": "^1.0.8" + "@babel/code-frame": "^7.27.1", + "@babel/generator": "^7.28.5", + "@babel/helper-compilation-targets": "^7.27.2", + "@babel/helper-module-transforms": "^7.28.3", + "@babel/helpers": "^7.28.4", + "@babel/parser": "^7.28.5", + "@babel/template": "^7.27.2", + "@babel/traverse": "^7.28.5", + "@babel/types": "^7.28.5", + "@jridgewell/remapping": "^2.3.5", + "convert-source-map": "^2.0.0", + "debug": "^4.1.0", + "gensync": "^1.0.0-beta.2", + "json5": "^2.2.3", + "semver": "^6.3.1" }, "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + "node": ">=6.9.0" }, - "optionalDependencies": { - "fsevents": "^2.3.2" + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/babel" + } + }, + "node_modules/@babel/core/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" } }, - "lambdas/api-handler/node_modules/jest-leak-detector": { - "version": "29.7.0", + "node_modules/@babel/generator": { + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.28.5.tgz", + "integrity": "sha512-3EwLFhZ38J4VyIP6WNtt2kUdW9dokXA9Cr4IVIFHuCpZ3H8/YFOl5JjZHisrn1fATPBmKKqXzDFvh9fUwHz6CQ==", "dev": true, "license": "MIT", "dependencies": { - "jest-get-type": "^29.6.3", - "pretty-format": "^29.7.0" + "@babel/parser": "^7.28.5", + "@babel/types": "^7.28.5", + "@jridgewell/gen-mapping": "^0.3.12", + "@jridgewell/trace-mapping": "^0.3.28", + "jsesc": "^3.0.2" }, "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + "node": ">=6.9.0" } }, - "lambdas/api-handler/node_modules/jest-mock": { - "version": "29.7.0", + "node_modules/@babel/helper-compilation-targets": { + "version": "7.27.2", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.27.2.tgz", + "integrity": "sha512-2+1thGUUWWjLTYTHZWK1n8Yga0ijBz1XAhUXcKy81rd5g6yh7hGqMp45v7cadSbEHc9G3OTv45SyneRN3ps4DQ==", "dev": true, "license": "MIT", "dependencies": { - "@jest/types": "^29.6.3", - "@types/node": "*", - "jest-util": "^29.7.0" + "@babel/compat-data": "^7.27.2", + "@babel/helper-validator-option": "^7.27.1", + "browserslist": "^4.24.0", + "lru-cache": "^5.1.1", + "semver": "^6.3.1" }, "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + "node": ">=6.9.0" } }, - "lambdas/api-handler/node_modules/jest-mock-extended": { - "version": "3.0.7", + "node_modules/@babel/helper-compilation-targets/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", "dev": true, - "license": "MIT", - "dependencies": { - "ts-essentials": "^10.0.0" - }, - "peerDependencies": { - "jest": "^24.0.0 || ^25.0.0 || ^26.0.0 || ^27.0.0 || ^28.0.0 || ^29.0.0", - "typescript": "^3.0.0 || ^4.0.0 || ^5.0.0" + "license": "ISC", + "bin": { + "semver": "bin/semver.js" } }, - "lambdas/api-handler/node_modules/jest-regex-util": { - "version": "29.6.3", + "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==", "dev": true, "license": "MIT", "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + "node": ">=6.9.0" } }, - "lambdas/api-handler/node_modules/jest-resolve": { - "version": "29.7.0", + "node_modules/@babel/helper-module-imports": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.27.1.tgz", + "integrity": "sha512-0gSFWUPNXNopqtIPQvlD5WgXYI5GY2kP2cCvoT8kczjbfcfuIljTbcWrulD1CIPIX2gt1wghbDy08yE1p+/r3w==", "dev": true, "license": "MIT", "dependencies": { - "chalk": "^4.0.0", - "graceful-fs": "^4.2.9", - "jest-haste-map": "^29.7.0", - "jest-pnp-resolver": "^1.2.2", - "jest-util": "^29.7.0", - "jest-validate": "^29.7.0", - "resolve": "^1.20.0", - "resolve.exports": "^2.0.0", - "slash": "^3.0.0" + "@babel/traverse": "^7.27.1", + "@babel/types": "^7.27.1" }, "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + "node": ">=6.9.0" } }, - "lambdas/api-handler/node_modules/jest-resolve-dependencies": { - "version": "29.7.0", + "node_modules/@babel/helper-module-transforms": { + "version": "7.28.3", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.28.3.tgz", + "integrity": "sha512-gytXUbs8k2sXS9PnQptz5o0QnpLL51SwASIORY6XaBKF88nsOT0Zw9szLqlSGQDP/4TljBAD5y98p2U1fqkdsw==", "dev": true, "license": "MIT", "dependencies": { - "jest-regex-util": "^29.6.3", - "jest-snapshot": "^29.7.0" + "@babel/helper-module-imports": "^7.27.1", + "@babel/helper-validator-identifier": "^7.27.1", + "@babel/traverse": "^7.28.3" }, "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" } }, - "lambdas/api-handler/node_modules/jest-runner": { - "version": "29.7.0", + "node_modules/@babel/helper-plugin-utils": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.27.1.tgz", + "integrity": "sha512-1gn1Up5YXka3YYAHGKpbideQ5Yjf1tDa9qYcgysz+cNCXukyLl6DjPXhD3VRwSb8c0J9tA4b2+rHEZtc6R0tlw==", "dev": true, "license": "MIT", - "dependencies": { - "@jest/console": "^29.7.0", - "@jest/environment": "^29.7.0", - "@jest/test-result": "^29.7.0", - "@jest/transform": "^29.7.0", - "@jest/types": "^29.6.3", - "@types/node": "*", - "chalk": "^4.0.0", - "emittery": "^0.13.1", - "graceful-fs": "^4.2.9", - "jest-docblock": "^29.7.0", - "jest-environment-node": "^29.7.0", - "jest-haste-map": "^29.7.0", - "jest-leak-detector": "^29.7.0", - "jest-message-util": "^29.7.0", - "jest-resolve": "^29.7.0", - "jest-runtime": "^29.7.0", - "jest-util": "^29.7.0", - "jest-watcher": "^29.7.0", - "jest-worker": "^29.7.0", - "p-limit": "^3.1.0", - "source-map-support": "0.5.13" - }, "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + "node": ">=6.9.0" } }, - "lambdas/api-handler/node_modules/jest-runtime": { - "version": "29.7.0", + "node_modules/@babel/helper-string-parser": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz", + "integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==", "dev": true, "license": "MIT", - "dependencies": { - "@jest/environment": "^29.7.0", - "@jest/fake-timers": "^29.7.0", - "@jest/globals": "^29.7.0", - "@jest/source-map": "^29.6.3", - "@jest/test-result": "^29.7.0", - "@jest/transform": "^29.7.0", - "@jest/types": "^29.6.3", - "@types/node": "*", - "chalk": "^4.0.0", - "cjs-module-lexer": "^1.0.0", - "collect-v8-coverage": "^1.0.0", - "glob": "^7.1.3", - "graceful-fs": "^4.2.9", - "jest-haste-map": "^29.7.0", - "jest-message-util": "^29.7.0", - "jest-mock": "^29.7.0", - "jest-regex-util": "^29.6.3", - "jest-resolve": "^29.7.0", - "jest-snapshot": "^29.7.0", - "jest-util": "^29.7.0", - "slash": "^3.0.0", - "strip-bom": "^4.0.0" - }, "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + "node": ">=6.9.0" } }, - "lambdas/api-handler/node_modules/jest-snapshot": { - "version": "29.7.0", + "node_modules/@babel/helper-validator-identifier": { + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.28.5.tgz", + "integrity": "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==", "dev": true, "license": "MIT", - "dependencies": { - "@babel/core": "^7.11.6", - "@babel/generator": "^7.7.2", - "@babel/plugin-syntax-jsx": "^7.7.2", - "@babel/plugin-syntax-typescript": "^7.7.2", - "@babel/types": "^7.3.3", - "@jest/expect-utils": "^29.7.0", - "@jest/transform": "^29.7.0", - "@jest/types": "^29.6.3", - "babel-preset-current-node-syntax": "^1.0.0", - "chalk": "^4.0.0", - "expect": "^29.7.0", - "graceful-fs": "^4.2.9", - "jest-diff": "^29.7.0", - "jest-get-type": "^29.6.3", - "jest-matcher-utils": "^29.7.0", - "jest-message-util": "^29.7.0", - "jest-util": "^29.7.0", - "natural-compare": "^1.4.0", - "pretty-format": "^29.7.0", - "semver": "^7.5.3" - }, "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + "node": ">=6.9.0" } }, - "lambdas/api-handler/node_modules/jest-validate": { - "version": "29.7.0", + "node_modules/@babel/helper-validator-option": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.27.1.tgz", + "integrity": "sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==", "dev": true, "license": "MIT", - "dependencies": { - "@jest/types": "^29.6.3", - "camelcase": "^6.2.0", - "chalk": "^4.0.0", - "jest-get-type": "^29.6.3", - "leven": "^3.1.0", - "pretty-format": "^29.7.0" - }, "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + "node": ">=6.9.0" } }, - "lambdas/api-handler/node_modules/jest-watcher": { - "version": "29.7.0", + "node_modules/@babel/helpers": { + "version": "7.28.4", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.28.4.tgz", + "integrity": "sha512-HFN59MmQXGHVyYadKLVumYsA9dBFun/ldYxipEjzA4196jpLZd8UjEEBLkbEkvfYreDqJhZxYAWFPtrfhNpj4w==", "dev": true, "license": "MIT", "dependencies": { - "@jest/test-result": "^29.7.0", - "@jest/types": "^29.6.3", - "@types/node": "*", - "ansi-escapes": "^4.2.1", - "chalk": "^4.0.0", - "emittery": "^0.13.1", - "jest-util": "^29.7.0", - "string-length": "^4.0.1" + "@babel/template": "^7.27.2", + "@babel/types": "^7.28.4" }, "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + "node": ">=6.9.0" } }, - "lambdas/api-handler/node_modules/jest-worker": { - "version": "29.7.0", + "node_modules/@babel/parser": { + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.28.5.tgz", + "integrity": "sha512-KKBU1VGYR7ORr3At5HAtUQ+TV3SzRCXmA/8OdDZiLDBIZxVyzXuztPjfLd3BV1PRAQGCMWWSHYhL0F8d5uHBDQ==", "dev": true, "license": "MIT", "dependencies": { - "@types/node": "*", - "jest-util": "^29.7.0", - "merge-stream": "^2.0.0", - "supports-color": "^8.0.0" + "@babel/types": "^7.28.5" + }, + "bin": { + "parser": "bin/babel-parser.js" }, "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + "node": ">=6.0.0" } }, - "lambdas/api-handler/node_modules/minimatch": { - "version": "3.1.2", + "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==", "dev": true, - "license": "ISC", + "license": "MIT", "dependencies": { - "brace-expansion": "^1.1.7" + "@babel/helper-plugin-utils": "^7.8.0" }, - "engines": { - "node": "*" + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "lambdas/api-handler/node_modules/pure-rand": { - "version": "6.1.0", + "node_modules/@babel/plugin-syntax-bigint": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-bigint/-/plugin-syntax-bigint-7.8.3.tgz", + "integrity": "sha512-wnTnFlG+YxQm3vDxpGE57Pj0srRU4sHE/mDkt1qv2YJJSeUAec2ma4WLUnUPeKjyrfntVwe/N6dCXpU+zL3Npg==", "dev": true, - "funding": [ - { - "type": "individual", - "url": "https://github.com/sponsors/dubzzz" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/fast-check" - } - ], - "license": "MIT" + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } }, - "lambdas/api-handler/node_modules/signal-exit": { - "version": "3.0.7", + "node_modules/@babel/plugin-syntax-class-properties": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.12.13.tgz", + "integrity": "sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA==", "dev": true, - "license": "ISC" + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.12.13" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } }, - "lambdas/api-handler/node_modules/supports-color": { - "version": "8.1.1", + "node_modules/@babel/plugin-syntax-class-static-block": { + "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==", "dev": true, "license": "MIT", "dependencies": { - "has-flag": "^4.0.0" + "@babel/helper-plugin-utils": "^7.14.5" }, "engines": { - "node": ">=10" + "node": ">=6.9.0" }, - "funding": { - "url": "https://github.com/chalk/supports-color?sponsor=1" + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "lambdas/api-handler/node_modules/wrap-ansi": { - "version": "7.0.0", + "node_modules/@babel/plugin-syntax-import-attributes": { + "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==", "dev": true, "license": "MIT", "dependencies": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" + "@babel/helper-plugin-utils": "^7.27.1" }, "engines": { - "node": ">=10" + "node": ">=6.9.0" }, - "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "lambdas/api-handler/node_modules/write-file-atomic": { - "version": "4.0.2", + "node_modules/@babel/plugin-syntax-import-meta": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-meta/-/plugin-syntax-import-meta-7.10.4.tgz", + "integrity": "sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g==", "dev": true, - "license": "ISC", + "license": "MIT", "dependencies": { - "imurmurhash": "^0.1.4", - "signal-exit": "^3.0.7" + "@babel/helper-plugin-utils": "^7.10.4" }, - "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "lambdas/api-handler/node_modules/yargs": { - "version": "17.7.2", + "node_modules/@babel/plugin-syntax-json-strings": { + "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==", "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" + "@babel/helper-plugin-utils": "^7.8.0" }, - "engines": { - "node": ">=12" + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "lambdas/api-handler/node_modules/zod": { - "version": "4.1.12", + "node_modules/@babel/plugin-syntax-jsx": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.27.1.tgz", + "integrity": "sha512-y8YTNIeKoyhGd9O0Jiyzyyqk8gdjnumGTQPsz0xOZOQ2RmkVJeZ1vmmfIvFEKqucBG6axJGBZDE/7iI5suUI/w==", "dev": true, "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/colinhacks" + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "lambdas/authorizer": { - "name": "nhs-notify-supplier-authorizer", - "version": "0.0.1", + "node_modules/@babel/plugin-syntax-logical-assignment-operators": { + "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==", + "dev": true, + "license": "MIT", "dependencies": { - "esbuild": "^0.25.11" + "@babel/helper-plugin-utils": "^7.10.4" }, - "devDependencies": { - "@tsconfig/node22": "^22.0.2", - "@types/aws-lambda": "^8.10.148", - "@types/jest": "^30.0.0", - "jest": "^30.2.0", - "jest-mock-extended": "^4.0.0", - "typescript": "^5.8.3" + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "node_modules/@ampproject/remapping": { - "version": "2.3.0", + "node_modules/@babel/plugin-syntax-nullish-coalescing-operator": { + "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==", "dev": true, - "license": "Apache-2.0", + "license": "MIT", "dependencies": { - "@jridgewell/gen-mapping": "^0.3.5", - "@jridgewell/trace-mapping": "^0.3.24" + "@babel/helper-plugin-utils": "^7.8.0" }, - "engines": { - "node": ">=6.0.0" + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "node_modules/@asamuzakjp/css-color": { - "version": "3.2.0", + "node_modules/@babel/plugin-syntax-numeric-separator": { + "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==", "dev": true, "license": "MIT", "dependencies": { - "@csstools/css-calc": "^2.1.3", - "@csstools/css-color-parser": "^3.0.9", - "@csstools/css-parser-algorithms": "^3.0.4", - "@csstools/css-tokenizer": "^3.0.3", - "lru-cache": "^10.4.3" + "@babel/helper-plugin-utils": "^7.10.4" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "node_modules/@asamuzakjp/css-color/node_modules/lru-cache": { - "version": "10.4.3", + "node_modules/@babel/plugin-syntax-object-rest-spread": { + "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==", "dev": true, - "license": "ISC" - }, - "node_modules/@aws-crypto/crc32": { - "version": "5.2.0", - "license": "Apache-2.0", + "license": "MIT", "dependencies": { - "@aws-crypto/util": "^5.2.0", - "@aws-sdk/types": "^3.222.0", - "tslib": "^2.6.2" + "@babel/helper-plugin-utils": "^7.8.0" }, - "engines": { - "node": ">=16.0.0" + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "node_modules/@aws-crypto/crc32c": { - "version": "5.2.0", - "license": "Apache-2.0", + "node_modules/@babel/plugin-syntax-optional-catch-binding": { + "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==", + "dev": true, + "license": "MIT", "dependencies": { - "@aws-crypto/util": "^5.2.0", - "@aws-sdk/types": "^3.222.0", - "tslib": "^2.6.2" + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "node_modules/@aws-crypto/sha1-browser": { - "version": "5.2.0", - "license": "Apache-2.0", + "node_modules/@babel/plugin-syntax-optional-chaining": { + "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==", + "dev": true, + "license": "MIT", "dependencies": { - "@aws-crypto/supports-web-crypto": "^5.2.0", - "@aws-crypto/util": "^5.2.0", - "@aws-sdk/types": "^3.222.0", - "@aws-sdk/util-locate-window": "^3.0.0", - "@smithy/util-utf8": "^2.0.0", - "tslib": "^2.6.2" + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "node_modules/@aws-crypto/sha1-browser/node_modules/@smithy/is-array-buffer": { - "version": "2.2.0", - "license": "Apache-2.0", + "node_modules/@babel/plugin-syntax-private-property-in-object": { + "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==", + "dev": true, + "license": "MIT", "dependencies": { - "tslib": "^2.6.2" + "@babel/helper-plugin-utils": "^7.14.5" }, "engines": { - "node": ">=14.0.0" + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "node_modules/@aws-crypto/sha1-browser/node_modules/@smithy/util-buffer-from": { - "version": "2.2.0", - "license": "Apache-2.0", + "node_modules/@babel/plugin-syntax-top-level-await": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.14.5.tgz", + "integrity": "sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw==", + "dev": true, + "license": "MIT", "dependencies": { - "@smithy/is-array-buffer": "^2.2.0", - "tslib": "^2.6.2" + "@babel/helper-plugin-utils": "^7.14.5" }, "engines": { - "node": ">=14.0.0" + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "node_modules/@aws-crypto/sha1-browser/node_modules/@smithy/util-utf8": { - "version": "2.3.0", - "license": "Apache-2.0", + "node_modules/@babel/plugin-syntax-typescript": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.27.1.tgz", + "integrity": "sha512-xfYCBMxveHrRMnAWl1ZlPXOZjzkN82THFvLhQhFXFt81Z5HnN+EtUkZhv/zcKpmT3fzmWZB0ywiBrbC3vogbwQ==", + "dev": true, + "license": "MIT", "dependencies": { - "@smithy/util-buffer-from": "^2.2.0", - "tslib": "^2.6.2" + "@babel/helper-plugin-utils": "^7.27.1" }, "engines": { - "node": ">=14.0.0" + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "node_modules/@aws-crypto/sha256-browser": { - "version": "5.2.0", - "license": "Apache-2.0", - "dependencies": { - "@aws-crypto/sha256-js": "^5.2.0", - "@aws-crypto/supports-web-crypto": "^5.2.0", - "@aws-crypto/util": "^5.2.0", - "@aws-sdk/types": "^3.222.0", - "@aws-sdk/util-locate-window": "^3.0.0", - "@smithy/util-utf8": "^2.0.0", - "tslib": "^2.6.2" + "node_modules/@babel/runtime": { + "version": "7.28.4", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.28.4.tgz", + "integrity": "sha512-Q/N6JNWvIvPnLDvjlE1OUBLPQHH6l3CltCEsHIujp45zQUSSh8K+gHnaEX45yAT1nyngnINhvWtzN+Nb9D8RAQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" } }, - "node_modules/@aws-crypto/sha256-browser/node_modules/@smithy/is-array-buffer": { - "version": "2.2.0", - "license": "Apache-2.0", + "node_modules/@babel/template": { + "version": "7.27.2", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.27.2.tgz", + "integrity": "sha512-LPDZ85aEJyYSd18/DkjNh4/y1ntkE5KwUHWTiqgRxruuZL2F1yuHligVHLvcHY2vMHXttKFpJn6LwfI7cw7ODw==", + "dev": true, + "license": "MIT", "dependencies": { - "tslib": "^2.6.2" + "@babel/code-frame": "^7.27.1", + "@babel/parser": "^7.27.2", + "@babel/types": "^7.27.1" }, "engines": { - "node": ">=14.0.0" + "node": ">=6.9.0" } }, - "node_modules/@aws-crypto/sha256-browser/node_modules/@smithy/util-buffer-from": { - "version": "2.2.0", - "license": "Apache-2.0", + "node_modules/@babel/traverse": { + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.28.5.tgz", + "integrity": "sha512-TCCj4t55U90khlYkVV/0TfkJkAkUg3jZFA3Neb7unZT8CPok7iiRfaX0F+WnqWqt7OxhOn0uBKXCw4lbL8W0aQ==", + "dev": true, + "license": "MIT", "dependencies": { - "@smithy/is-array-buffer": "^2.2.0", - "tslib": "^2.6.2" + "@babel/code-frame": "^7.27.1", + "@babel/generator": "^7.28.5", + "@babel/helper-globals": "^7.28.0", + "@babel/parser": "^7.28.5", + "@babel/template": "^7.27.2", + "@babel/types": "^7.28.5", + "debug": "^4.3.1" }, "engines": { - "node": ">=14.0.0" + "node": ">=6.9.0" } }, - "node_modules/@aws-crypto/sha256-browser/node_modules/@smithy/util-utf8": { - "version": "2.3.0", - "license": "Apache-2.0", + "node_modules/@babel/types": { + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.28.5.tgz", + "integrity": "sha512-qQ5m48eI/MFLQ5PxQj4PFaprjyCTLI37ElWMmNs0K8Lk3dVeOdNpB3ks8jc7yM5CDmVC73eMVk/trk3fgmrUpA==", + "dev": true, + "license": "MIT", "dependencies": { - "@smithy/util-buffer-from": "^2.2.0", - "tslib": "^2.6.2" + "@babel/helper-string-parser": "^7.27.1", + "@babel/helper-validator-identifier": "^7.28.5" }, "engines": { - "node": ">=14.0.0" + "node": ">=6.9.0" } }, - "node_modules/@aws-crypto/sha256-js": { - "version": "5.2.0", - "license": "Apache-2.0", - "dependencies": { - "@aws-crypto/util": "^5.2.0", - "@aws-sdk/types": "^3.222.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } + "node_modules/@balena/dockerignore": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@balena/dockerignore/-/dockerignore-1.0.2.tgz", + "integrity": "sha512-wMue2Sy4GAVTk6Ic4tJVcnfdau+gx2EnG7S+uAEe+TWJFqE4YoWN4/H8MSLj4eYJKxGg26lZwboEniNiNwZQ6Q==", + "dev": true, + "license": "Apache-2.0" }, - "node_modules/@aws-crypto/supports-web-crypto": { - "version": "5.2.0", - "license": "Apache-2.0", - "dependencies": { - "tslib": "^2.6.2" - } + "node_modules/@bcoe/v8-coverage": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz", + "integrity": "sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==", + "dev": true, + "license": "MIT" }, - "node_modules/@aws-crypto/util": { - "version": "5.2.0", - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/types": "^3.222.0", - "@smithy/util-utf8": "^2.0.0", - "tslib": "^2.6.2" + "node_modules/@borewit/text-codec": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/@borewit/text-codec/-/text-codec-0.1.1.tgz", + "integrity": "sha512-5L/uBxmjaCIX5h8Z+uu+kA9BQLkc/Wl06UGR5ajNRxu+/XjonB5i8JpgFMrPj3LXTCPA0pv8yxUvbUi+QthGGA==", + "dev": true, + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Borewit" } }, - "node_modules/@aws-crypto/util/node_modules/@smithy/is-array-buffer": { - "version": "2.2.0", - "license": "Apache-2.0", + "node_modules/@cspotcode/source-map-support": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz", + "integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==", + "dev": true, + "license": "MIT", "dependencies": { - "tslib": "^2.6.2" + "@jridgewell/trace-mapping": "0.3.9" }, "engines": { - "node": ">=14.0.0" + "node": ">=12" } }, - "node_modules/@aws-crypto/util/node_modules/@smithy/util-buffer-from": { - "version": "2.2.0", - "license": "Apache-2.0", + "node_modules/@cspotcode/source-map-support/node_modules/@jridgewell/trace-mapping": { + "version": "0.3.9", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz", + "integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==", + "dev": true, + "license": "MIT", "dependencies": { - "@smithy/is-array-buffer": "^2.2.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=14.0.0" + "@jridgewell/resolve-uri": "^3.0.3", + "@jridgewell/sourcemap-codec": "^1.4.10" } }, - "node_modules/@aws-crypto/util/node_modules/@smithy/util-utf8": { - "version": "2.3.0", - "license": "Apache-2.0", - "dependencies": { - "@smithy/util-buffer-from": "^2.2.0", - "tslib": "^2.6.2" - }, + "node_modules/@csstools/color-helpers": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/@csstools/color-helpers/-/color-helpers-5.1.0.tgz", + "integrity": "sha512-S11EXWJyy0Mz5SYvRmY8nJYTFFd1LCNV+7cXyAgQtOOuzb4EsgfqDufL+9esx72/eLhsRdGZwaldu/h+E4t4BA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "license": "MIT-0", "engines": { - "node": ">=14.0.0" + "node": ">=18" } }, - "node_modules/@aws-sdk/client-api-gateway": { - "version": "3.906.0", - "license": "Apache-2.0", - "dependencies": { - "@aws-crypto/sha256-browser": "5.2.0", - "@aws-crypto/sha256-js": "5.2.0", - "@aws-sdk/core": "3.906.0", - "@aws-sdk/credential-provider-node": "3.906.0", - "@aws-sdk/middleware-host-header": "3.901.0", - "@aws-sdk/middleware-logger": "3.901.0", - "@aws-sdk/middleware-recursion-detection": "3.901.0", - "@aws-sdk/middleware-sdk-api-gateway": "3.901.0", - "@aws-sdk/middleware-user-agent": "3.906.0", - "@aws-sdk/region-config-resolver": "3.901.0", - "@aws-sdk/types": "3.901.0", - "@aws-sdk/util-endpoints": "3.901.0", - "@aws-sdk/util-user-agent-browser": "3.901.0", - "@aws-sdk/util-user-agent-node": "3.906.0", - "@smithy/config-resolver": "^4.3.0", - "@smithy/core": "^3.14.0", - "@smithy/fetch-http-handler": "^5.3.0", - "@smithy/hash-node": "^4.2.0", - "@smithy/invalid-dependency": "^4.2.0", - "@smithy/middleware-content-length": "^4.2.0", - "@smithy/middleware-endpoint": "^4.3.0", - "@smithy/middleware-retry": "^4.4.0", - "@smithy/middleware-serde": "^4.2.0", - "@smithy/middleware-stack": "^4.2.0", - "@smithy/node-config-provider": "^4.3.0", - "@smithy/node-http-handler": "^4.3.0", - "@smithy/protocol-http": "^5.3.0", - "@smithy/smithy-client": "^4.7.0", - "@smithy/types": "^4.6.0", - "@smithy/url-parser": "^4.2.0", - "@smithy/util-base64": "^4.2.0", - "@smithy/util-body-length-browser": "^4.2.0", - "@smithy/util-body-length-node": "^4.2.0", - "@smithy/util-defaults-mode-browser": "^4.2.0", - "@smithy/util-defaults-mode-node": "^4.2.0", - "@smithy/util-endpoints": "^3.2.0", - "@smithy/util-middleware": "^4.2.0", - "@smithy/util-retry": "^4.2.0", - "@smithy/util-stream": "^4.4.0", - "@smithy/util-utf8": "^4.2.0", - "tslib": "^2.6.2" - }, + "node_modules/@csstools/css-calc": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/@csstools/css-calc/-/css-calc-2.1.4.tgz", + "integrity": "sha512-3N8oaj+0juUw/1H3YwmDDJXCgTB1gKU6Hc/bB502u9zR0q2vd786XJH9QfrKIEgFlZmhZiq6epXl4rHqhzsIgQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "license": "MIT", "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@aws-sdk/client-api-gateway/node_modules/@aws-sdk/client-sso": { - "version": "3.906.0", - "license": "Apache-2.0", - "dependencies": { - "@aws-crypto/sha256-browser": "5.2.0", - "@aws-crypto/sha256-js": "5.2.0", - "@aws-sdk/core": "3.906.0", - "@aws-sdk/middleware-host-header": "3.901.0", - "@aws-sdk/middleware-logger": "3.901.0", - "@aws-sdk/middleware-recursion-detection": "3.901.0", - "@aws-sdk/middleware-user-agent": "3.906.0", - "@aws-sdk/region-config-resolver": "3.901.0", - "@aws-sdk/types": "3.901.0", - "@aws-sdk/util-endpoints": "3.901.0", - "@aws-sdk/util-user-agent-browser": "3.901.0", - "@aws-sdk/util-user-agent-node": "3.906.0", - "@smithy/config-resolver": "^4.3.0", - "@smithy/core": "^3.14.0", - "@smithy/fetch-http-handler": "^5.3.0", - "@smithy/hash-node": "^4.2.0", - "@smithy/invalid-dependency": "^4.2.0", - "@smithy/middleware-content-length": "^4.2.0", - "@smithy/middleware-endpoint": "^4.3.0", - "@smithy/middleware-retry": "^4.4.0", - "@smithy/middleware-serde": "^4.2.0", - "@smithy/middleware-stack": "^4.2.0", - "@smithy/node-config-provider": "^4.3.0", - "@smithy/node-http-handler": "^4.3.0", - "@smithy/protocol-http": "^5.3.0", - "@smithy/smithy-client": "^4.7.0", - "@smithy/types": "^4.6.0", - "@smithy/url-parser": "^4.2.0", - "@smithy/util-base64": "^4.2.0", - "@smithy/util-body-length-browser": "^4.2.0", - "@smithy/util-body-length-node": "^4.2.0", - "@smithy/util-defaults-mode-browser": "^4.2.0", - "@smithy/util-defaults-mode-node": "^4.2.0", - "@smithy/util-endpoints": "^3.2.0", - "@smithy/util-middleware": "^4.2.0", - "@smithy/util-retry": "^4.2.0", - "@smithy/util-utf8": "^4.2.0", - "tslib": "^2.6.2" + "node": ">=18" }, - "engines": { - "node": ">=18.0.0" + "peerDependencies": { + "@csstools/css-parser-algorithms": "^3.0.5", + "@csstools/css-tokenizer": "^3.0.4" } }, - "node_modules/@aws-sdk/client-api-gateway/node_modules/@aws-sdk/core": { - "version": "3.906.0", - "license": "Apache-2.0", + "node_modules/@csstools/css-color-parser": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@csstools/css-color-parser/-/css-color-parser-3.1.0.tgz", + "integrity": "sha512-nbtKwh3a6xNVIp/VRuXV64yTKnb1IjTAEEh3irzS+HkKjAOYLTGNb9pmVNntZ8iVBHcWDA2Dof0QtPgFI1BaTA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "license": "MIT", "dependencies": { - "@aws-sdk/types": "3.901.0", - "@aws-sdk/xml-builder": "3.901.0", - "@smithy/core": "^3.14.0", - "@smithy/node-config-provider": "^4.3.0", - "@smithy/property-provider": "^4.2.0", - "@smithy/protocol-http": "^5.3.0", - "@smithy/signature-v4": "^5.3.0", - "@smithy/smithy-client": "^4.7.0", - "@smithy/types": "^4.6.0", - "@smithy/util-base64": "^4.2.0", - "@smithy/util-middleware": "^4.2.0", - "@smithy/util-utf8": "^4.2.0", - "tslib": "^2.6.2" + "@csstools/color-helpers": "^5.1.0", + "@csstools/css-calc": "^2.1.4" }, "engines": { - "node": ">=18.0.0" + "node": ">=18" + }, + "peerDependencies": { + "@csstools/css-parser-algorithms": "^3.0.5", + "@csstools/css-tokenizer": "^3.0.4" } }, - "node_modules/@aws-sdk/client-api-gateway/node_modules/@aws-sdk/credential-provider-env": { - "version": "3.906.0", - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/core": "3.906.0", - "@aws-sdk/types": "3.901.0", - "@smithy/property-provider": "^4.2.0", - "@smithy/types": "^4.6.0", - "tslib": "^2.6.2" - }, + "node_modules/@csstools/css-parser-algorithms": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/@csstools/css-parser-algorithms/-/css-parser-algorithms-3.0.5.tgz", + "integrity": "sha512-DaDeUkXZKjdGhgYaHNJTV9pV7Y9B3b644jCLs9Upc3VeNGg6LWARAT6O+Q+/COo+2gg/bM5rhpMAtf70WqfBdQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "license": "MIT", "engines": { - "node": ">=18.0.0" + "node": ">=18" + }, + "peerDependencies": { + "@csstools/css-tokenizer": "^3.0.4" } }, - "node_modules/@aws-sdk/client-api-gateway/node_modules/@aws-sdk/credential-provider-http": { - "version": "3.906.0", - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/core": "3.906.0", - "@aws-sdk/types": "3.901.0", - "@smithy/fetch-http-handler": "^5.3.0", - "@smithy/node-http-handler": "^4.3.0", - "@smithy/property-provider": "^4.2.0", - "@smithy/protocol-http": "^5.3.0", - "@smithy/smithy-client": "^4.7.0", - "@smithy/types": "^4.6.0", - "@smithy/util-stream": "^4.4.0", - "tslib": "^2.6.2" - }, + "node_modules/@csstools/css-tokenizer": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@csstools/css-tokenizer/-/css-tokenizer-3.0.4.tgz", + "integrity": "sha512-Vd/9EVDiu6PPJt9yAh6roZP6El1xHrdvIVGjyBsHR0RYwNHgL7FJPyIIW4fANJNG6FtyZfvlRPpFI4ZM/lubvw==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "license": "MIT", "engines": { - "node": ">=18.0.0" + "node": ">=18" } }, - "node_modules/@aws-sdk/client-api-gateway/node_modules/@aws-sdk/credential-provider-ini": { - "version": "3.906.0", - "license": "Apache-2.0", + "node_modules/@emnapi/core": { + "version": "1.7.1", + "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.7.1.tgz", + "integrity": "sha512-o1uhUASyo921r2XtHYOHy7gdkGLge8ghBEQHMWmyJFoXlpU58kIrhhN3w26lpQb6dspetweapMn2CSNwQ8I4wg==", + "dev": true, + "license": "MIT", + "optional": true, "dependencies": { - "@aws-sdk/core": "3.906.0", - "@aws-sdk/credential-provider-env": "3.906.0", - "@aws-sdk/credential-provider-http": "3.906.0", - "@aws-sdk/credential-provider-process": "3.906.0", - "@aws-sdk/credential-provider-sso": "3.906.0", - "@aws-sdk/credential-provider-web-identity": "3.906.0", - "@aws-sdk/nested-clients": "3.906.0", - "@aws-sdk/types": "3.901.0", - "@smithy/credential-provider-imds": "^4.2.0", - "@smithy/property-provider": "^4.2.0", - "@smithy/shared-ini-file-loader": "^4.3.0", - "@smithy/types": "^4.6.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" + "@emnapi/wasi-threads": "1.1.0", + "tslib": "^2.4.0" } }, - "node_modules/@aws-sdk/client-api-gateway/node_modules/@aws-sdk/credential-provider-node": { - "version": "3.906.0", - "license": "Apache-2.0", + "node_modules/@emnapi/runtime": { + "version": "1.7.1", + "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.7.1.tgz", + "integrity": "sha512-PVtJr5CmLwYAU9PZDMITZoR5iAOShYREoR45EyyLrbntV50mdePTgUn4AmOw90Ifcj+x2kRjdzr1HP3RrNiHGA==", + "dev": true, + "license": "MIT", + "optional": true, "dependencies": { - "@aws-sdk/credential-provider-env": "3.906.0", - "@aws-sdk/credential-provider-http": "3.906.0", - "@aws-sdk/credential-provider-ini": "3.906.0", - "@aws-sdk/credential-provider-process": "3.906.0", - "@aws-sdk/credential-provider-sso": "3.906.0", - "@aws-sdk/credential-provider-web-identity": "3.906.0", - "@aws-sdk/types": "3.901.0", - "@smithy/credential-provider-imds": "^4.2.0", - "@smithy/property-provider": "^4.2.0", - "@smithy/shared-ini-file-loader": "^4.3.0", - "@smithy/types": "^4.6.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" + "tslib": "^2.4.0" } }, - "node_modules/@aws-sdk/client-api-gateway/node_modules/@aws-sdk/credential-provider-process": { - "version": "3.906.0", - "license": "Apache-2.0", + "node_modules/@emnapi/wasi-threads": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.1.0.tgz", + "integrity": "sha512-WI0DdZ8xFSbgMjR1sFsKABJ/C5OnRrjT06JXbZKexJGrDuPTzZdDYfFlsgcCXCyf+suG5QU2e/y1Wo2V/OapLQ==", + "dev": true, + "license": "MIT", + "optional": true, "dependencies": { - "@aws-sdk/core": "3.906.0", - "@aws-sdk/types": "3.901.0", - "@smithy/property-provider": "^4.2.0", - "@smithy/shared-ini-file-loader": "^4.3.0", - "@smithy/types": "^4.6.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" + "tslib": "^2.4.0" } }, - "node_modules/@aws-sdk/client-api-gateway/node_modules/@aws-sdk/credential-provider-sso": { - "version": "3.906.0", - "license": "Apache-2.0", + "node_modules/@emotion/is-prop-valid": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/@emotion/is-prop-valid/-/is-prop-valid-1.2.2.tgz", + "integrity": "sha512-uNsoYd37AFmaCdXlg6EYD1KaPOaRWRByMCYzbKUX4+hhMfrxdVSelShywL4JVaAeM/eHUOSprYBQls+/neX3pw==", + "dev": true, + "license": "MIT", "dependencies": { - "@aws-sdk/client-sso": "3.906.0", - "@aws-sdk/core": "3.906.0", - "@aws-sdk/token-providers": "3.906.0", - "@aws-sdk/types": "3.901.0", - "@smithy/property-provider": "^4.2.0", - "@smithy/shared-ini-file-loader": "^4.3.0", - "@smithy/types": "^4.6.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" + "@emotion/memoize": "^0.8.1" } }, - "node_modules/@aws-sdk/client-api-gateway/node_modules/@aws-sdk/credential-provider-web-identity": { - "version": "3.906.0", - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/core": "3.906.0", - "@aws-sdk/nested-clients": "3.906.0", - "@aws-sdk/types": "3.901.0", - "@smithy/property-provider": "^4.2.0", - "@smithy/shared-ini-file-loader": "^4.3.0", - "@smithy/types": "^4.6.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } + "node_modules/@emotion/memoize": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/@emotion/memoize/-/memoize-0.8.1.tgz", + "integrity": "sha512-W2P2c/VRW1/1tLox0mVUalvnWXxavmv/Oum2aPsRcoDJuob75FC3Y8FbpfLwUegRcxINtGUMPq0tFCvYNTBXNA==", + "dev": true, + "license": "MIT" }, - "node_modules/@aws-sdk/client-api-gateway/node_modules/@aws-sdk/middleware-host-header": { - "version": "3.901.0", - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/types": "3.901.0", - "@smithy/protocol-http": "^5.3.0", - "@smithy/types": "^4.6.0", - "tslib": "^2.6.2" - }, + "node_modules/@emotion/unitless": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/@emotion/unitless/-/unitless-0.8.1.tgz", + "integrity": "sha512-KOEGMu6dmJZtpadb476IsZBclKvILjopjUii3V+7MnXIQCYh8W3NgNcgwo21n9LXZX6EDIKvqfjYxXebDwxKmQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@esbuild/aix-ppc64": { + "version": "0.24.2", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.24.2.tgz", + "integrity": "sha512-thpVCb/rhxE/BnMLQ7GReQLLN8q9qbHmI55F4489/ByVg2aQaQ6kbcLb6FHkocZzQhxc4gx0sCk0tJkKBFzDhA==", + "cpu": [ + "ppc64" + ], + "license": "MIT", + "optional": true, + "os": [ + "aix" + ], "engines": { - "node": ">=18.0.0" + "node": ">=18" } }, - "node_modules/@aws-sdk/client-api-gateway/node_modules/@aws-sdk/middleware-logger": { - "version": "3.901.0", - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/types": "3.901.0", - "@smithy/types": "^4.6.0", - "tslib": "^2.6.2" - }, + "node_modules/@esbuild/android-arm": { + "version": "0.24.2", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.24.2.tgz", + "integrity": "sha512-tmwl4hJkCfNHwFB3nBa8z1Uy3ypZpxqxfTQOcHX+xRByyYgunVbZ9MzUUfb0RxaHIMnbHagwAxuTL+tnNM+1/Q==", + "cpu": [ + "arm" + ], + "license": "MIT", + "optional": true, + "os": [ + "android" + ], "engines": { - "node": ">=18.0.0" + "node": ">=18" } }, - "node_modules/@aws-sdk/client-api-gateway/node_modules/@aws-sdk/middleware-recursion-detection": { - "version": "3.901.0", - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/types": "3.901.0", - "@aws/lambda-invoke-store": "^0.0.1", - "@smithy/protocol-http": "^5.3.0", - "@smithy/types": "^4.6.0", - "tslib": "^2.6.2" - }, + "node_modules/@esbuild/android-arm64": { + "version": "0.24.2", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.24.2.tgz", + "integrity": "sha512-cNLgeqCqV8WxfcTIOeL4OAtSmL8JjcN6m09XIgro1Wi7cF4t/THaWEa7eL5CMoMBdjoHOTh/vwTO/o2TRXIyzg==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "android" + ], "engines": { - "node": ">=18.0.0" + "node": ">=18" } }, - "node_modules/@aws-sdk/client-api-gateway/node_modules/@aws-sdk/middleware-user-agent": { - "version": "3.906.0", - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/core": "3.906.0", - "@aws-sdk/types": "3.901.0", - "@aws-sdk/util-endpoints": "3.901.0", - "@smithy/core": "^3.14.0", - "@smithy/protocol-http": "^5.3.0", - "@smithy/types": "^4.6.0", - "tslib": "^2.6.2" - }, + "node_modules/@esbuild/android-x64": { + "version": "0.24.2", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.24.2.tgz", + "integrity": "sha512-B6Q0YQDqMx9D7rvIcsXfmJfvUYLoP722bgfBlO5cGvNVb5V/+Y7nhBE3mHV9OpxBf4eAS2S68KZztiPaWq4XYw==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "android" + ], "engines": { - "node": ">=18.0.0" + "node": ">=18" } }, - "node_modules/@aws-sdk/client-api-gateway/node_modules/@aws-sdk/nested-clients": { - "version": "3.906.0", - "license": "Apache-2.0", - "dependencies": { - "@aws-crypto/sha256-browser": "5.2.0", - "@aws-crypto/sha256-js": "5.2.0", - "@aws-sdk/core": "3.906.0", - "@aws-sdk/middleware-host-header": "3.901.0", - "@aws-sdk/middleware-logger": "3.901.0", - "@aws-sdk/middleware-recursion-detection": "3.901.0", - "@aws-sdk/middleware-user-agent": "3.906.0", - "@aws-sdk/region-config-resolver": "3.901.0", - "@aws-sdk/types": "3.901.0", - "@aws-sdk/util-endpoints": "3.901.0", - "@aws-sdk/util-user-agent-browser": "3.901.0", - "@aws-sdk/util-user-agent-node": "3.906.0", - "@smithy/config-resolver": "^4.3.0", - "@smithy/core": "^3.14.0", - "@smithy/fetch-http-handler": "^5.3.0", - "@smithy/hash-node": "^4.2.0", - "@smithy/invalid-dependency": "^4.2.0", - "@smithy/middleware-content-length": "^4.2.0", - "@smithy/middleware-endpoint": "^4.3.0", - "@smithy/middleware-retry": "^4.4.0", - "@smithy/middleware-serde": "^4.2.0", - "@smithy/middleware-stack": "^4.2.0", - "@smithy/node-config-provider": "^4.3.0", - "@smithy/node-http-handler": "^4.3.0", - "@smithy/protocol-http": "^5.3.0", - "@smithy/smithy-client": "^4.7.0", - "@smithy/types": "^4.6.0", - "@smithy/url-parser": "^4.2.0", - "@smithy/util-base64": "^4.2.0", - "@smithy/util-body-length-browser": "^4.2.0", - "@smithy/util-body-length-node": "^4.2.0", - "@smithy/util-defaults-mode-browser": "^4.2.0", - "@smithy/util-defaults-mode-node": "^4.2.0", - "@smithy/util-endpoints": "^3.2.0", - "@smithy/util-middleware": "^4.2.0", - "@smithy/util-retry": "^4.2.0", - "@smithy/util-utf8": "^4.2.0", - "tslib": "^2.6.2" - }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.24.2", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.24.2.tgz", + "integrity": "sha512-kj3AnYWc+CekmZnS5IPu9D+HWtUI49hbnyqk0FLEJDbzCIQt7hg7ucF1SQAilhtYpIujfaHr6O0UHlzzSPdOeA==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], "engines": { - "node": ">=18.0.0" + "node": ">=18" } }, - "node_modules/@aws-sdk/client-api-gateway/node_modules/@aws-sdk/region-config-resolver": { - "version": "3.901.0", - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/types": "3.901.0", - "@smithy/node-config-provider": "^4.3.0", - "@smithy/types": "^4.6.0", - "@smithy/util-config-provider": "^4.2.0", - "@smithy/util-middleware": "^4.2.0", - "tslib": "^2.6.2" - }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.24.2", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.24.2.tgz", + "integrity": "sha512-WeSrmwwHaPkNR5H3yYfowhZcbriGqooyu3zI/3GGpF8AyUdsrrP0X6KumITGA9WOyiJavnGZUwPGvxvwfWPHIA==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], "engines": { - "node": ">=18.0.0" + "node": ">=18" } }, - "node_modules/@aws-sdk/client-api-gateway/node_modules/@aws-sdk/token-providers": { - "version": "3.906.0", - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/core": "3.906.0", - "@aws-sdk/nested-clients": "3.906.0", - "@aws-sdk/types": "3.901.0", - "@smithy/property-provider": "^4.2.0", - "@smithy/shared-ini-file-loader": "^4.3.0", - "@smithy/types": "^4.6.0", - "tslib": "^2.6.2" - }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.24.2", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.24.2.tgz", + "integrity": "sha512-UN8HXjtJ0k/Mj6a9+5u6+2eZ2ERD7Edt1Q9IZiB5UZAIdPnVKDoG7mdTVGhHJIeEml60JteamR3qhsr1r8gXvg==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], "engines": { - "node": ">=18.0.0" + "node": ">=18" } }, - "node_modules/@aws-sdk/client-api-gateway/node_modules/@aws-sdk/types": { - "version": "3.901.0", - "license": "Apache-2.0", - "dependencies": { - "@smithy/types": "^4.6.0", - "tslib": "^2.6.2" - }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.24.2", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.24.2.tgz", + "integrity": "sha512-TvW7wE/89PYW+IevEJXZ5sF6gJRDY/14hyIGFXdIucxCsbRmLUcjseQu1SyTko+2idmCw94TgyaEZi9HUSOe3Q==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], "engines": { - "node": ">=18.0.0" + "node": ">=18" } }, - "node_modules/@aws-sdk/client-api-gateway/node_modules/@aws-sdk/util-endpoints": { - "version": "3.901.0", - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/types": "3.901.0", - "@smithy/types": "^4.6.0", - "@smithy/url-parser": "^4.2.0", - "@smithy/util-endpoints": "^3.2.0", - "tslib": "^2.6.2" - }, + "node_modules/@esbuild/linux-arm": { + "version": "0.24.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.24.2.tgz", + "integrity": "sha512-n0WRM/gWIdU29J57hJyUdIsk0WarGd6To0s+Y+LwvlC55wt+GT/OgkwoXCXvIue1i1sSNWblHEig00GBWiJgfA==", + "cpu": [ + "arm" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], "engines": { - "node": ">=18.0.0" + "node": ">=18" } }, - "node_modules/@aws-sdk/client-api-gateway/node_modules/@aws-sdk/util-user-agent-browser": { - "version": "3.901.0", - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/types": "3.901.0", - "@smithy/types": "^4.6.0", - "bowser": "^2.11.0", - "tslib": "^2.6.2" + "node_modules/@esbuild/linux-arm64": { + "version": "0.24.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.24.2.tgz", + "integrity": "sha512-7HnAD6074BW43YvvUmE/35Id9/NB7BeX5EoNkK9obndmZBUk8xmJJeU7DwmUeN7tkysslb2eSl6CTrYz6oEMQg==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" } }, - "node_modules/@aws-sdk/client-api-gateway/node_modules/@aws-sdk/util-user-agent-node": { - "version": "3.906.0", - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/middleware-user-agent": "3.906.0", - "@aws-sdk/types": "3.901.0", - "@smithy/node-config-provider": "^4.3.0", - "@smithy/types": "^4.6.0", - "tslib": "^2.6.2" - }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.24.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.24.2.tgz", + "integrity": "sha512-sfv0tGPQhcZOgTKO3oBE9xpHuUqguHvSo4jl+wjnKwFpapx+vUDcawbwPNuBIAYdRAvIDBfZVvXprIj3HA+Ugw==", + "cpu": [ + "ia32" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], "engines": { - "node": ">=18.0.0" - }, - "peerDependencies": { - "aws-crt": ">=1.0.0" - }, - "peerDependenciesMeta": { - "aws-crt": { - "optional": true - } + "node": ">=18" } }, - "node_modules/@aws-sdk/client-api-gateway/node_modules/@aws-sdk/xml-builder": { - "version": "3.901.0", - "license": "Apache-2.0", - "dependencies": { - "@smithy/types": "^4.6.0", - "fast-xml-parser": "5.2.5", - "tslib": "^2.6.2" - }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.24.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.24.2.tgz", + "integrity": "sha512-CN9AZr8kEndGooS35ntToZLTQLHEjtVB5n7dl8ZcTZMonJ7CCfStrYhrzF97eAecqVbVJ7APOEe18RPI4KLhwQ==", + "cpu": [ + "loong64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], "engines": { - "node": ">=18.0.0" + "node": ">=18" } }, - "node_modules/@aws-sdk/client-api-gateway/node_modules/fast-xml-parser": { - "version": "5.2.5", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/NaturalIntelligence" - } + "node_modules/@esbuild/linux-mips64el": { + "version": "0.24.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.24.2.tgz", + "integrity": "sha512-iMkk7qr/wl3exJATwkISxI7kTcmHKE+BlymIAbHO8xanq/TjHaaVThFF6ipWzPHryoFsesNQJPE/3wFJw4+huw==", + "cpu": [ + "mips64el" ], "license": "MIT", - "dependencies": { - "strnum": "^2.1.0" - }, - "bin": { - "fxparser": "src/cli/cli.js" + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" } }, - "node_modules/@aws-sdk/client-api-gateway/node_modules/strnum": { - "version": "2.1.1", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/NaturalIntelligence" - } + "node_modules/@esbuild/linux-ppc64": { + "version": "0.24.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.24.2.tgz", + "integrity": "sha512-shsVrgCZ57Vr2L8mm39kO5PPIb+843FStGt7sGGoqiiWYconSxwTiuswC1VJZLCjNiMLAMh34jg4VSEQb+iEbw==", + "cpu": [ + "ppc64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" ], - "license": "MIT" - }, - "node_modules/@aws-sdk/client-dynamodb": { - "version": "3.864.0", - "license": "Apache-2.0", - "dependencies": { - "@aws-crypto/sha256-browser": "5.2.0", - "@aws-crypto/sha256-js": "5.2.0", - "@aws-sdk/core": "3.864.0", - "@aws-sdk/credential-provider-node": "3.864.0", - "@aws-sdk/middleware-endpoint-discovery": "3.862.0", - "@aws-sdk/middleware-host-header": "3.862.0", - "@aws-sdk/middleware-logger": "3.862.0", - "@aws-sdk/middleware-recursion-detection": "3.862.0", - "@aws-sdk/middleware-user-agent": "3.864.0", - "@aws-sdk/region-config-resolver": "3.862.0", - "@aws-sdk/types": "3.862.0", - "@aws-sdk/util-endpoints": "3.862.0", - "@aws-sdk/util-user-agent-browser": "3.862.0", - "@aws-sdk/util-user-agent-node": "3.864.0", - "@smithy/config-resolver": "^4.1.5", - "@smithy/core": "^3.8.0", - "@smithy/fetch-http-handler": "^5.1.1", - "@smithy/hash-node": "^4.0.5", - "@smithy/invalid-dependency": "^4.0.5", - "@smithy/middleware-content-length": "^4.0.5", - "@smithy/middleware-endpoint": "^4.1.18", - "@smithy/middleware-retry": "^4.1.19", - "@smithy/middleware-serde": "^4.0.9", - "@smithy/middleware-stack": "^4.0.5", - "@smithy/node-config-provider": "^4.1.4", - "@smithy/node-http-handler": "^4.1.1", - "@smithy/protocol-http": "^5.1.3", - "@smithy/smithy-client": "^4.4.10", - "@smithy/types": "^4.3.2", - "@smithy/url-parser": "^4.0.5", - "@smithy/util-base64": "^4.0.0", - "@smithy/util-body-length-browser": "^4.0.0", - "@smithy/util-body-length-node": "^4.0.0", - "@smithy/util-defaults-mode-browser": "^4.0.26", - "@smithy/util-defaults-mode-node": "^4.0.26", - "@smithy/util-endpoints": "^3.0.7", - "@smithy/util-middleware": "^4.0.5", - "@smithy/util-retry": "^4.0.7", - "@smithy/util-utf8": "^4.0.0", - "@smithy/util-waiter": "^4.0.7", - "@types/uuid": "^9.0.1", - "tslib": "^2.6.2", - "uuid": "^9.0.1" - }, "engines": { - "node": ">=18.0.0" + "node": ">=18" } }, - "node_modules/@aws-sdk/client-s3": { - "version": "3.896.0", - "license": "Apache-2.0", - "dependencies": { - "@aws-crypto/sha1-browser": "5.2.0", - "@aws-crypto/sha256-browser": "5.2.0", - "@aws-crypto/sha256-js": "5.2.0", - "@aws-sdk/core": "3.896.0", - "@aws-sdk/credential-provider-node": "3.896.0", - "@aws-sdk/middleware-bucket-endpoint": "3.893.0", - "@aws-sdk/middleware-expect-continue": "3.893.0", - "@aws-sdk/middleware-flexible-checksums": "3.896.0", - "@aws-sdk/middleware-host-header": "3.893.0", - "@aws-sdk/middleware-location-constraint": "3.893.0", - "@aws-sdk/middleware-logger": "3.893.0", - "@aws-sdk/middleware-recursion-detection": "3.893.0", - "@aws-sdk/middleware-sdk-s3": "3.896.0", - "@aws-sdk/middleware-ssec": "3.893.0", - "@aws-sdk/middleware-user-agent": "3.896.0", - "@aws-sdk/region-config-resolver": "3.893.0", - "@aws-sdk/signature-v4-multi-region": "3.896.0", - "@aws-sdk/types": "3.893.0", - "@aws-sdk/util-endpoints": "3.895.0", - "@aws-sdk/util-user-agent-browser": "3.893.0", - "@aws-sdk/util-user-agent-node": "3.896.0", - "@aws-sdk/xml-builder": "3.894.0", - "@smithy/config-resolver": "^4.2.2", - "@smithy/core": "^3.12.0", - "@smithy/eventstream-serde-browser": "^4.1.1", - "@smithy/eventstream-serde-config-resolver": "^4.2.1", - "@smithy/eventstream-serde-node": "^4.1.1", - "@smithy/fetch-http-handler": "^5.2.1", - "@smithy/hash-blob-browser": "^4.1.1", - "@smithy/hash-node": "^4.1.1", - "@smithy/hash-stream-node": "^4.1.1", - "@smithy/invalid-dependency": "^4.1.1", - "@smithy/md5-js": "^4.1.1", - "@smithy/middleware-content-length": "^4.1.1", - "@smithy/middleware-endpoint": "^4.2.4", - "@smithy/middleware-retry": "^4.3.0", - "@smithy/middleware-serde": "^4.1.1", - "@smithy/middleware-stack": "^4.1.1", - "@smithy/node-config-provider": "^4.2.2", - "@smithy/node-http-handler": "^4.2.1", - "@smithy/protocol-http": "^5.2.1", - "@smithy/smithy-client": "^4.6.4", - "@smithy/types": "^4.5.0", - "@smithy/url-parser": "^4.1.1", - "@smithy/util-base64": "^4.1.0", - "@smithy/util-body-length-browser": "^4.1.0", - "@smithy/util-body-length-node": "^4.1.0", - "@smithy/util-defaults-mode-browser": "^4.1.4", - "@smithy/util-defaults-mode-node": "^4.1.4", - "@smithy/util-endpoints": "^3.1.2", - "@smithy/util-middleware": "^4.1.1", - "@smithy/util-retry": "^4.1.2", - "@smithy/util-stream": "^4.3.2", - "@smithy/util-utf8": "^4.1.0", - "@smithy/util-waiter": "^4.1.1", - "@smithy/uuid": "^1.0.0", - "tslib": "^2.6.2" - }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.24.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.24.2.tgz", + "integrity": "sha512-4eSFWnU9Hhd68fW16GD0TINewo1L6dRrB+oLNNbYyMUAeOD2yCK5KXGK1GH4qD/kT+bTEXjsyTCiJGHPZ3eM9Q==", + "cpu": [ + "riscv64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], "engines": { - "node": ">=18.0.0" + "node": ">=18" } }, - "node_modules/@aws-sdk/client-s3/node_modules/@aws-sdk/client-sso": { - "version": "3.896.0", - "license": "Apache-2.0", - "dependencies": { - "@aws-crypto/sha256-browser": "5.2.0", - "@aws-crypto/sha256-js": "5.2.0", - "@aws-sdk/core": "3.896.0", - "@aws-sdk/middleware-host-header": "3.893.0", - "@aws-sdk/middleware-logger": "3.893.0", - "@aws-sdk/middleware-recursion-detection": "3.893.0", - "@aws-sdk/middleware-user-agent": "3.896.0", - "@aws-sdk/region-config-resolver": "3.893.0", - "@aws-sdk/types": "3.893.0", - "@aws-sdk/util-endpoints": "3.895.0", - "@aws-sdk/util-user-agent-browser": "3.893.0", - "@aws-sdk/util-user-agent-node": "3.896.0", - "@smithy/config-resolver": "^4.2.2", - "@smithy/core": "^3.12.0", - "@smithy/fetch-http-handler": "^5.2.1", - "@smithy/hash-node": "^4.1.1", - "@smithy/invalid-dependency": "^4.1.1", - "@smithy/middleware-content-length": "^4.1.1", - "@smithy/middleware-endpoint": "^4.2.4", - "@smithy/middleware-retry": "^4.3.0", - "@smithy/middleware-serde": "^4.1.1", - "@smithy/middleware-stack": "^4.1.1", - "@smithy/node-config-provider": "^4.2.2", - "@smithy/node-http-handler": "^4.2.1", - "@smithy/protocol-http": "^5.2.1", - "@smithy/smithy-client": "^4.6.4", - "@smithy/types": "^4.5.0", - "@smithy/url-parser": "^4.1.1", - "@smithy/util-base64": "^4.1.0", - "@smithy/util-body-length-browser": "^4.1.0", - "@smithy/util-body-length-node": "^4.1.0", - "@smithy/util-defaults-mode-browser": "^4.1.4", - "@smithy/util-defaults-mode-node": "^4.1.4", - "@smithy/util-endpoints": "^3.1.2", - "@smithy/util-middleware": "^4.1.1", - "@smithy/util-retry": "^4.1.2", - "@smithy/util-utf8": "^4.1.0", - "tslib": "^2.6.2" - }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.24.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.24.2.tgz", + "integrity": "sha512-S0Bh0A53b0YHL2XEXC20bHLuGMOhFDO6GN4b3YjRLK//Ep3ql3erpNcPlEFed93hsQAjAQDNsvcK+hV90FubSw==", + "cpu": [ + "s390x" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], "engines": { - "node": ">=18.0.0" + "node": ">=18" } }, - "node_modules/@aws-sdk/client-s3/node_modules/@aws-sdk/core": { - "version": "3.896.0", - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/types": "3.893.0", - "@aws-sdk/xml-builder": "3.894.0", - "@smithy/core": "^3.12.0", - "@smithy/node-config-provider": "^4.2.2", - "@smithy/property-provider": "^4.1.1", - "@smithy/protocol-http": "^5.2.1", - "@smithy/signature-v4": "^5.2.1", - "@smithy/smithy-client": "^4.6.4", - "@smithy/types": "^4.5.0", - "@smithy/util-base64": "^4.1.0", - "@smithy/util-middleware": "^4.1.1", - "@smithy/util-utf8": "^4.1.0", - "tslib": "^2.6.2" - }, + "node_modules/@esbuild/linux-x64": { + "version": "0.24.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.24.2.tgz", + "integrity": "sha512-8Qi4nQcCTbLnK9WoMjdC9NiTG6/E38RNICU6sUNqK0QFxCYgoARqVqxdFmWkdonVsvGqWhmm7MO0jyTqLqwj0Q==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], "engines": { - "node": ">=18.0.0" + "node": ">=18" } }, - "node_modules/@aws-sdk/client-s3/node_modules/@aws-sdk/credential-provider-env": { - "version": "3.896.0", - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/core": "3.896.0", - "@aws-sdk/types": "3.893.0", - "@smithy/property-provider": "^4.1.1", - "@smithy/types": "^4.5.0", - "tslib": "^2.6.2" - }, + "node_modules/@esbuild/netbsd-arm64": { + "version": "0.24.2", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.24.2.tgz", + "integrity": "sha512-wuLK/VztRRpMt9zyHSazyCVdCXlpHkKm34WUyinD2lzK07FAHTq0KQvZZlXikNWkDGoT6x3TD51jKQ7gMVpopw==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], "engines": { - "node": ">=18.0.0" + "node": ">=18" } }, - "node_modules/@aws-sdk/client-s3/node_modules/@aws-sdk/credential-provider-http": { - "version": "3.896.0", - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/core": "3.896.0", - "@aws-sdk/types": "3.893.0", - "@smithy/fetch-http-handler": "^5.2.1", - "@smithy/node-http-handler": "^4.2.1", - "@smithy/property-provider": "^4.1.1", - "@smithy/protocol-http": "^5.2.1", - "@smithy/smithy-client": "^4.6.4", - "@smithy/types": "^4.5.0", - "@smithy/util-stream": "^4.3.2", - "tslib": "^2.6.2" - }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.24.2", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.24.2.tgz", + "integrity": "sha512-VefFaQUc4FMmJuAxmIHgUmfNiLXY438XrL4GDNV1Y1H/RW3qow68xTwjZKfj/+Plp9NANmzbH5R40Meudu8mmw==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], "engines": { - "node": ">=18.0.0" + "node": ">=18" } }, - "node_modules/@aws-sdk/client-s3/node_modules/@aws-sdk/credential-provider-ini": { - "version": "3.896.0", - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/core": "3.896.0", - "@aws-sdk/credential-provider-env": "3.896.0", - "@aws-sdk/credential-provider-http": "3.896.0", - "@aws-sdk/credential-provider-process": "3.896.0", - "@aws-sdk/credential-provider-sso": "3.896.0", - "@aws-sdk/credential-provider-web-identity": "3.896.0", - "@aws-sdk/nested-clients": "3.896.0", - "@aws-sdk/types": "3.893.0", - "@smithy/credential-provider-imds": "^4.1.2", - "@smithy/property-provider": "^4.1.1", - "@smithy/shared-ini-file-loader": "^4.2.0", - "@smithy/types": "^4.5.0", - "tslib": "^2.6.2" - }, + "node_modules/@esbuild/openbsd-arm64": { + "version": "0.24.2", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.24.2.tgz", + "integrity": "sha512-YQbi46SBct6iKnszhSvdluqDmxCJA+Pu280Av9WICNwQmMxV7nLRHZfjQzwbPs3jeWnuAhE9Jy0NrnJ12Oz+0A==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], "engines": { - "node": ">=18.0.0" + "node": ">=18" } }, - "node_modules/@aws-sdk/client-s3/node_modules/@aws-sdk/credential-provider-node": { - "version": "3.896.0", - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/credential-provider-env": "3.896.0", - "@aws-sdk/credential-provider-http": "3.896.0", - "@aws-sdk/credential-provider-ini": "3.896.0", - "@aws-sdk/credential-provider-process": "3.896.0", - "@aws-sdk/credential-provider-sso": "3.896.0", - "@aws-sdk/credential-provider-web-identity": "3.896.0", - "@aws-sdk/types": "3.893.0", - "@smithy/credential-provider-imds": "^4.1.2", - "@smithy/property-provider": "^4.1.1", - "@smithy/shared-ini-file-loader": "^4.2.0", - "@smithy/types": "^4.5.0", - "tslib": "^2.6.2" - }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.24.2", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.24.2.tgz", + "integrity": "sha512-+iDS6zpNM6EnJyWv0bMGLWSWeXGN/HTaF/LXHXHwejGsVi+ooqDfMCCTerNFxEkM3wYVcExkeGXNqshc9iMaOA==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], "engines": { - "node": ">=18.0.0" + "node": ">=18" } }, - "node_modules/@aws-sdk/client-s3/node_modules/@aws-sdk/credential-provider-process": { - "version": "3.896.0", - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/core": "3.896.0", - "@aws-sdk/types": "3.893.0", - "@smithy/property-provider": "^4.1.1", - "@smithy/shared-ini-file-loader": "^4.2.0", - "@smithy/types": "^4.5.0", - "tslib": "^2.6.2" - }, + "node_modules/@esbuild/openharmony-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.25.12.tgz", + "integrity": "sha512-rm0YWsqUSRrjncSXGA7Zv78Nbnw4XL6/dzr20cyrQf7ZmRcsovpcRBdhD43Nuk3y7XIoW2OxMVvwuRvk9XdASg==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ], "engines": { - "node": ">=18.0.0" + "node": ">=18" } }, - "node_modules/@aws-sdk/client-s3/node_modules/@aws-sdk/credential-provider-sso": { - "version": "3.896.0", - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/client-sso": "3.896.0", - "@aws-sdk/core": "3.896.0", - "@aws-sdk/token-providers": "3.896.0", - "@aws-sdk/types": "3.893.0", - "@smithy/property-provider": "^4.1.1", - "@smithy/shared-ini-file-loader": "^4.2.0", - "@smithy/types": "^4.5.0", - "tslib": "^2.6.2" - }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.24.2", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.24.2.tgz", + "integrity": "sha512-hTdsW27jcktEvpwNHJU4ZwWFGkz2zRJUz8pvddmXPtXDzVKTTINmlmga3ZzwcuMpUvLw7JkLy9QLKyGpD2Yxig==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], "engines": { - "node": ">=18.0.0" + "node": ">=18" } }, - "node_modules/@aws-sdk/client-s3/node_modules/@aws-sdk/credential-provider-web-identity": { - "version": "3.896.0", - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/core": "3.896.0", - "@aws-sdk/nested-clients": "3.896.0", - "@aws-sdk/types": "3.893.0", - "@smithy/property-provider": "^4.1.1", - "@smithy/shared-ini-file-loader": "^4.2.0", - "@smithy/types": "^4.5.0", - "tslib": "^2.6.2" - }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.24.2", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.24.2.tgz", + "integrity": "sha512-LihEQ2BBKVFLOC9ZItT9iFprsE9tqjDjnbulhHoFxYQtQfai7qfluVODIYxt1PgdoyQkz23+01rzwNwYfutxUQ==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], "engines": { - "node": ">=18.0.0" + "node": ">=18" } }, - "node_modules/@aws-sdk/client-s3/node_modules/@aws-sdk/middleware-host-header": { - "version": "3.893.0", - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/types": "3.893.0", - "@smithy/protocol-http": "^5.2.1", - "@smithy/types": "^4.5.0", - "tslib": "^2.6.2" - }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.24.2", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.24.2.tgz", + "integrity": "sha512-q+iGUwfs8tncmFC9pcnD5IvRHAzmbwQ3GPS5/ceCyHdjXubwQWI12MKWSNSMYLJMq23/IUCvJMS76PDqXe1fxA==", + "cpu": [ + "ia32" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], "engines": { - "node": ">=18.0.0" + "node": ">=18" } }, - "node_modules/@aws-sdk/client-s3/node_modules/@aws-sdk/middleware-logger": { - "version": "3.893.0", - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/types": "3.893.0", - "@smithy/types": "^4.5.0", - "tslib": "^2.6.2" - }, + "node_modules/@esbuild/win32-x64": { + "version": "0.24.2", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.24.2.tgz", + "integrity": "sha512-7VTgWzgMGvup6aSqDPLiW5zHaxYJGTO4OokMjIlrCtf+VpEL+cXKtCvg723iguPYI5oaUNdS+/V7OU2gvXVWEg==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], "engines": { - "node": ">=18.0.0" + "node": ">=18" } }, - "node_modules/@aws-sdk/client-s3/node_modules/@aws-sdk/middleware-recursion-detection": { - "version": "3.893.0", - "license": "Apache-2.0", + "node_modules/@eslint-community/eslint-utils": { + "version": "4.9.0", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.9.0.tgz", + "integrity": "sha512-ayVFHdtZ+hsq1t2Dy24wCmGXGe4q9Gu3smhLYALJrr473ZH27MsnSL+LKUlimp4BWJqMDMLmPpx/Q9R3OAlL4g==", + "dev": true, + "license": "MIT", "dependencies": { - "@aws-sdk/types": "3.893.0", - "@aws/lambda-invoke-store": "^0.0.1", - "@smithy/protocol-http": "^5.2.1", - "@smithy/types": "^4.5.0", - "tslib": "^2.6.2" + "eslint-visitor-keys": "^3.4.3" }, "engines": { - "node": ">=18.0.0" + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" } }, - "node_modules/@aws-sdk/client-s3/node_modules/@aws-sdk/middleware-user-agent": { - "version": "3.896.0", + "node_modules/@eslint-community/eslint-utils/node_modules/eslint-visitor-keys": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", + "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", + "dev": true, "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/core": "3.896.0", - "@aws-sdk/types": "3.893.0", - "@aws-sdk/util-endpoints": "3.895.0", - "@smithy/core": "^3.12.0", - "@smithy/protocol-http": "^5.2.1", - "@smithy/types": "^4.5.0", - "tslib": "^2.6.2" + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@eslint-community/regexpp": { + "version": "4.12.2", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.2.tgz", + "integrity": "sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew==", + "dev": true, + "license": "MIT", "engines": { - "node": ">=18.0.0" + "node": "^12.0.0 || ^14.0.0 || >=16.0.0" } }, - "node_modules/@aws-sdk/client-s3/node_modules/@aws-sdk/nested-clients": { - "version": "3.896.0", + "node_modules/@eslint/config-array": { + "version": "0.21.1", + "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.21.1.tgz", + "integrity": "sha512-aw1gNayWpdI/jSYVgzN5pL0cfzU02GT3NBpeT/DXbx1/1x7ZKxFPd9bwrzygx/qiwIQiJ1sw/zD8qY/kRvlGHA==", + "dev": true, "license": "Apache-2.0", "dependencies": { - "@aws-crypto/sha256-browser": "5.2.0", - "@aws-crypto/sha256-js": "5.2.0", - "@aws-sdk/core": "3.896.0", - "@aws-sdk/middleware-host-header": "3.893.0", - "@aws-sdk/middleware-logger": "3.893.0", - "@aws-sdk/middleware-recursion-detection": "3.893.0", - "@aws-sdk/middleware-user-agent": "3.896.0", - "@aws-sdk/region-config-resolver": "3.893.0", - "@aws-sdk/types": "3.893.0", - "@aws-sdk/util-endpoints": "3.895.0", - "@aws-sdk/util-user-agent-browser": "3.893.0", - "@aws-sdk/util-user-agent-node": "3.896.0", - "@smithy/config-resolver": "^4.2.2", - "@smithy/core": "^3.12.0", - "@smithy/fetch-http-handler": "^5.2.1", - "@smithy/hash-node": "^4.1.1", - "@smithy/invalid-dependency": "^4.1.1", - "@smithy/middleware-content-length": "^4.1.1", - "@smithy/middleware-endpoint": "^4.2.4", - "@smithy/middleware-retry": "^4.3.0", - "@smithy/middleware-serde": "^4.1.1", - "@smithy/middleware-stack": "^4.1.1", - "@smithy/node-config-provider": "^4.2.2", - "@smithy/node-http-handler": "^4.2.1", - "@smithy/protocol-http": "^5.2.1", - "@smithy/smithy-client": "^4.6.4", - "@smithy/types": "^4.5.0", - "@smithy/url-parser": "^4.1.1", - "@smithy/util-base64": "^4.1.0", - "@smithy/util-body-length-browser": "^4.1.0", - "@smithy/util-body-length-node": "^4.1.0", - "@smithy/util-defaults-mode-browser": "^4.1.4", - "@smithy/util-defaults-mode-node": "^4.1.4", - "@smithy/util-endpoints": "^3.1.2", - "@smithy/util-middleware": "^4.1.1", - "@smithy/util-retry": "^4.1.2", - "@smithy/util-utf8": "^4.1.0", - "tslib": "^2.6.2" + "@eslint/object-schema": "^2.1.7", + "debug": "^4.3.1", + "minimatch": "^3.1.2" }, "engines": { - "node": ">=18.0.0" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" } }, - "node_modules/@aws-sdk/client-s3/node_modules/@aws-sdk/region-config-resolver": { - "version": "3.893.0", - "license": "Apache-2.0", + "node_modules/@eslint/config-array/node_modules/brace-expansion": { + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", + "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", + "dev": true, + "license": "MIT", "dependencies": { - "@aws-sdk/types": "3.893.0", - "@smithy/node-config-provider": "^4.2.2", - "@smithy/types": "^4.5.0", - "@smithy/util-config-provider": "^4.1.0", - "@smithy/util-middleware": "^4.1.1", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" } }, - "node_modules/@aws-sdk/client-s3/node_modules/@aws-sdk/token-providers": { - "version": "3.896.0", - "license": "Apache-2.0", + "node_modules/@eslint/config-array/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "license": "ISC", "dependencies": { - "@aws-sdk/core": "3.896.0", - "@aws-sdk/nested-clients": "3.896.0", - "@aws-sdk/types": "3.893.0", - "@smithy/property-provider": "^4.1.1", - "@smithy/shared-ini-file-loader": "^4.2.0", - "@smithy/types": "^4.5.0", - "tslib": "^2.6.2" + "brace-expansion": "^1.1.7" }, "engines": { - "node": ">=18.0.0" + "node": "*" } }, - "node_modules/@aws-sdk/client-s3/node_modules/@aws-sdk/types": { - "version": "3.893.0", + "node_modules/@eslint/config-helpers": { + "version": "0.4.2", + "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.4.2.tgz", + "integrity": "sha512-gBrxN88gOIf3R7ja5K9slwNayVcZgK6SOUORm2uBzTeIEfeVaIhOpCtTox3P6R7o2jLFwLFTLnC7kU/RGcYEgw==", + "dev": true, "license": "Apache-2.0", "dependencies": { - "@smithy/types": "^4.5.0", - "tslib": "^2.6.2" + "@eslint/core": "^0.17.0" }, "engines": { - "node": ">=18.0.0" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" } }, - "node_modules/@aws-sdk/client-s3/node_modules/@aws-sdk/util-endpoints": { - "version": "3.895.0", + "node_modules/@eslint/core": { + "version": "0.17.0", + "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.17.0.tgz", + "integrity": "sha512-yL/sLrpmtDaFEiUj1osRP4TI2MDz1AddJL+jZ7KSqvBuliN4xqYY54IfdN8qD8Toa6g1iloph1fxQNkjOxrrpQ==", + "dev": true, "license": "Apache-2.0", "dependencies": { - "@aws-sdk/types": "3.893.0", - "@smithy/types": "^4.5.0", - "@smithy/url-parser": "^4.1.1", - "@smithy/util-endpoints": "^3.1.2", - "tslib": "^2.6.2" + "@types/json-schema": "^7.0.15" }, "engines": { - "node": ">=18.0.0" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" } }, - "node_modules/@aws-sdk/client-s3/node_modules/@aws-sdk/util-user-agent-browser": { - "version": "3.893.0", - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/types": "3.893.0", - "@smithy/types": "^4.5.0", - "bowser": "^2.11.0", - "tslib": "^2.6.2" - } - }, - "node_modules/@aws-sdk/client-s3/node_modules/@aws-sdk/util-user-agent-node": { - "version": "3.896.0", - "license": "Apache-2.0", + "node_modules/@eslint/eslintrc": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.3.3.tgz", + "integrity": "sha512-Kr+LPIUVKz2qkx1HAMH8q1q6azbqBAsXJUxBl/ODDuVPX45Z9DfwB8tPjTi6nNZ8BuM3nbJxC5zCAg5elnBUTQ==", + "dev": true, + "license": "MIT", "dependencies": { - "@aws-sdk/middleware-user-agent": "3.896.0", - "@aws-sdk/types": "3.893.0", - "@smithy/node-config-provider": "^4.2.2", - "@smithy/types": "^4.5.0", - "tslib": "^2.6.2" + "ajv": "^6.12.4", + "debug": "^4.3.2", + "espree": "^10.0.1", + "globals": "^14.0.0", + "ignore": "^5.2.0", + "import-fresh": "^3.2.1", + "js-yaml": "^4.1.1", + "minimatch": "^3.1.2", + "strip-json-comments": "^3.1.1" }, "engines": { - "node": ">=18.0.0" - }, - "peerDependencies": { - "aws-crt": ">=1.0.0" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, - "peerDependenciesMeta": { - "aws-crt": { - "optional": true - } + "funding": { + "url": "https://opencollective.com/eslint" } }, - "node_modules/@aws-sdk/client-s3/node_modules/@aws-sdk/xml-builder": { - "version": "3.894.0", - "license": "Apache-2.0", + "node_modules/@eslint/eslintrc/node_modules/ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dev": true, + "license": "MIT", "dependencies": { - "@smithy/types": "^4.5.0", - "fast-xml-parser": "5.2.5", - "tslib": "^2.6.2" + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" }, - "engines": { - "node": ">=18.0.0" + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" } }, - "node_modules/@aws-sdk/client-s3/node_modules/fast-xml-parser": { - "version": "5.2.5", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/NaturalIntelligence" - } - ], + "node_modules/@eslint/eslintrc/node_modules/brace-expansion": { + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", + "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", + "dev": true, "license": "MIT", "dependencies": { - "strnum": "^2.1.0" - }, - "bin": { - "fxparser": "src/cli/cli.js" + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" } }, - "node_modules/@aws-sdk/client-s3/node_modules/strnum": { - "version": "2.1.1", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/NaturalIntelligence" - } - ], - "license": "MIT" - }, - "node_modules/@aws-sdk/client-sso": { - "version": "3.864.0", - "license": "Apache-2.0", - "dependencies": { - "@aws-crypto/sha256-browser": "5.2.0", - "@aws-crypto/sha256-js": "5.2.0", - "@aws-sdk/core": "3.864.0", - "@aws-sdk/middleware-host-header": "3.862.0", - "@aws-sdk/middleware-logger": "3.862.0", - "@aws-sdk/middleware-recursion-detection": "3.862.0", - "@aws-sdk/middleware-user-agent": "3.864.0", - "@aws-sdk/region-config-resolver": "3.862.0", - "@aws-sdk/types": "3.862.0", - "@aws-sdk/util-endpoints": "3.862.0", - "@aws-sdk/util-user-agent-browser": "3.862.0", - "@aws-sdk/util-user-agent-node": "3.864.0", - "@smithy/config-resolver": "^4.1.5", - "@smithy/core": "^3.8.0", - "@smithy/fetch-http-handler": "^5.1.1", - "@smithy/hash-node": "^4.0.5", - "@smithy/invalid-dependency": "^4.0.5", - "@smithy/middleware-content-length": "^4.0.5", - "@smithy/middleware-endpoint": "^4.1.18", - "@smithy/middleware-retry": "^4.1.19", - "@smithy/middleware-serde": "^4.0.9", - "@smithy/middleware-stack": "^4.0.5", - "@smithy/node-config-provider": "^4.1.4", - "@smithy/node-http-handler": "^4.1.1", - "@smithy/protocol-http": "^5.1.3", - "@smithy/smithy-client": "^4.4.10", - "@smithy/types": "^4.3.2", - "@smithy/url-parser": "^4.0.5", - "@smithy/util-base64": "^4.0.0", - "@smithy/util-body-length-browser": "^4.0.0", - "@smithy/util-body-length-node": "^4.0.0", - "@smithy/util-defaults-mode-browser": "^4.0.26", - "@smithy/util-defaults-mode-node": "^4.0.26", - "@smithy/util-endpoints": "^3.0.7", - "@smithy/util-middleware": "^4.0.5", - "@smithy/util-retry": "^4.0.7", - "@smithy/util-utf8": "^4.0.0", - "tslib": "^2.6.2" - }, + "node_modules/@eslint/eslintrc/node_modules/ignore": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", + "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", + "dev": true, + "license": "MIT", "engines": { - "node": ">=18.0.0" + "node": ">= 4" } }, - "node_modules/@aws-sdk/core": { - "version": "3.864.0", - "license": "Apache-2.0", + "node_modules/@eslint/eslintrc/node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@eslint/eslintrc/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "license": "ISC", "dependencies": { - "@aws-sdk/types": "3.862.0", - "@aws-sdk/xml-builder": "3.862.0", - "@smithy/core": "^3.8.0", - "@smithy/node-config-provider": "^4.1.4", - "@smithy/property-provider": "^4.0.5", - "@smithy/protocol-http": "^5.1.3", - "@smithy/signature-v4": "^5.1.3", - "@smithy/smithy-client": "^4.4.10", - "@smithy/types": "^4.3.2", - "@smithy/util-base64": "^4.0.0", - "@smithy/util-body-length-browser": "^4.0.0", - "@smithy/util-middleware": "^4.0.5", - "@smithy/util-utf8": "^4.0.0", - "fast-xml-parser": "5.2.5", - "tslib": "^2.6.2" + "brace-expansion": "^1.1.7" }, "engines": { - "node": ">=18.0.0" + "node": "*" } }, - "node_modules/@aws-sdk/core/node_modules/fast-xml-parser": { - "version": "5.2.5", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/NaturalIntelligence" - } - ], + "node_modules/@eslint/js": { + "version": "9.39.2", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.39.2.tgz", + "integrity": "sha512-q1mjIoW1VX4IvSocvM/vbTiveKC4k9eLrajNEuSsmjymSDEbpGddtpfOoN7YGAqBK3NG+uqo8ia4PDTt8buCYA==", + "dev": true, "license": "MIT", - "dependencies": { - "strnum": "^2.1.0" + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, - "bin": { - "fxparser": "src/cli/cli.js" + "funding": { + "url": "https://eslint.org/donate" } }, - "node_modules/@aws-sdk/core/node_modules/strnum": { - "version": "2.1.1", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/NaturalIntelligence" - } - ], - "license": "MIT" - }, - "node_modules/@aws-sdk/credential-provider-env": { - "version": "3.864.0", + "node_modules/@eslint/object-schema": { + "version": "2.1.7", + "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-2.1.7.tgz", + "integrity": "sha512-VtAOaymWVfZcmZbp6E2mympDIHvyjXs/12LqWYjVw6qjrfF+VK+fyG33kChz3nnK+SU5/NeHOqrTEHS8sXO3OA==", + "dev": true, "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/core": "3.864.0", - "@aws-sdk/types": "3.862.0", - "@smithy/property-provider": "^4.0.5", - "@smithy/types": "^4.3.2", - "tslib": "^2.6.2" - }, "engines": { - "node": ">=18.0.0" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" } }, - "node_modules/@aws-sdk/credential-provider-http": { - "version": "3.864.0", + "node_modules/@eslint/plugin-kit": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.4.1.tgz", + "integrity": "sha512-43/qtrDUokr7LJqoF2c3+RInu/t4zfrpYdoSDfYyhg52rwLV6TnOvdG4fXm7IkSB3wErkcmJS9iEhjVtOSEjjA==", + "dev": true, "license": "Apache-2.0", "dependencies": { - "@aws-sdk/core": "3.864.0", - "@aws-sdk/types": "3.862.0", - "@smithy/fetch-http-handler": "^5.1.1", - "@smithy/node-http-handler": "^4.1.1", - "@smithy/property-provider": "^4.0.5", - "@smithy/protocol-http": "^5.1.3", - "@smithy/smithy-client": "^4.4.10", - "@smithy/types": "^4.3.2", - "@smithy/util-stream": "^4.2.4", - "tslib": "^2.6.2" + "@eslint/core": "^0.17.0", + "levn": "^0.4.1" }, "engines": { - "node": ">=18.0.0" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" } }, - "node_modules/@aws-sdk/credential-provider-ini": { - "version": "3.864.0", - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/core": "3.864.0", - "@aws-sdk/credential-provider-env": "3.864.0", - "@aws-sdk/credential-provider-http": "3.864.0", - "@aws-sdk/credential-provider-process": "3.864.0", - "@aws-sdk/credential-provider-sso": "3.864.0", - "@aws-sdk/credential-provider-web-identity": "3.864.0", - "@aws-sdk/nested-clients": "3.864.0", - "@aws-sdk/types": "3.862.0", - "@smithy/credential-provider-imds": "^4.0.7", - "@smithy/property-provider": "^4.0.5", - "@smithy/shared-ini-file-loader": "^4.0.5", - "@smithy/types": "^4.3.2", - "tslib": "^2.6.2" - }, + "node_modules/@exodus/schemasafe": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@exodus/schemasafe/-/schemasafe-1.3.0.tgz", + "integrity": "sha512-5Aap/GaRupgNx/feGBwLLTVv8OQFfv3pq2lPRzPg9R+IOBnDgghTGW7l7EuVXOvg5cc/xSAlRW8rBrjIC3Nvqw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@faker-js/faker": { + "version": "7.6.0", + "resolved": "https://registry.npmjs.org/@faker-js/faker/-/faker-7.6.0.tgz", + "integrity": "sha512-XK6BTq1NDMo9Xqw/YkYyGjSsg44fbNwYRx7QK2CuoQgyy+f1rrTDHoExVM5PsyXCtfl2vs2vVJ0MN0yN6LppRw==", + "dev": true, + "license": "MIT", "engines": { - "node": ">=18.0.0" + "node": ">=14.0.0", + "npm": ">=6.0.0" } }, - "node_modules/@aws-sdk/credential-provider-node": { - "version": "3.864.0", + "node_modules/@grpc/grpc-js": { + "version": "1.14.3", + "resolved": "https://registry.npmjs.org/@grpc/grpc-js/-/grpc-js-1.14.3.tgz", + "integrity": "sha512-Iq8QQQ/7X3Sac15oB6p0FmUg/klxQvXLeileoqrTRGJYLV+/9tubbr9ipz0GKHjmXVsgFPo/+W+2cA8eNcR+XA==", + "dev": true, "license": "Apache-2.0", "dependencies": { - "@aws-sdk/credential-provider-env": "3.864.0", - "@aws-sdk/credential-provider-http": "3.864.0", - "@aws-sdk/credential-provider-ini": "3.864.0", - "@aws-sdk/credential-provider-process": "3.864.0", - "@aws-sdk/credential-provider-sso": "3.864.0", - "@aws-sdk/credential-provider-web-identity": "3.864.0", - "@aws-sdk/types": "3.862.0", - "@smithy/credential-provider-imds": "^4.0.7", - "@smithy/property-provider": "^4.0.5", - "@smithy/shared-ini-file-loader": "^4.0.5", - "@smithy/types": "^4.3.2", - "tslib": "^2.6.2" + "@grpc/proto-loader": "^0.8.0", + "@js-sdsl/ordered-map": "^4.4.2" }, "engines": { - "node": ">=18.0.0" + "node": ">=12.10.0" } }, - "node_modules/@aws-sdk/credential-provider-process": { - "version": "3.864.0", + "node_modules/@grpc/grpc-js/node_modules/@grpc/proto-loader": { + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/@grpc/proto-loader/-/proto-loader-0.8.0.tgz", + "integrity": "sha512-rc1hOQtjIWGxcxpb9aHAfLpIctjEnsDehj0DAiVfBlmT84uvR0uUtN2hEi/ecvWVjXUGf5qPF4qEgiLOx1YIMQ==", + "dev": true, "license": "Apache-2.0", "dependencies": { - "@aws-sdk/core": "3.864.0", - "@aws-sdk/types": "3.862.0", - "@smithy/property-provider": "^4.0.5", - "@smithy/shared-ini-file-loader": "^4.0.5", - "@smithy/types": "^4.3.2", - "tslib": "^2.6.2" + "lodash.camelcase": "^4.3.0", + "long": "^5.0.0", + "protobufjs": "^7.5.3", + "yargs": "^17.7.2" + }, + "bin": { + "proto-loader-gen-types": "build/bin/proto-loader-gen-types.js" }, "engines": { - "node": ">=18.0.0" + "node": ">=6" } }, - "node_modules/@aws-sdk/credential-provider-sso": { - "version": "3.864.0", + "node_modules/@grpc/proto-loader": { + "version": "0.7.15", + "resolved": "https://registry.npmjs.org/@grpc/proto-loader/-/proto-loader-0.7.15.tgz", + "integrity": "sha512-tMXdRCfYVixjuFK+Hk0Q1s38gV9zDiDJfWL3h1rv4Qc39oILCu1TRTDt7+fGUI8K4G1Fj125Hx/ru3azECWTyQ==", + "dev": true, "license": "Apache-2.0", "dependencies": { - "@aws-sdk/client-sso": "3.864.0", - "@aws-sdk/core": "3.864.0", - "@aws-sdk/token-providers": "3.864.0", - "@aws-sdk/types": "3.862.0", - "@smithy/property-provider": "^4.0.5", - "@smithy/shared-ini-file-loader": "^4.0.5", - "@smithy/types": "^4.3.2", - "tslib": "^2.6.2" + "lodash.camelcase": "^4.3.0", + "long": "^5.0.0", + "protobufjs": "^7.2.5", + "yargs": "^17.7.2" + }, + "bin": { + "proto-loader-gen-types": "build/bin/proto-loader-gen-types.js" }, "engines": { - "node": ">=18.0.0" + "node": ">=6" } }, - "node_modules/@aws-sdk/credential-provider-web-identity": { - "version": "3.864.0", + "node_modules/@humanfs/core": { + "version": "0.19.1", + "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.1.tgz", + "integrity": "sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==", + "dev": true, "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/core": "3.864.0", - "@aws-sdk/nested-clients": "3.864.0", - "@aws-sdk/types": "3.862.0", - "@smithy/property-provider": "^4.0.5", - "@smithy/types": "^4.3.2", - "tslib": "^2.6.2" - }, "engines": { - "node": ">=18.0.0" + "node": ">=18.18.0" } }, - "node_modules/@aws-sdk/endpoint-cache": { - "version": "3.804.0", + "node_modules/@humanfs/node": { + "version": "0.16.7", + "resolved": "https://registry.npmjs.org/@humanfs/node/-/node-0.16.7.tgz", + "integrity": "sha512-/zUx+yOsIrG4Y43Eh2peDeKCxlRt/gET6aHfaKpuq267qXdYDFViVHfMaLyygZOnl0kGWxFIgsBy8QFuTLUXEQ==", + "dev": true, "license": "Apache-2.0", "dependencies": { - "mnemonist": "0.38.3", - "tslib": "^2.6.2" + "@humanfs/core": "^0.19.1", + "@humanwhocodes/retry": "^0.4.0" }, "engines": { - "node": ">=18.0.0" + "node": ">=18.18.0" } }, - "node_modules/@aws-sdk/lib-dynamodb": { - "version": "3.864.0", + "node_modules/@humanwhocodes/module-importer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", + "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", + "dev": true, "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/core": "3.864.0", - "@aws-sdk/util-dynamodb": "3.864.0", - "@smithy/core": "^3.8.0", - "@smithy/smithy-client": "^4.4.10", - "@smithy/types": "^4.3.2", - "tslib": "^2.6.2" - }, "engines": { - "node": ">=18.0.0" + "node": ">=12.22" }, - "peerDependencies": { - "@aws-sdk/client-dynamodb": "^3.864.0" + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" } }, - "node_modules/@aws-sdk/middleware-bucket-endpoint": { - "version": "3.893.0", + "node_modules/@humanwhocodes/momoa": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/@humanwhocodes/momoa/-/momoa-2.0.4.tgz", + "integrity": "sha512-RE815I4arJFtt+FVeU1Tgp9/Xvecacji8w/V6XtXsWWH/wz/eNkNbhb+ny/+PlVZjV0rxQpRSQKNKE3lcktHEA==", + "dev": true, "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/types": "3.893.0", - "@aws-sdk/util-arn-parser": "3.893.0", - "@smithy/node-config-provider": "^4.2.2", - "@smithy/protocol-http": "^5.2.1", - "@smithy/types": "^4.5.0", - "@smithy/util-config-provider": "^4.1.0", - "tslib": "^2.6.2" - }, "engines": { - "node": ">=18.0.0" + "node": ">=10.10.0" } }, - "node_modules/@aws-sdk/middleware-bucket-endpoint/node_modules/@aws-sdk/types": { - "version": "3.893.0", + "node_modules/@humanwhocodes/retry": { + "version": "0.4.3", + "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.4.3.tgz", + "integrity": "sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==", + "dev": true, "license": "Apache-2.0", - "dependencies": { - "@smithy/types": "^4.5.0", - "tslib": "^2.6.2" - }, "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@aws-sdk/middleware-endpoint-discovery": { - "version": "3.862.0", - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/endpoint-cache": "3.804.0", - "@aws-sdk/types": "3.862.0", - "@smithy/node-config-provider": "^4.1.4", - "@smithy/protocol-http": "^5.1.3", - "@smithy/types": "^4.3.2", - "tslib": "^2.6.2" + "node": ">=18.18" }, - "engines": { - "node": ">=18.0.0" + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" } }, - "node_modules/@aws-sdk/middleware-expect-continue": { - "version": "3.893.0", - "license": "Apache-2.0", + "node_modules/@inquirer/external-editor": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@inquirer/external-editor/-/external-editor-1.0.3.tgz", + "integrity": "sha512-RWbSrDiYmO4LbejWY7ttpxczuwQyZLBUyygsA9Nsv95hpzUWwnNTVQmAq3xuh7vNwCp07UTmE5i11XAEExx4RA==", + "dev": true, + "license": "MIT", "dependencies": { - "@aws-sdk/types": "3.893.0", - "@smithy/protocol-http": "^5.2.1", - "@smithy/types": "^4.5.0", - "tslib": "^2.6.2" + "chardet": "^2.1.1", + "iconv-lite": "^0.7.0" }, "engines": { - "node": ">=18.0.0" + "node": ">=18" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } } }, - "node_modules/@aws-sdk/middleware-expect-continue/node_modules/@aws-sdk/types": { - "version": "3.893.0", - "license": "Apache-2.0", - "dependencies": { - "@smithy/types": "^4.5.0", - "tslib": "^2.6.2" - }, + "node_modules/@internal/datastore": { + "resolved": "internal/datastore", + "link": true + }, + "node_modules/@internal/helpers": { + "resolved": "internal/helpers", + "link": true + }, + "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, + "license": "MIT", "engines": { - "node": ">=18.0.0" + "node": "20 || >=22" } }, - "node_modules/@aws-sdk/middleware-flexible-checksums": { - "version": "3.896.0", - "license": "Apache-2.0", + "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, + "license": "MIT", "dependencies": { - "@aws-crypto/crc32": "5.2.0", - "@aws-crypto/crc32c": "5.2.0", - "@aws-crypto/util": "5.2.0", - "@aws-sdk/core": "3.896.0", - "@aws-sdk/types": "3.893.0", - "@smithy/is-array-buffer": "^4.1.0", - "@smithy/node-config-provider": "^4.2.2", - "@smithy/protocol-http": "^5.2.1", - "@smithy/types": "^4.5.0", - "@smithy/util-middleware": "^4.1.1", - "@smithy/util-stream": "^4.3.2", - "@smithy/util-utf8": "^4.1.0", - "tslib": "^2.6.2" + "@isaacs/balanced-match": "^4.0.1" }, "engines": { - "node": ">=18.0.0" + "node": "20 || >=22" } }, - "node_modules/@aws-sdk/middleware-flexible-checksums/node_modules/@aws-sdk/core": { - "version": "3.896.0", - "license": "Apache-2.0", + "node_modules/@isaacs/cliui": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", + "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", + "dev": true, + "license": "ISC", "dependencies": { - "@aws-sdk/types": "3.893.0", - "@aws-sdk/xml-builder": "3.894.0", - "@smithy/core": "^3.12.0", - "@smithy/node-config-provider": "^4.2.2", - "@smithy/property-provider": "^4.1.1", - "@smithy/protocol-http": "^5.2.1", - "@smithy/signature-v4": "^5.2.1", - "@smithy/smithy-client": "^4.6.4", - "@smithy/types": "^4.5.0", - "@smithy/util-base64": "^4.1.0", - "@smithy/util-middleware": "^4.1.1", - "@smithy/util-utf8": "^4.1.0", - "tslib": "^2.6.2" + "string-width": "^5.1.2", + "string-width-cjs": "npm:string-width@^4.2.0", + "strip-ansi": "^7.0.1", + "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", + "wrap-ansi": "^8.1.0", + "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" }, "engines": { - "node": ">=18.0.0" + "node": ">=12" } }, - "node_modules/@aws-sdk/middleware-flexible-checksums/node_modules/@aws-sdk/types": { - "version": "3.893.0", - "license": "Apache-2.0", - "dependencies": { - "@smithy/types": "^4.5.0", - "tslib": "^2.6.2" + "node_modules/@isaacs/cliui/node_modules/ansi-regex": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.2.tgz", + "integrity": "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/@isaacs/cliui/node_modules/ansi-styles": { + "version": "6.2.3", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.3.tgz", + "integrity": "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==", + "dev": true, + "license": "MIT", "engines": { - "node": ">=18.0.0" + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "node_modules/@aws-sdk/middleware-flexible-checksums/node_modules/@aws-sdk/xml-builder": { - "version": "3.894.0", - "license": "Apache-2.0", + "node_modules/@isaacs/cliui/node_modules/string-width": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", + "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", + "dev": true, + "license": "MIT", "dependencies": { - "@smithy/types": "^4.5.0", - "fast-xml-parser": "5.2.5", - "tslib": "^2.6.2" + "eastasianwidth": "^0.2.0", + "emoji-regex": "^9.2.2", + "strip-ansi": "^7.0.1" }, "engines": { - "node": ">=18.0.0" + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/@aws-sdk/middleware-flexible-checksums/node_modules/fast-xml-parser": { - "version": "5.2.5", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/NaturalIntelligence" - } - ], + "node_modules/@isaacs/cliui/node_modules/strip-ansi": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.2.tgz", + "integrity": "sha512-gmBGslpoQJtgnMAvOVqGZpEz9dyoKTCzy2nfz/n8aIFhN/jCE/rCmcxabB6jOOHV+0WNnylOxaxBQPSvcWklhA==", + "dev": true, "license": "MIT", "dependencies": { - "strnum": "^2.1.0" + "ansi-regex": "^6.0.1" }, - "bin": { - "fxparser": "src/cli/cli.js" + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" } }, - "node_modules/@aws-sdk/middleware-flexible-checksums/node_modules/strnum": { - "version": "2.1.1", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/NaturalIntelligence" - } - ], - "license": "MIT" - }, - "node_modules/@aws-sdk/middleware-host-header": { - "version": "3.862.0", - "license": "Apache-2.0", + "node_modules/@isaacs/cliui/node_modules/wrap-ansi": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", + "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", + "dev": true, + "license": "MIT", "dependencies": { - "@aws-sdk/types": "3.862.0", - "@smithy/protocol-http": "^5.1.3", - "@smithy/types": "^4.3.2", - "tslib": "^2.6.2" + "ansi-styles": "^6.1.0", + "string-width": "^5.0.1", + "strip-ansi": "^7.0.1" }, "engines": { - "node": ">=18.0.0" + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" } }, - "node_modules/@aws-sdk/middleware-location-constraint": { - "version": "3.893.0", - "license": "Apache-2.0", + "node_modules/@istanbuljs/load-nyc-config": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz", + "integrity": "sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ==", + "dev": true, + "license": "ISC", "dependencies": { - "@aws-sdk/types": "3.893.0", - "@smithy/types": "^4.5.0", - "tslib": "^2.6.2" + "camelcase": "^5.3.1", + "find-up": "^4.1.0", + "get-package-type": "^0.1.0", + "js-yaml": "^3.13.1", + "resolve-from": "^5.0.0" }, "engines": { - "node": ">=18.0.0" + "node": ">=8" } }, - "node_modules/@aws-sdk/middleware-location-constraint/node_modules/@aws-sdk/types": { - "version": "3.893.0", - "license": "Apache-2.0", + "node_modules/@istanbuljs/load-nyc-config/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, + "license": "MIT", "dependencies": { - "@smithy/types": "^4.5.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" + "sprintf-js": "~1.0.2" } }, - "node_modules/@aws-sdk/middleware-logger": { - "version": "3.862.0", - "license": "Apache-2.0", + "node_modules/@istanbuljs/load-nyc-config/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==", + "dev": true, + "license": "MIT", "dependencies": { - "@aws-sdk/types": "3.862.0", - "@smithy/types": "^4.3.2", - "tslib": "^2.6.2" + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" }, "engines": { - "node": ">=18.0.0" + "node": ">=8" } }, - "node_modules/@aws-sdk/middleware-recursion-detection": { - "version": "3.862.0", - "license": "Apache-2.0", + "node_modules/@istanbuljs/load-nyc-config/node_modules/js-yaml": { + "version": "3.14.2", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.2.tgz", + "integrity": "sha512-PMSmkqxr106Xa156c2M265Z+FTrPl+oxd/rgOQy2tijQeK5TxQ43psO1ZCwhVOSdnn+RzkzlRz/eY4BgJBYVpg==", + "dev": true, + "license": "MIT", "dependencies": { - "@aws-sdk/types": "3.862.0", - "@smithy/protocol-http": "^5.1.3", - "@smithy/types": "^4.3.2", - "tslib": "^2.6.2" + "argparse": "^1.0.7", + "esprima": "^4.0.0" }, - "engines": { - "node": ">=18.0.0" + "bin": { + "js-yaml": "bin/js-yaml.js" } }, - "node_modules/@aws-sdk/middleware-sdk-api-gateway": { - "version": "3.901.0", - "license": "Apache-2.0", + "node_modules/@istanbuljs/load-nyc-config/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==", + "dev": true, + "license": "MIT", "dependencies": { - "@aws-sdk/types": "3.901.0", - "@smithy/protocol-http": "^5.3.0", - "@smithy/types": "^4.6.0", - "tslib": "^2.6.2" + "p-locate": "^4.1.0" }, "engines": { - "node": ">=18.0.0" + "node": ">=8" } }, - "node_modules/@aws-sdk/middleware-sdk-api-gateway/node_modules/@aws-sdk/types": { - "version": "3.901.0", - "license": "Apache-2.0", + "node_modules/@istanbuljs/load-nyc-config/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==", + "dev": true, + "license": "MIT", "dependencies": { - "@smithy/types": "^4.6.0", - "tslib": "^2.6.2" + "p-try": "^2.0.0" }, "engines": { - "node": ">=18.0.0" + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/@aws-sdk/middleware-sdk-s3": { - "version": "3.896.0", - "license": "Apache-2.0", + "node_modules/@istanbuljs/load-nyc-config/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==", + "dev": true, + "license": "MIT", "dependencies": { - "@aws-sdk/core": "3.896.0", - "@aws-sdk/types": "3.893.0", - "@aws-sdk/util-arn-parser": "3.893.0", - "@smithy/core": "^3.12.0", - "@smithy/node-config-provider": "^4.2.2", - "@smithy/protocol-http": "^5.2.1", - "@smithy/signature-v4": "^5.2.1", - "@smithy/smithy-client": "^4.6.4", - "@smithy/types": "^4.5.0", - "@smithy/util-config-provider": "^4.1.0", - "@smithy/util-middleware": "^4.1.1", - "@smithy/util-stream": "^4.3.2", - "@smithy/util-utf8": "^4.1.0", - "tslib": "^2.6.2" + "p-limit": "^2.2.0" }, "engines": { - "node": ">=18.0.0" + "node": ">=8" } }, - "node_modules/@aws-sdk/middleware-sdk-s3/node_modules/@aws-sdk/core": { - "version": "3.896.0", - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/types": "3.893.0", - "@aws-sdk/xml-builder": "3.894.0", - "@smithy/core": "^3.12.0", - "@smithy/node-config-provider": "^4.2.2", - "@smithy/property-provider": "^4.1.1", - "@smithy/protocol-http": "^5.2.1", - "@smithy/signature-v4": "^5.2.1", - "@smithy/smithy-client": "^4.6.4", - "@smithy/types": "^4.5.0", - "@smithy/util-base64": "^4.1.0", - "@smithy/util-middleware": "^4.1.1", - "@smithy/util-utf8": "^4.1.0", - "tslib": "^2.6.2" - }, + "node_modules/@istanbuljs/load-nyc-config/node_modules/resolve-from": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", + "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", + "dev": true, + "license": "MIT", "engines": { - "node": ">=18.0.0" + "node": ">=8" } }, - "node_modules/@aws-sdk/middleware-sdk-s3/node_modules/@aws-sdk/types": { - "version": "3.893.0", - "license": "Apache-2.0", - "dependencies": { - "@smithy/types": "^4.5.0", - "tslib": "^2.6.2" - }, + "node_modules/@istanbuljs/schema": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.3.tgz", + "integrity": "sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==", + "dev": true, + "license": "MIT", "engines": { - "node": ">=18.0.0" + "node": ">=8" } }, - "node_modules/@aws-sdk/middleware-sdk-s3/node_modules/@aws-sdk/xml-builder": { - "version": "3.894.0", - "license": "Apache-2.0", + "node_modules/@jest/console": { + "version": "30.2.0", + "resolved": "https://registry.npmjs.org/@jest/console/-/console-30.2.0.tgz", + "integrity": "sha512-+O1ifRjkvYIkBqASKWgLxrpEhQAAE7hY77ALLUufSk5717KfOShg6IbqLmdsLMPdUiFvA2kTs0R7YZy+l0IzZQ==", + "dev": true, + "license": "MIT", "dependencies": { - "@smithy/types": "^4.5.0", - "fast-xml-parser": "5.2.5", - "tslib": "^2.6.2" + "@jest/types": "30.2.0", + "@types/node": "*", + "chalk": "^4.1.2", + "jest-message-util": "30.2.0", + "jest-util": "30.2.0", + "slash": "^3.0.0" }, "engines": { - "node": ">=18.0.0" + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" } }, - "node_modules/@aws-sdk/middleware-sdk-s3/node_modules/fast-xml-parser": { - "version": "5.2.5", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/NaturalIntelligence" - } - ], + "node_modules/@jest/core": { + "version": "30.2.0", + "resolved": "https://registry.npmjs.org/@jest/core/-/core-30.2.0.tgz", + "integrity": "sha512-03W6IhuhjqTlpzh/ojut/pDB2LPRygyWX8ExpgHtQA8H/3K7+1vKmcINx5UzeOX1se6YEsBsOHQ1CRzf3fOwTQ==", + "dev": true, "license": "MIT", "dependencies": { - "strnum": "^2.1.0" + "@jest/console": "30.2.0", + "@jest/pattern": "30.0.1", + "@jest/reporters": "30.2.0", + "@jest/test-result": "30.2.0", + "@jest/transform": "30.2.0", + "@jest/types": "30.2.0", + "@types/node": "*", + "ansi-escapes": "^4.3.2", + "chalk": "^4.1.2", + "ci-info": "^4.2.0", + "exit-x": "^0.2.2", + "graceful-fs": "^4.2.11", + "jest-changed-files": "30.2.0", + "jest-config": "30.2.0", + "jest-haste-map": "30.2.0", + "jest-message-util": "30.2.0", + "jest-regex-util": "30.0.1", + "jest-resolve": "30.2.0", + "jest-resolve-dependencies": "30.2.0", + "jest-runner": "30.2.0", + "jest-runtime": "30.2.0", + "jest-snapshot": "30.2.0", + "jest-util": "30.2.0", + "jest-validate": "30.2.0", + "jest-watcher": "30.2.0", + "micromatch": "^4.0.8", + "pretty-format": "30.2.0", + "slash": "^3.0.0" }, - "bin": { - "fxparser": "src/cli/cli.js" + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + }, + "peerDependencies": { + "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" + }, + "peerDependenciesMeta": { + "node-notifier": { + "optional": true + } } }, - "node_modules/@aws-sdk/middleware-sdk-s3/node_modules/strnum": { - "version": "2.1.1", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/NaturalIntelligence" - } - ], - "license": "MIT" + "node_modules/@jest/diff-sequences": { + "version": "30.0.1", + "resolved": "https://registry.npmjs.org/@jest/diff-sequences/-/diff-sequences-30.0.1.tgz", + "integrity": "sha512-n5H8QLDJ47QqbCNn5SuFjCRDrOLEZ0h8vAHCK5RL9Ls7Xa8AQLa/YxAc9UjFqoEDM48muwtBGjtMY5cr0PLDCw==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } }, - "node_modules/@aws-sdk/middleware-ssec": { - "version": "3.893.0", - "license": "Apache-2.0", + "node_modules/@jest/environment": { + "version": "30.2.0", + "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-30.2.0.tgz", + "integrity": "sha512-/QPTL7OBJQ5ac09UDRa3EQes4gt1FTEG/8jZ/4v5IVzx+Cv7dLxlVIvfvSVRiiX2drWyXeBjkMSR8hvOWSog5g==", + "dev": true, + "license": "MIT", "dependencies": { - "@aws-sdk/types": "3.893.0", - "@smithy/types": "^4.5.0", - "tslib": "^2.6.2" + "@jest/fake-timers": "30.2.0", + "@jest/types": "30.2.0", + "@types/node": "*", + "jest-mock": "30.2.0" }, "engines": { - "node": ">=18.0.0" + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" } }, - "node_modules/@aws-sdk/middleware-ssec/node_modules/@aws-sdk/types": { - "version": "3.893.0", - "license": "Apache-2.0", + "node_modules/@jest/environment-jsdom-abstract": { + "version": "30.2.0", + "resolved": "https://registry.npmjs.org/@jest/environment-jsdom-abstract/-/environment-jsdom-abstract-30.2.0.tgz", + "integrity": "sha512-kazxw2L9IPuZpQ0mEt9lu9Z98SqR74xcagANmMBU16X0lS23yPc0+S6hGLUz8kVRlomZEs/5S/Zlpqwf5yu6OQ==", + "dev": true, + "license": "MIT", "dependencies": { - "@smithy/types": "^4.5.0", - "tslib": "^2.6.2" + "@jest/environment": "30.2.0", + "@jest/fake-timers": "30.2.0", + "@jest/types": "30.2.0", + "@types/jsdom": "^21.1.7", + "@types/node": "*", + "jest-mock": "30.2.0", + "jest-util": "30.2.0" }, "engines": { - "node": ">=18.0.0" + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + }, + "peerDependencies": { + "canvas": "^3.0.0", + "jsdom": "*" + }, + "peerDependenciesMeta": { + "canvas": { + "optional": true + } } }, - "node_modules/@aws-sdk/middleware-user-agent": { - "version": "3.864.0", - "license": "Apache-2.0", + "node_modules/@jest/expect": { + "version": "30.2.0", + "resolved": "https://registry.npmjs.org/@jest/expect/-/expect-30.2.0.tgz", + "integrity": "sha512-V9yxQK5erfzx99Sf+7LbhBwNWEZ9eZay8qQ9+JSC0TrMR1pMDHLMY+BnVPacWU6Jamrh252/IKo4F1Xn/zfiqA==", + "dev": true, + "license": "MIT", "dependencies": { - "@aws-sdk/core": "3.864.0", - "@aws-sdk/types": "3.862.0", - "@aws-sdk/util-endpoints": "3.862.0", - "@smithy/core": "^3.8.0", - "@smithy/protocol-http": "^5.1.3", - "@smithy/types": "^4.3.2", - "tslib": "^2.6.2" + "expect": "30.2.0", + "jest-snapshot": "30.2.0" }, "engines": { - "node": ">=18.0.0" + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" } }, - "node_modules/@aws-sdk/nested-clients": { - "version": "3.864.0", - "license": "Apache-2.0", + "node_modules/@jest/expect-utils": { + "version": "30.2.0", + "resolved": "https://registry.npmjs.org/@jest/expect-utils/-/expect-utils-30.2.0.tgz", + "integrity": "sha512-1JnRfhqpD8HGpOmQp180Fo9Zt69zNtC+9lR+kT7NVL05tNXIi+QC8Csz7lfidMoVLPD3FnOtcmp0CEFnxExGEA==", + "dev": true, + "license": "MIT", "dependencies": { - "@aws-crypto/sha256-browser": "5.2.0", - "@aws-crypto/sha256-js": "5.2.0", - "@aws-sdk/core": "3.864.0", - "@aws-sdk/middleware-host-header": "3.862.0", - "@aws-sdk/middleware-logger": "3.862.0", - "@aws-sdk/middleware-recursion-detection": "3.862.0", - "@aws-sdk/middleware-user-agent": "3.864.0", - "@aws-sdk/region-config-resolver": "3.862.0", - "@aws-sdk/types": "3.862.0", - "@aws-sdk/util-endpoints": "3.862.0", - "@aws-sdk/util-user-agent-browser": "3.862.0", - "@aws-sdk/util-user-agent-node": "3.864.0", - "@smithy/config-resolver": "^4.1.5", - "@smithy/core": "^3.8.0", - "@smithy/fetch-http-handler": "^5.1.1", - "@smithy/hash-node": "^4.0.5", - "@smithy/invalid-dependency": "^4.0.5", - "@smithy/middleware-content-length": "^4.0.5", - "@smithy/middleware-endpoint": "^4.1.18", - "@smithy/middleware-retry": "^4.1.19", - "@smithy/middleware-serde": "^4.0.9", - "@smithy/middleware-stack": "^4.0.5", - "@smithy/node-config-provider": "^4.1.4", - "@smithy/node-http-handler": "^4.1.1", - "@smithy/protocol-http": "^5.1.3", - "@smithy/smithy-client": "^4.4.10", - "@smithy/types": "^4.3.2", - "@smithy/url-parser": "^4.0.5", - "@smithy/util-base64": "^4.0.0", - "@smithy/util-body-length-browser": "^4.0.0", - "@smithy/util-body-length-node": "^4.0.0", - "@smithy/util-defaults-mode-browser": "^4.0.26", - "@smithy/util-defaults-mode-node": "^4.0.26", - "@smithy/util-endpoints": "^3.0.7", - "@smithy/util-middleware": "^4.0.5", - "@smithy/util-retry": "^4.0.7", - "@smithy/util-utf8": "^4.0.0", - "tslib": "^2.6.2" + "@jest/get-type": "30.1.0" }, "engines": { - "node": ">=18.0.0" + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" } }, - "node_modules/@aws-sdk/region-config-resolver": { - "version": "3.862.0", - "license": "Apache-2.0", + "node_modules/@jest/fake-timers": { + "version": "30.2.0", + "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-30.2.0.tgz", + "integrity": "sha512-HI3tRLjRxAbBy0VO8dqqm7Hb2mIa8d5bg/NJkyQcOk7V118ObQML8RC5luTF/Zsg4474a+gDvhce7eTnP4GhYw==", + "dev": true, + "license": "MIT", "dependencies": { - "@aws-sdk/types": "3.862.0", - "@smithy/node-config-provider": "^4.1.4", - "@smithy/types": "^4.3.2", - "@smithy/util-config-provider": "^4.0.0", - "@smithy/util-middleware": "^4.0.5", - "tslib": "^2.6.2" + "@jest/types": "30.2.0", + "@sinonjs/fake-timers": "^13.0.0", + "@types/node": "*", + "jest-message-util": "30.2.0", + "jest-mock": "30.2.0", + "jest-util": "30.2.0" }, "engines": { - "node": ">=18.0.0" + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" } }, - "node_modules/@aws-sdk/s3-request-presigner": { - "version": "3.901.0", + "node_modules/@jest/get-type": { + "version": "30.1.0", + "resolved": "https://registry.npmjs.org/@jest/get-type/-/get-type-30.1.0.tgz", + "integrity": "sha512-eMbZE2hUnx1WV0pmURZY9XoXPkUYjpc55mb0CrhtdWLtzMQPFvu/rZkTLZFTsdaVQa+Tr4eWAteqcUzoawq/uA==", "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/signature-v4-multi-region": "3.901.0", - "@aws-sdk/types": "3.901.0", - "@aws-sdk/util-format-url": "3.901.0", - "@smithy/middleware-endpoint": "^4.3.0", - "@smithy/protocol-http": "^5.3.0", - "@smithy/smithy-client": "^4.7.0", - "@smithy/types": "^4.6.0", - "tslib": "^2.6.2" - }, + "license": "MIT", "engines": { - "node": ">=18.0.0" + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" } }, - "node_modules/@aws-sdk/s3-request-presigner/node_modules/@aws-sdk/core": { - "version": "3.901.0", + "node_modules/@jest/globals": { + "version": "30.2.0", + "resolved": "https://registry.npmjs.org/@jest/globals/-/globals-30.2.0.tgz", + "integrity": "sha512-b63wmnKPaK+6ZZfpYhz9K61oybvbI1aMcIs80++JI1O1rR1vaxHUCNqo3ITu6NU0d4V34yZFoHMn/uoKr/Rwfw==", "dev": true, - "license": "Apache-2.0", + "license": "MIT", "dependencies": { - "@aws-sdk/types": "3.901.0", - "@aws-sdk/xml-builder": "3.901.0", - "@smithy/core": "^3.14.0", - "@smithy/node-config-provider": "^4.3.0", - "@smithy/property-provider": "^4.2.0", - "@smithy/protocol-http": "^5.3.0", - "@smithy/signature-v4": "^5.3.0", - "@smithy/smithy-client": "^4.7.0", - "@smithy/types": "^4.6.0", - "@smithy/util-base64": "^4.2.0", - "@smithy/util-middleware": "^4.2.0", - "@smithy/util-utf8": "^4.2.0", - "tslib": "^2.6.2" + "@jest/environment": "30.2.0", + "@jest/expect": "30.2.0", + "@jest/types": "30.2.0", + "jest-mock": "30.2.0" }, "engines": { - "node": ">=18.0.0" + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" } }, - "node_modules/@aws-sdk/s3-request-presigner/node_modules/@aws-sdk/middleware-sdk-s3": { - "version": "3.901.0", + "node_modules/@jest/pattern": { + "version": "30.0.1", + "resolved": "https://registry.npmjs.org/@jest/pattern/-/pattern-30.0.1.tgz", + "integrity": "sha512-gWp7NfQW27LaBQz3TITS8L7ZCQ0TLvtmI//4OwlQRx4rnWxcPNIYjxZpDcN4+UlGxgm3jS5QPz8IPTCkb59wZA==", "dev": true, - "license": "Apache-2.0", + "license": "MIT", "dependencies": { - "@aws-sdk/core": "3.901.0", - "@aws-sdk/types": "3.901.0", - "@aws-sdk/util-arn-parser": "3.893.0", - "@smithy/core": "^3.14.0", - "@smithy/node-config-provider": "^4.3.0", - "@smithy/protocol-http": "^5.3.0", - "@smithy/signature-v4": "^5.3.0", - "@smithy/smithy-client": "^4.7.0", - "@smithy/types": "^4.6.0", - "@smithy/util-config-provider": "^4.2.0", - "@smithy/util-middleware": "^4.2.0", - "@smithy/util-stream": "^4.4.0", - "@smithy/util-utf8": "^4.2.0", - "tslib": "^2.6.2" + "@types/node": "*", + "jest-regex-util": "30.0.1" }, "engines": { - "node": ">=18.0.0" + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" } }, - "node_modules/@aws-sdk/s3-request-presigner/node_modules/@aws-sdk/signature-v4-multi-region": { - "version": "3.901.0", + "node_modules/@jest/reporters": { + "version": "30.2.0", + "resolved": "https://registry.npmjs.org/@jest/reporters/-/reporters-30.2.0.tgz", + "integrity": "sha512-DRyW6baWPqKMa9CzeiBjHwjd8XeAyco2Vt8XbcLFjiwCOEKOvy82GJ8QQnJE9ofsxCMPjH4MfH8fCWIHHDKpAQ==", "dev": true, - "license": "Apache-2.0", + "license": "MIT", "dependencies": { - "@aws-sdk/middleware-sdk-s3": "3.901.0", - "@aws-sdk/types": "3.901.0", - "@smithy/protocol-http": "^5.3.0", - "@smithy/signature-v4": "^5.3.0", - "@smithy/types": "^4.6.0", - "tslib": "^2.6.2" + "@bcoe/v8-coverage": "^0.2.3", + "@jest/console": "30.2.0", + "@jest/test-result": "30.2.0", + "@jest/transform": "30.2.0", + "@jest/types": "30.2.0", + "@jridgewell/trace-mapping": "^0.3.25", + "@types/node": "*", + "chalk": "^4.1.2", + "collect-v8-coverage": "^1.0.2", + "exit-x": "^0.2.2", + "glob": "^10.3.10", + "graceful-fs": "^4.2.11", + "istanbul-lib-coverage": "^3.0.0", + "istanbul-lib-instrument": "^6.0.0", + "istanbul-lib-report": "^3.0.0", + "istanbul-lib-source-maps": "^5.0.0", + "istanbul-reports": "^3.1.3", + "jest-message-util": "30.2.0", + "jest-util": "30.2.0", + "jest-worker": "30.2.0", + "slash": "^3.0.0", + "string-length": "^4.0.2", + "v8-to-istanbul": "^9.0.1" }, "engines": { - "node": ">=18.0.0" + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + }, + "peerDependencies": { + "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" + }, + "peerDependenciesMeta": { + "node-notifier": { + "optional": true + } } }, - "node_modules/@aws-sdk/s3-request-presigner/node_modules/@aws-sdk/types": { - "version": "3.901.0", + "node_modules/@jest/reporters/node_modules/glob": { + "version": "10.5.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.5.0.tgz", + "integrity": "sha512-DfXN8DfhJ7NH3Oe7cFmu3NCu1wKbkReJ8TorzSAFbSKrlNaQSKfIzqYqVY8zlbs2NLBbWpRiU52GX2PbaBVNkg==", "dev": true, - "license": "Apache-2.0", + "license": "ISC", "dependencies": { - "@smithy/types": "^4.6.0", - "tslib": "^2.6.2" + "foreground-child": "^3.1.0", + "jackspeak": "^3.1.2", + "minimatch": "^9.0.4", + "minipass": "^7.1.2", + "package-json-from-dist": "^1.0.0", + "path-scurry": "^1.11.1" }, - "engines": { - "node": ">=18.0.0" + "bin": { + "glob": "dist/esm/bin.mjs" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/@aws-sdk/s3-request-presigner/node_modules/@aws-sdk/xml-builder": { - "version": "3.901.0", + "node_modules/@jest/reporters/node_modules/lru-cache": { + "version": "10.4.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", + "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", "dev": true, - "license": "Apache-2.0", + "license": "ISC" + }, + "node_modules/@jest/reporters/node_modules/path-scurry": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz", + "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==", + "dev": true, + "license": "BlueOak-1.0.0", "dependencies": { - "@smithy/types": "^4.6.0", - "fast-xml-parser": "5.2.5", - "tslib": "^2.6.2" + "lru-cache": "^10.2.0", + "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" }, "engines": { - "node": ">=18.0.0" + "node": ">=16 || 14 >=14.18" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/@aws-sdk/s3-request-presigner/node_modules/fast-xml-parser": { - "version": "5.2.5", + "node_modules/@jest/schemas": { + "version": "30.0.5", + "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-30.0.5.tgz", + "integrity": "sha512-DmdYgtezMkh3cpU8/1uyXakv3tJRcmcXxBOcO0tbaozPwpmh4YMsnWrQm9ZmZMfa5ocbxzbFk6O4bDPEc/iAnA==", "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/NaturalIntelligence" - } - ], "license": "MIT", "dependencies": { - "strnum": "^2.1.0" + "@sinclair/typebox": "^0.34.0" }, - "bin": { - "fxparser": "src/cli/cli.js" + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" } }, - "node_modules/@aws-sdk/s3-request-presigner/node_modules/strnum": { - "version": "2.1.1", + "node_modules/@jest/snapshot-utils": { + "version": "30.2.0", + "resolved": "https://registry.npmjs.org/@jest/snapshot-utils/-/snapshot-utils-30.2.0.tgz", + "integrity": "sha512-0aVxM3RH6DaiLcjj/b0KrIBZhSX1373Xci4l3cW5xiUWPctZ59zQ7jj4rqcJQ/Z8JuN/4wX3FpJSa3RssVvCug==", "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/NaturalIntelligence" - } - ], - "license": "MIT" - }, - "node_modules/@aws-sdk/signature-v4-multi-region": { - "version": "3.896.0", - "license": "Apache-2.0", + "license": "MIT", "dependencies": { - "@aws-sdk/middleware-sdk-s3": "3.896.0", - "@aws-sdk/types": "3.893.0", - "@smithy/protocol-http": "^5.2.1", - "@smithy/signature-v4": "^5.2.1", - "@smithy/types": "^4.5.0", - "tslib": "^2.6.2" + "@jest/types": "30.2.0", + "chalk": "^4.1.2", + "graceful-fs": "^4.2.11", + "natural-compare": "^1.4.0" }, "engines": { - "node": ">=18.0.0" + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" } }, - "node_modules/@aws-sdk/signature-v4-multi-region/node_modules/@aws-sdk/types": { - "version": "3.893.0", - "license": "Apache-2.0", + "node_modules/@jest/source-map": { + "version": "30.0.1", + "resolved": "https://registry.npmjs.org/@jest/source-map/-/source-map-30.0.1.tgz", + "integrity": "sha512-MIRWMUUR3sdbP36oyNyhbThLHyJ2eEDClPCiHVbrYAe5g3CHRArIVpBw7cdSB5fr+ofSfIb2Tnsw8iEHL0PYQg==", + "dev": true, + "license": "MIT", "dependencies": { - "@smithy/types": "^4.5.0", - "tslib": "^2.6.2" + "@jridgewell/trace-mapping": "^0.3.25", + "callsites": "^3.1.0", + "graceful-fs": "^4.2.11" }, "engines": { - "node": ">=18.0.0" + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" } }, - "node_modules/@aws-sdk/token-providers": { - "version": "3.864.0", - "license": "Apache-2.0", + "node_modules/@jest/test-result": { + "version": "30.2.0", + "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-30.2.0.tgz", + "integrity": "sha512-RF+Z+0CCHkARz5HT9mcQCBulb1wgCP3FBvl9VFokMX27acKphwyQsNuWH3c+ojd1LeWBLoTYoxF0zm6S/66mjg==", + "dev": true, + "license": "MIT", "dependencies": { - "@aws-sdk/core": "3.864.0", - "@aws-sdk/nested-clients": "3.864.0", - "@aws-sdk/types": "3.862.0", - "@smithy/property-provider": "^4.0.5", - "@smithy/shared-ini-file-loader": "^4.0.5", - "@smithy/types": "^4.3.2", - "tslib": "^2.6.2" + "@jest/console": "30.2.0", + "@jest/types": "30.2.0", + "@types/istanbul-lib-coverage": "^2.0.6", + "collect-v8-coverage": "^1.0.2" }, "engines": { - "node": ">=18.0.0" + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" } }, - "node_modules/@aws-sdk/types": { - "version": "3.862.0", - "license": "Apache-2.0", + "node_modules/@jest/test-sequencer": { + "version": "30.2.0", + "resolved": "https://registry.npmjs.org/@jest/test-sequencer/-/test-sequencer-30.2.0.tgz", + "integrity": "sha512-wXKgU/lk8fKXMu/l5Hog1R61bL4q5GCdT6OJvdAFz1P+QrpoFuLU68eoKuVc4RbrTtNnTL5FByhWdLgOPSph+Q==", + "dev": true, + "license": "MIT", "dependencies": { - "@smithy/types": "^4.3.2", - "tslib": "^2.6.2" + "@jest/test-result": "30.2.0", + "graceful-fs": "^4.2.11", + "jest-haste-map": "30.2.0", + "slash": "^3.0.0" }, "engines": { - "node": ">=18.0.0" + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" } }, - "node_modules/@aws-sdk/util-arn-parser": { - "version": "3.893.0", - "license": "Apache-2.0", + "node_modules/@jest/transform": { + "version": "30.2.0", + "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-30.2.0.tgz", + "integrity": "sha512-XsauDV82o5qXbhalKxD7p4TZYYdwcaEXC77PPD2HixEFF+6YGppjrAAQurTl2ECWcEomHBMMNS9AH3kcCFx8jA==", + "dev": true, + "license": "MIT", "dependencies": { - "tslib": "^2.6.2" + "@babel/core": "^7.27.4", + "@jest/types": "30.2.0", + "@jridgewell/trace-mapping": "^0.3.25", + "babel-plugin-istanbul": "^7.0.1", + "chalk": "^4.1.2", + "convert-source-map": "^2.0.0", + "fast-json-stable-stringify": "^2.1.0", + "graceful-fs": "^4.2.11", + "jest-haste-map": "30.2.0", + "jest-regex-util": "30.0.1", + "jest-util": "30.2.0", + "micromatch": "^4.0.8", + "pirates": "^4.0.7", + "slash": "^3.0.0", + "write-file-atomic": "^5.0.1" }, "engines": { - "node": ">=18.0.0" + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" } }, - "node_modules/@aws-sdk/util-dynamodb": { - "version": "3.864.0", - "license": "Apache-2.0", + "node_modules/@jest/types": { + "version": "30.2.0", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-30.2.0.tgz", + "integrity": "sha512-H9xg1/sfVvyfU7o3zMfBEjQ1gcsdeTMgqHoYdN79tuLqfTtuu7WckRA1R5whDwOzxaZAeMKTYWqP+WCAi0CHsg==", + "dev": true, + "license": "MIT", "dependencies": { - "tslib": "^2.6.2" + "@jest/pattern": "30.0.1", + "@jest/schemas": "30.0.5", + "@types/istanbul-lib-coverage": "^2.0.6", + "@types/istanbul-reports": "^3.0.4", + "@types/node": "*", + "@types/yargs": "^17.0.33", + "chalk": "^4.1.2" }, "engines": { - "node": ">=18.0.0" - }, - "peerDependencies": { - "@aws-sdk/client-dynamodb": "^3.864.0" + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" } }, - "node_modules/@aws-sdk/util-endpoints": { - "version": "3.862.0", - "license": "Apache-2.0", + "node_modules/@jridgewell/gen-mapping": { + "version": "0.3.13", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", + "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==", + "dev": true, + "license": "MIT", "dependencies": { - "@aws-sdk/types": "3.862.0", - "@smithy/types": "^4.3.2", - "@smithy/url-parser": "^4.0.5", - "@smithy/util-endpoints": "^3.0.7", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" + "@jridgewell/sourcemap-codec": "^1.5.0", + "@jridgewell/trace-mapping": "^0.3.24" } }, - "node_modules/@aws-sdk/util-format-url": { - "version": "3.901.0", + "node_modules/@jridgewell/remapping": { + "version": "2.3.5", + "resolved": "https://registry.npmjs.org/@jridgewell/remapping/-/remapping-2.3.5.tgz", + "integrity": "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==", "dev": true, - "license": "Apache-2.0", + "license": "MIT", "dependencies": { - "@aws-sdk/types": "3.901.0", - "@smithy/querystring-builder": "^4.2.0", - "@smithy/types": "^4.6.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.24" } }, - "node_modules/@aws-sdk/util-format-url/node_modules/@aws-sdk/types": { - "version": "3.901.0", + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@smithy/types": "^4.6.0", - "tslib": "^2.6.2" - }, + "license": "MIT", "engines": { - "node": ">=18.0.0" + "node": ">=6.0.0" } }, - "node_modules/@aws-sdk/util-locate-window": { - "version": "3.804.0", - "license": "Apache-2.0", + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.5.5", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", + "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", + "dev": true, + "license": "MIT" + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.31", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz", + "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==", + "dev": true, + "license": "MIT", "dependencies": { - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" } }, - "node_modules/@aws-sdk/util-user-agent-browser": { - "version": "3.862.0", - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/types": "3.862.0", - "@smithy/types": "^4.3.2", - "bowser": "^2.11.0", - "tslib": "^2.6.2" + "node_modules/@js-sdsl/ordered-map": { + "version": "4.4.2", + "resolved": "https://registry.npmjs.org/@js-sdsl/ordered-map/-/ordered-map-4.4.2.tgz", + "integrity": "sha512-iUKgm52T8HOE/makSxjqoWhe95ZJA1/G1sYsGev2JDKUSS14KAgg1LHb+Ba+IPow0xflbnSkOsZcO08C7w1gYw==", + "dev": true, + "license": "MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/js-sdsl" } }, - "node_modules/@aws-sdk/util-user-agent-node": { - "version": "3.864.0", - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/middleware-user-agent": "3.864.0", - "@aws-sdk/types": "3.862.0", - "@smithy/node-config-provider": "^4.1.4", - "@smithy/types": "^4.3.2", - "tslib": "^2.6.2" - }, + "node_modules/@jsdevtools/ono": { + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/@jsdevtools/ono/-/ono-7.1.3.tgz", + "integrity": "sha512-4JQNk+3mVzK3xh2rqd6RB4J46qUR19azEHBneZyTZM+c456qOrbbM/5xcR8huNCCcbVt7+UmizG6GuUvPvKUYg==", + "license": "MIT" + }, + "node_modules/@jsep-plugin/assignment": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@jsep-plugin/assignment/-/assignment-1.3.0.tgz", + "integrity": "sha512-VVgV+CXrhbMI3aSusQyclHkenWSAm95WaiKrMxRFam3JSUiIaQjoMIw2sEs/OX4XifnqeQUN4DYbJjlA8EfktQ==", + "dev": true, + "license": "MIT", "engines": { - "node": ">=18.0.0" + "node": ">= 10.16.0" }, "peerDependencies": { - "aws-crt": ">=1.0.0" - }, - "peerDependenciesMeta": { - "aws-crt": { - "optional": true - } - } - }, - "node_modules/@aws-sdk/xml-builder": { - "version": "3.862.0", - "license": "Apache-2.0", - "dependencies": { - "@smithy/types": "^4.3.2", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" + "jsep": "^0.4.0||^1.0.0" } }, - "node_modules/@aws/lambda-invoke-store": { - "version": "0.0.1", - "license": "Apache-2.0", + "node_modules/@jsep-plugin/regex": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@jsep-plugin/regex/-/regex-1.0.4.tgz", + "integrity": "sha512-q7qL4Mgjs1vByCaTnDFcBnV9HS7GVPJX5vyVoCgZHNSC9rjwIlmbXG5sUuorR5ndfHAIlJ8pVStxvjXHbNvtUg==", + "dev": true, + "license": "MIT", "engines": { - "node": ">=18.0.0" + "node": ">= 10.16.0" + }, + "peerDependencies": { + "jsep": "^0.4.0||^1.0.0" } }, - "node_modules/@babel/code-frame": { - "version": "7.27.1", + "node_modules/@jsep-plugin/ternary": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/@jsep-plugin/ternary/-/ternary-1.1.4.tgz", + "integrity": "sha512-ck5wiqIbqdMX6WRQztBL7ASDty9YLgJ3sSAK5ZpBzXeySvFGCzIvM6UiAI4hTZ22fEcYQVV/zhUbNscggW+Ukg==", "dev": true, "license": "MIT", - "dependencies": { - "@babel/helper-validator-identifier": "^7.27.1", - "js-tokens": "^4.0.0", - "picocolors": "^1.1.1" - }, "engines": { - "node": ">=6.9.0" + "node": ">= 10.16.0" + }, + "peerDependencies": { + "jsep": "^0.4.0||^1.0.0" } }, - "node_modules/@babel/compat-data": { - "version": "7.28.0", + "node_modules/@lukeed/csprng": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@lukeed/csprng/-/csprng-1.1.0.tgz", + "integrity": "sha512-Z7C/xXCiGWsg0KuKsHTKJxbWhpI3Vs5GwLfOean7MGyVFGqdRgBbAjOCh6u4bbjPc/8MJ2pZmK/0DLdCbivLDA==", "dev": true, "license": "MIT", "engines": { - "node": ">=6.9.0" + "node": ">=8" } }, - "node_modules/@babel/core": { - "version": "7.28.3", + "node_modules/@napi-rs/wasm-runtime": { + "version": "0.2.12", + "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-0.2.12.tgz", + "integrity": "sha512-ZVWUcfwY4E/yPitQJl481FjFo3K22D6qF0DuFH6Y/nbnE11GY5uguDxZMGXPQ8WQ0128MXQD7TnfHyK4oWoIJQ==", "dev": true, "license": "MIT", + "optional": true, "dependencies": { - "@ampproject/remapping": "^2.2.0", - "@babel/code-frame": "^7.27.1", - "@babel/generator": "^7.28.3", - "@babel/helper-compilation-targets": "^7.27.2", - "@babel/helper-module-transforms": "^7.28.3", - "@babel/helpers": "^7.28.3", - "@babel/parser": "^7.28.3", - "@babel/template": "^7.27.2", - "@babel/traverse": "^7.28.3", - "@babel/types": "^7.28.2", - "convert-source-map": "^2.0.0", - "debug": "^4.1.0", - "gensync": "^1.0.0-beta.2", - "json5": "^2.2.3", - "semver": "^6.3.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/babel" + "@emnapi/core": "^1.4.3", + "@emnapi/runtime": "^1.4.3", + "@tybys/wasm-util": "^0.10.0" } }, - "node_modules/@babel/core/node_modules/semver": { - "version": "6.3.1", + "node_modules/@nestjs/axios": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@nestjs/axios/-/axios-4.0.1.tgz", + "integrity": "sha512-68pFJgu+/AZbWkGu65Z3r55bTsCPlgyKaV4BSG8yUAD72q1PPuyVRgUwFv6BxdnibTUHlyxm06FmYWNC+bjN7A==", "dev": true, - "license": "ISC", - "bin": { - "semver": "bin/semver.js" + "license": "MIT", + "peerDependencies": { + "@nestjs/common": "^10.0.0 || ^11.0.0", + "axios": "^1.3.1", + "rxjs": "^7.0.0" } }, - "node_modules/@babel/generator": { - "version": "7.28.3", + "node_modules/@nestjs/common": { + "version": "11.1.9", + "resolved": "https://registry.npmjs.org/@nestjs/common/-/common-11.1.9.tgz", + "integrity": "sha512-zDntUTReRbAThIfSp3dQZ9kKqI+LjgLp5YZN5c1bgNRDuoeLySAoZg46Bg1a+uV8TMgIRziHocglKGNzr6l+bQ==", "dev": true, "license": "MIT", "dependencies": { - "@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" + "file-type": "21.1.0", + "iterare": "1.2.1", + "load-esm": "1.0.3", + "tslib": "2.8.1", + "uid": "2.0.2" }, - "engines": { - "node": ">=6.9.0" + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/nest" + }, + "peerDependencies": { + "class-transformer": ">=0.4.1", + "class-validator": ">=0.13.2", + "reflect-metadata": "^0.1.12 || ^0.2.0", + "rxjs": "^7.1.0" + }, + "peerDependenciesMeta": { + "class-transformer": { + "optional": true + }, + "class-validator": { + "optional": true + } } }, - "node_modules/@babel/helper-compilation-targets": { - "version": "7.27.2", + "node_modules/@nestjs/core": { + "version": "11.1.9", + "resolved": "https://registry.npmjs.org/@nestjs/core/-/core-11.1.9.tgz", + "integrity": "sha512-a00B0BM4X+9z+t3UxJqIZlemIwCQdYoPKrMcM+ky4z3pkqqG1eTWexjs+YXpGObnLnjtMPVKWlcZHp3adDYvUw==", "dev": true, + "hasInstallScript": true, "license": "MIT", "dependencies": { - "@babel/compat-data": "^7.27.2", - "@babel/helper-validator-option": "^7.27.1", - "browserslist": "^4.24.0", - "lru-cache": "^5.1.1", - "semver": "^6.3.1" + "@nuxt/opencollective": "0.4.1", + "fast-safe-stringify": "2.1.1", + "iterare": "1.2.1", + "path-to-regexp": "8.3.0", + "tslib": "2.8.1", + "uid": "2.0.2" }, "engines": { - "node": ">=6.9.0" + "node": ">= 20" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/nest" + }, + "peerDependencies": { + "@nestjs/common": "^11.0.0", + "@nestjs/microservices": "^11.0.0", + "@nestjs/platform-express": "^11.0.0", + "@nestjs/websockets": "^11.0.0", + "reflect-metadata": "^0.1.12 || ^0.2.0", + "rxjs": "^7.1.0" + }, + "peerDependenciesMeta": { + "@nestjs/microservices": { + "optional": true + }, + "@nestjs/platform-express": { + "optional": true + }, + "@nestjs/websockets": { + "optional": true + } } }, - "node_modules/@babel/helper-compilation-targets/node_modules/semver": { - "version": "6.3.1", + "node_modules/@next/eslint-plugin-next": { + "version": "15.5.9", + "resolved": "https://registry.npmjs.org/@next/eslint-plugin-next/-/eslint-plugin-next-15.5.9.tgz", + "integrity": "sha512-kUzXx0iFiXw27cQAViE1yKWnz/nF8JzRmwgMRTMh8qMY90crNsdXJRh2e+R0vBpFR3kk1yvAR7wev7+fCCb79Q==", "dev": true, - "license": "ISC", - "bin": { - "semver": "bin/semver.js" + "license": "MIT", + "dependencies": { + "fast-glob": "3.3.1" } }, - "node_modules/@babel/helper-globals": { - "version": "7.28.0", - "dev": true, + "node_modules/@nhsdigital/nhs-notify-event-schemas-letter-rendering": { + "version": "2.0.1", + "resolved": "https://npm.pkg.github.com/download/@nhsdigital/nhs-notify-event-schemas-letter-rendering/2.0.1/23a5011fb0addd3da400f798bb1e4340440d62a5", + "integrity": "sha512-U2AWQEBcTDSxA3RX29fdmwjaOPQvwrCQP5rVCgLgtlPGes4Wl695VLw7tDxgpyLY1p9ct3HHrx3Wc6k/QGeW7g==", "license": "MIT", - "engines": { - "node": ">=6.9.0" + "dependencies": { + "zod": "^4.0.17" } }, - "node_modules/@babel/helper-module-imports": { - "version": "7.27.1", + "node_modules/@nhsdigital/nhs-notify-event-schemas-supplier-api": { + "resolved": "internal/events", + "link": true + }, + "node_modules/@nhsdigital/notify-supplier-api-consumer-contracts": { + "resolved": "pact-contracts", + "link": true + }, + "node_modules/@noble/hashes": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.8.0.tgz", + "integrity": "sha512-jCs9ldd7NwzpgXDIf6P3+NrHh9/sD6CQdxHyjQI+h/6rDNo88ypBxxz45UDuZHz9r3tNz7N/VInSVoVdtXEI4A==", "dev": true, "license": "MIT", - "dependencies": { - "@babel/traverse": "^7.27.1", - "@babel/types": "^7.27.1" - }, "engines": { - "node": ">=6.9.0" + "node": "^14.21.3 || >=16" + }, + "funding": { + "url": "https://paulmillr.com/funding/" } }, - "node_modules/@babel/helper-module-transforms": { - "version": "7.28.3", + "node_modules/@nodelib/fs.scandir": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-module-imports": "^7.27.1", - "@babel/helper-validator-identifier": "^7.27.1", - "@babel/traverse": "^7.28.3" + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" }, "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" + "node": ">= 8" } }, - "node_modules/@babel/helper-plugin-utils": { - "version": "7.27.1", + "node_modules/@nodelib/fs.stat": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", "dev": true, "license": "MIT", "engines": { - "node": ">=6.9.0" + "node": ">= 8" } }, - "node_modules/@babel/helper-string-parser": { - "version": "7.27.1", + "node_modules/@nodelib/fs.walk": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", "dev": true, "license": "MIT", + "dependencies": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + }, "engines": { - "node": ">=6.9.0" + "node": ">= 8" } }, - "node_modules/@babel/helper-validator-identifier": { - "version": "7.28.5", + "node_modules/@nolyfill/is-core-module": { + "version": "1.0.39", + "resolved": "https://registry.npmjs.org/@nolyfill/is-core-module/-/is-core-module-1.0.39.tgz", + "integrity": "sha512-nn5ozdjYQpUCZlWGuxcJY/KpxkWQs4DcbMCmKojjyrYDEAGy4Ce19NN4v5MduafTwJlbKc99UA8YhSVqq9yPZA==", "dev": true, "license": "MIT", "engines": { - "node": ">=6.9.0" + "node": ">=12.4.0" } }, - "node_modules/@babel/helper-validator-option": { - "version": "7.27.1", + "node_modules/@nuxt/opencollective": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/@nuxt/opencollective/-/opencollective-0.4.1.tgz", + "integrity": "sha512-GXD3wy50qYbxCJ652bDrDzgMr3NFEkIS374+IgFQKkCvk9yiYcLvX2XDYr7UyQxf4wK0e+yqDYRubZ0DtOxnmQ==", "dev": true, "license": "MIT", + "dependencies": { + "consola": "^3.2.3" + }, + "bin": { + "opencollective": "bin/opencollective.js" + }, "engines": { - "node": ">=6.9.0" + "node": "^14.18.0 || >=16.10.0", + "npm": ">=5.10.0" } }, - "node_modules/@babel/helpers": { - "version": "7.28.3", + "node_modules/@nuxtjs/opencollective": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/@nuxtjs/opencollective/-/opencollective-0.3.2.tgz", + "integrity": "sha512-um0xL3fO7Mf4fDxcqx9KryrB7zgRM5JSlvGN5AGkP6JLM5XEKyjeAiPbNxdXVXQ16isuAhYpvP88NgL2BGd6aA==", "dev": true, "license": "MIT", "dependencies": { - "@babel/template": "^7.27.2", - "@babel/types": "^7.28.2" + "chalk": "^4.1.0", + "consola": "^2.15.0", + "node-fetch": "^2.6.1" + }, + "bin": { + "opencollective": "bin/opencollective.js" }, "engines": { - "node": ">=6.9.0" + "node": ">=8.0.0", + "npm": ">=5.0.0" } }, - "node_modules/@babel/parser": { - "version": "7.28.3", + "node_modules/@nuxtjs/opencollective/node_modules/consola": { + "version": "2.15.3", + "resolved": "https://registry.npmjs.org/consola/-/consola-2.15.3.tgz", + "integrity": "sha512-9vAdYbHj6x2fLKC4+oPH0kFzY/orMZyG2Aj+kNylHxKGJ/Ed4dpNyAQYwJOdqO4zdM7XpVHmyejQDcQHrnuXbw==", "dev": true, - "license": "MIT", + "license": "MIT" + }, + "node_modules/@openapitools/openapi-generator-cli": { + "version": "2.25.2", + "resolved": "https://registry.npmjs.org/@openapitools/openapi-generator-cli/-/openapi-generator-cli-2.25.2.tgz", + "integrity": "sha512-TXElbW1NXCy0EECXiO5AD2ZzT1dmaCs41Z8t3pBUGaJf8zgF/Lm0P6GRhVEpw29iHBNjZcy8nrgQ1acUfuCdng==", + "dev": true, + "hasInstallScript": true, + "license": "Apache-2.0", "dependencies": { - "@babel/types": "^7.28.2" + "@nestjs/axios": "4.0.1", + "@nestjs/common": "11.1.9", + "@nestjs/core": "11.1.9", + "@nuxtjs/opencollective": "0.3.2", + "axios": "1.13.2", + "chalk": "4.1.2", + "commander": "8.3.0", + "compare-versions": "6.1.1", + "concurrently": "9.2.1", + "console.table": "0.10.0", + "fs-extra": "11.3.2", + "glob": "13.0.0", + "inquirer": "8.2.7", + "proxy-agent": "6.5.0", + "reflect-metadata": "0.2.2", + "rxjs": "7.8.2", + "tslib": "2.8.1" }, "bin": { - "parser": "bin/babel-parser.js" + "openapi-generator-cli": "main.js" }, "engines": { - "node": ">=6.0.0" + "node": ">=16" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/openapi_generator" } }, - "node_modules/@babel/plugin-syntax-async-generators": { - "version": "7.8.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==", "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" + "license": "Apache-2.0", + "engines": { + "node": ">=8.0.0" } }, - "node_modules/@babel/plugin-syntax-bigint": { - "version": "7.8.3", + "node_modules/@opentelemetry/api-logs": { + "version": "0.202.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/api-logs/-/api-logs-0.202.0.tgz", + "integrity": "sha512-fTBjMqKCfotFWfLzaKyhjLvyEyq5vDKTTFfBmx21btv3gvy8Lq6N5Dh2OzqeuN4DjtpSvNT1uNVfg08eD2Rfxw==", "dev": true, - "license": "MIT", + "license": "Apache-2.0", "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" + "@opentelemetry/api": "^1.3.0" }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" + "engines": { + "node": ">=8.0.0" } }, - "node_modules/@babel/plugin-syntax-class-properties": { - "version": "7.12.13", + "node_modules/@opentelemetry/context-async-hooks": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/context-async-hooks/-/context-async-hooks-2.0.1.tgz", + "integrity": "sha512-XuY23lSI3d4PEqKA+7SLtAgwqIfc6E/E9eAQWLN1vlpC53ybO3o6jW4BsXo1xvz9lYyyWItfQDDLzezER01mCw==", "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.12.13" + "license": "Apache-2.0", + "engines": { + "node": "^18.19.0 || >=20.6.0" }, "peerDependencies": { - "@babel/core": "^7.0.0-0" + "@opentelemetry/api": ">=1.0.0 <1.10.0" } }, - "node_modules/@babel/plugin-syntax-class-static-block": { - "version": "7.14.5", + "node_modules/@opentelemetry/core": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-2.0.1.tgz", + "integrity": "sha512-MaZk9SJIDgo1peKevlbhP6+IwIiNPNmswNL4AF0WaQJLbHXjr9SrZMgS12+iqr9ToV4ZVosCcc0f8Rg67LXjxw==", "dev": true, - "license": "MIT", + "license": "Apache-2.0", "dependencies": { - "@babel/helper-plugin-utils": "^7.14.5" + "@opentelemetry/semantic-conventions": "^1.29.0" }, "engines": { - "node": ">=6.9.0" + "node": "^18.19.0 || >=20.6.0" }, "peerDependencies": { - "@babel/core": "^7.0.0-0" + "@opentelemetry/api": ">=1.0.0 <1.10.0" } }, - "node_modules/@babel/plugin-syntax-import-attributes": { - "version": "7.27.1", + "node_modules/@opentelemetry/exporter-trace-otlp-http": { + "version": "0.202.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/exporter-trace-otlp-http/-/exporter-trace-otlp-http-0.202.0.tgz", + "integrity": "sha512-/hKE8DaFCJuaQqE1IxpgkcjOolUIwgi3TgHElPVKGdGRBSmJMTmN/cr6vWa55pCJIXPyhKvcMrbrya7DZ3VmzA==", "dev": true, - "license": "MIT", + "license": "Apache-2.0", "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1" + "@opentelemetry/core": "2.0.1", + "@opentelemetry/otlp-exporter-base": "0.202.0", + "@opentelemetry/otlp-transformer": "0.202.0", + "@opentelemetry/resources": "2.0.1", + "@opentelemetry/sdk-trace-base": "2.0.1" }, "engines": { - "node": ">=6.9.0" + "node": "^18.19.0 || >=20.6.0" }, "peerDependencies": { - "@babel/core": "^7.0.0-0" + "@opentelemetry/api": "^1.3.0" } }, - "node_modules/@babel/plugin-syntax-import-meta": { - "version": "7.10.4", + "node_modules/@opentelemetry/otlp-exporter-base": { + "version": "0.202.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/otlp-exporter-base/-/otlp-exporter-base-0.202.0.tgz", + "integrity": "sha512-nMEOzel+pUFYuBJg2znGmHJWbmvMbdX5/RhoKNKowguMbURhz0fwik5tUKplLcUtl8wKPL1y9zPnPxeBn65N0Q==", "dev": true, - "license": "MIT", + "license": "Apache-2.0", "dependencies": { - "@babel/helper-plugin-utils": "^7.10.4" + "@opentelemetry/core": "2.0.1", + "@opentelemetry/otlp-transformer": "0.202.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" }, "peerDependencies": { - "@babel/core": "^7.0.0-0" + "@opentelemetry/api": "^1.3.0" } }, - "node_modules/@babel/plugin-syntax-json-strings": { - "version": "7.8.3", + "node_modules/@opentelemetry/otlp-transformer": { + "version": "0.202.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/otlp-transformer/-/otlp-transformer-0.202.0.tgz", + "integrity": "sha512-5XO77QFzs9WkexvJQL9ksxL8oVFb/dfi9NWQSq7Sv0Efr9x3N+nb1iklP1TeVgxqJ7m1xWiC/Uv3wupiQGevMw==", "dev": true, - "license": "MIT", + "license": "Apache-2.0", "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" + "@opentelemetry/api-logs": "0.202.0", + "@opentelemetry/core": "2.0.1", + "@opentelemetry/resources": "2.0.1", + "@opentelemetry/sdk-logs": "0.202.0", + "@opentelemetry/sdk-metrics": "2.0.1", + "@opentelemetry/sdk-trace-base": "2.0.1", + "protobufjs": "^7.3.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" }, "peerDependencies": { - "@babel/core": "^7.0.0-0" + "@opentelemetry/api": "^1.3.0" } }, - "node_modules/@babel/plugin-syntax-jsx": { - "version": "7.27.1", + "node_modules/@opentelemetry/resources": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/resources/-/resources-2.0.1.tgz", + "integrity": "sha512-dZOB3R6zvBwDKnHDTB4X1xtMArB/d324VsbiPkX/Yu0Q8T2xceRthoIVFhJdvgVM2QhGVUyX9tzwiNxGtoBJUw==", "dev": true, - "license": "MIT", + "license": "Apache-2.0", "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1" + "@opentelemetry/core": "2.0.1", + "@opentelemetry/semantic-conventions": "^1.29.0" }, "engines": { - "node": ">=6.9.0" + "node": "^18.19.0 || >=20.6.0" }, "peerDependencies": { - "@babel/core": "^7.0.0-0" + "@opentelemetry/api": ">=1.3.0 <1.10.0" } }, - "node_modules/@babel/plugin-syntax-logical-assignment-operators": { - "version": "7.10.4", + "node_modules/@opentelemetry/sdk-logs": { + "version": "0.202.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-logs/-/sdk-logs-0.202.0.tgz", + "integrity": "sha512-pv8QiQLQzk4X909YKm0lnW4hpuQg4zHwJ4XBd5bZiXcd9urvrJNoNVKnxGHPiDVX/GiLFvr5DMYsDBQbZCypRQ==", "dev": true, - "license": "MIT", + "license": "Apache-2.0", "dependencies": { - "@babel/helper-plugin-utils": "^7.10.4" + "@opentelemetry/api-logs": "0.202.0", + "@opentelemetry/core": "2.0.1", + "@opentelemetry/resources": "2.0.1" }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-nullish-coalescing-operator": { - "version": "7.8.3", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" + "engines": { + "node": "^18.19.0 || >=20.6.0" }, "peerDependencies": { - "@babel/core": "^7.0.0-0" + "@opentelemetry/api": ">=1.4.0 <1.10.0" } }, - "node_modules/@babel/plugin-syntax-numeric-separator": { - "version": "7.10.4", + "node_modules/@opentelemetry/sdk-metrics": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-metrics/-/sdk-metrics-2.0.1.tgz", + "integrity": "sha512-wf8OaJoSnujMAHWR3g+/hGvNcsC16rf9s1So4JlMiFaFHiE4HpIA3oUh+uWZQ7CNuK8gVW/pQSkgoa5HkkOl0g==", "dev": true, - "license": "MIT", + "license": "Apache-2.0", "dependencies": { - "@babel/helper-plugin-utils": "^7.10.4" + "@opentelemetry/core": "2.0.1", + "@opentelemetry/resources": "2.0.1" }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-object-rest-spread": { - "version": "7.8.3", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" + "engines": { + "node": "^18.19.0 || >=20.6.0" }, "peerDependencies": { - "@babel/core": "^7.0.0-0" + "@opentelemetry/api": ">=1.9.0 <1.10.0" } }, - "node_modules/@babel/plugin-syntax-optional-catch-binding": { - "version": "7.8.3", + "node_modules/@opentelemetry/sdk-trace-base": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-trace-base/-/sdk-trace-base-2.0.1.tgz", + "integrity": "sha512-xYLlvk/xdScGx1aEqvxLwf6sXQLXCjk3/1SQT9X9AoN5rXRhkdvIFShuNNmtTEPRBqcsMbS4p/gJLNI2wXaDuQ==", "dev": true, - "license": "MIT", + "license": "Apache-2.0", "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" + "@opentelemetry/core": "2.0.1", + "@opentelemetry/resources": "2.0.1", + "@opentelemetry/semantic-conventions": "^1.29.0" }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-optional-chaining": { - "version": "7.8.3", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" + "engines": { + "node": "^18.19.0 || >=20.6.0" }, "peerDependencies": { - "@babel/core": "^7.0.0-0" + "@opentelemetry/api": ">=1.3.0 <1.10.0" } }, - "node_modules/@babel/plugin-syntax-private-property-in-object": { - "version": "7.14.5", + "node_modules/@opentelemetry/sdk-trace-node": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-trace-node/-/sdk-trace-node-2.0.1.tgz", + "integrity": "sha512-UhdbPF19pMpBtCWYP5lHbTogLWx9N0EBxtdagvkn5YtsAnCBZzL7SjktG+ZmupRgifsHMjwUaCCaVmqGfSADmA==", "dev": true, - "license": "MIT", + "license": "Apache-2.0", "dependencies": { - "@babel/helper-plugin-utils": "^7.14.5" + "@opentelemetry/context-async-hooks": "2.0.1", + "@opentelemetry/core": "2.0.1", + "@opentelemetry/sdk-trace-base": "2.0.1" }, "engines": { - "node": ">=6.9.0" + "node": "^18.19.0 || >=20.6.0" }, "peerDependencies": { - "@babel/core": "^7.0.0-0" + "@opentelemetry/api": ">=1.0.0 <1.10.0" } }, - "node_modules/@babel/plugin-syntax-top-level-await": { - "version": "7.14.5", + "node_modules/@opentelemetry/semantic-conventions": { + "version": "1.34.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-1.34.0.tgz", + "integrity": "sha512-aKcOkyrorBGlajjRdVoJWHTxfxO1vCNHLJVlSDaRHDIdjU+pX8IYQPvPDkYiujKLbRnWU+1TBwEt0QRgSm4SGA==", "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=14" + } + }, + "node_modules/@pact-foundation/pact": { + "version": "16.0.4", + "resolved": "https://registry.npmjs.org/@pact-foundation/pact/-/pact-16.0.4.tgz", + "integrity": "sha512-ftnaalgOHgYYigyIF8P9h/YJIuez6RILbluVAIQ4D3ZvZYuTCBR6DV0ZeXu4s4/UJICYyrpNiBbZif2c9MqiUw==", "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.14.5" + "@pact-foundation/pact-core": "^17.0.2", + "axios": "^1.12.2", + "body-parser": "^2.2.0", + "chalk": "4.1.2", + "express": "^5.1.0", + "graphql": "^16.11.0", + "graphql-tag": "^2.12.6", + "http-proxy": "^1.18.1", + "https-proxy-agent": "^7.0.6", + "js-base64": "^3.7.8", + "lodash": "^4.17.21", + "ramda": "^0.32.0", + "randexp": "^0.5.3", + "router": "^2.2.0", + "stack-utils": "^2.0.6" }, "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" + "node": ">=20" } }, - "node_modules/@babel/plugin-syntax-typescript": { - "version": "7.27.1", - "dev": true, + "node_modules/@pact-foundation/pact-core": { + "version": "17.1.0", + "resolved": "https://registry.npmjs.org/@pact-foundation/pact-core/-/pact-core-17.1.0.tgz", + "integrity": "sha512-0yAUBpLP9ggibw3uX8FW8gHj6zbxCiGNDh1K9oG9b6opzqD3ZsGD8YaKYOHOLTQSoQRsB//Kkeztvi4IfWO3iQ==", + "cpu": [ + "x64", + "ia32", + "arm64" + ], "license": "MIT", + "os": [ + "darwin", + "linux", + "win32" + ], "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1" + "check-types": "11.2.3", + "detect-libc": "^2.0.3", + "node-gyp-build": "^4.6.0", + "pino": "^10.0.0", + "pino-pretty": "^13.1.1", + "underscore": "1.13.7" }, "engines": { - "node": ">=6.9.0" + "node": ">=20" }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } + "optionalDependencies": { + "@pact-foundation/pact-core-darwin-arm64": "17.1.0", + "@pact-foundation/pact-core-darwin-x64": "17.1.0", + "@pact-foundation/pact-core-linux-arm64-glibc": "17.1.0", + "@pact-foundation/pact-core-linux-arm64-musl": "17.1.0", + "@pact-foundation/pact-core-linux-x64-glibc": "17.1.0", + "@pact-foundation/pact-core-linux-x64-musl": "17.1.0", + "@pact-foundation/pact-core-windows-x64": "17.1.0" + } + }, + "node_modules/@pact-foundation/pact-core-darwin-arm64": { + "version": "17.1.0", + "resolved": "https://registry.npmjs.org/@pact-foundation/pact-core-darwin-arm64/-/pact-core-darwin-arm64-17.1.0.tgz", + "integrity": "sha512-S4+VgqpuG2/0V7JRdDA9HvdOh38h45mEGr0m5Dqdh23hOvhRQHF25f3ylBpem6of+LacNIqJ+eyBEm4k/0H8MQ==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] }, - "node_modules/@babel/runtime": { - "version": "7.28.3", - "dev": true, + "node_modules/@pact-foundation/pact-core-darwin-x64": { + "version": "17.1.0", + "resolved": "https://registry.npmjs.org/@pact-foundation/pact-core-darwin-x64/-/pact-core-darwin-x64-17.1.0.tgz", + "integrity": "sha512-Ex7kykXXq4kyu9NHxvKzwV6yItXZduTF+Ui4dR316xaw7hzTQ+WWnHs0fPFlYFroO/LbWCFBzO5zUUkdU1UknQ==", + "cpu": [ + "x64" + ], "license": "MIT", - "engines": { - "node": ">=6.9.0" - } + "optional": true, + "os": [ + "darwin" + ] }, - "node_modules/@babel/template": { - "version": "7.27.2", - "dev": true, + "node_modules/@pact-foundation/pact-core-linux-arm64-glibc": { + "version": "17.1.0", + "resolved": "https://registry.npmjs.org/@pact-foundation/pact-core-linux-arm64-glibc/-/pact-core-linux-arm64-glibc-17.1.0.tgz", + "integrity": "sha512-bz34LVZz9DNJWUCwIkq71ZBkSSstjr4febDpnOy/JXPvxuDbVZ6OIy8L8vV24c6JJNTxC1E7z194yxz/zuGAKw==", + "cpu": [ + "arm64" + ], "license": "MIT", - "dependencies": { - "@babel/code-frame": "^7.27.1", - "@babel/parser": "^7.27.2", - "@babel/types": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - } + "optional": true, + "os": [ + "linux" + ] }, - "node_modules/@babel/traverse": { - "version": "7.28.3", - "dev": true, + "node_modules/@pact-foundation/pact-core-linux-arm64-musl": { + "version": "17.1.0", + "resolved": "https://registry.npmjs.org/@pact-foundation/pact-core-linux-arm64-musl/-/pact-core-linux-arm64-musl-17.1.0.tgz", + "integrity": "sha512-NE+1rEMhheBNo8UbUi2bUfjvLwhV9QkY+k/6M+VUvGMVoeNhTXAYBMqz13lWkCcMh4IUbjCaVm6GQrMnV6DV5g==", + "cpu": [ + "arm64" + ], "license": "MIT", - "dependencies": { - "@babel/code-frame": "^7.27.1", - "@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" - } + "optional": true, + "os": [ + "linux" + ] }, - "node_modules/@babel/types": { - "version": "7.28.2", - "dev": true, + "node_modules/@pact-foundation/pact-core-linux-x64-glibc": { + "version": "17.1.0", + "resolved": "https://registry.npmjs.org/@pact-foundation/pact-core-linux-x64-glibc/-/pact-core-linux-x64-glibc-17.1.0.tgz", + "integrity": "sha512-FgTeIVV+/2fCZaKEQN7MCXTNuHzBAT0d8TBGwiQXt6AzxNG9WvqqxpJIzH0mzjVmot3Q+8K4pySiSin/n4Y5CA==", + "cpu": [ + "x64" + ], "license": "MIT", - "dependencies": { - "@babel/helper-string-parser": "^7.27.1", - "@babel/helper-validator-identifier": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - } + "optional": true, + "os": [ + "linux" + ] }, - "node_modules/@balena/dockerignore": { - "version": "1.0.2", - "dev": true, - "license": "Apache-2.0" + "node_modules/@pact-foundation/pact-core-linux-x64-musl": { + "version": "17.1.0", + "resolved": "https://registry.npmjs.org/@pact-foundation/pact-core-linux-x64-musl/-/pact-core-linux-x64-musl-17.1.0.tgz", + "integrity": "sha512-2wB65MO1QxH6HvXSVjj8Ii6nRrvEh5Y2O5b7vy7xHAPCbXBR67A/mUw9cxTZLdomAZEaOZN/34p+KXAoTA9JHg==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] }, - "node_modules/@bcoe/v8-coverage": { - "version": "0.2.3", - "dev": true, + "node_modules/@pact-foundation/pact-core-windows-x64": { + "version": "17.1.0", + "resolved": "https://registry.npmjs.org/@pact-foundation/pact-core-windows-x64/-/pact-core-windows-x64-17.1.0.tgz", + "integrity": "sha512-iKpoKzUkcMUcdc5AbwLJDGNTv64DC0hZEh1xlyysIw6dbQkCcmgAx0Sjw8j7nBH/VQNxrCOaaN54fHzYgVmL9g==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@pinojs/redact": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/@pinojs/redact/-/redact-0.4.0.tgz", + "integrity": "sha512-k2ENnmBugE/rzQfEcdWHcCY+/FM3VLzH9cYEsbdsoqrvzAKRhUZeRNhAZvB8OitQJ1TBed3yqWtdjzS6wJKBwg==", "license": "MIT" }, - "node_modules/@borewit/text-codec": { - "version": "0.1.1", + "node_modules/@pkgjs/parseargs": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", + "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==", "dev": true, "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/Borewit" + "optional": true, + "engines": { + "node": ">=14" } }, - "node_modules/@cspotcode/source-map-support": { - "version": "0.8.1", + "node_modules/@pkgr/core": { + "version": "0.2.9", + "resolved": "https://registry.npmjs.org/@pkgr/core/-/core-0.2.9.tgz", + "integrity": "sha512-QNqXyfVS2wm9hweSYD2O7F0G06uurj9kZ96TRQE5Y9hU7+tgdZwIkbAKc5Ocy1HxEY2kuDQa6cQ1WRs/O5LFKA==", "dev": true, "license": "MIT", + "engines": { + "node": "^12.20.0 || ^14.18.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/pkgr" + } + }, + "node_modules/@playwright/test": { + "version": "1.57.0", + "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.57.0.tgz", + "integrity": "sha512-6TyEnHgd6SArQO8UO2OMTxshln3QMWBtPGrOCgs3wVEmQmwyuNtB10IZMfmYDE0riwNR1cu4q+pPcxMVtaG3TA==", + "license": "Apache-2.0", "dependencies": { - "@jridgewell/trace-mapping": "0.3.9" + "playwright": "1.57.0" + }, + "bin": { + "playwright": "cli.js" }, "engines": { - "node": ">=12" + "node": ">=18" } }, - "node_modules/@cspotcode/source-map-support/node_modules/@jridgewell/trace-mapping": { - "version": "0.3.9", + "node_modules/@protobufjs/aspromise": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@protobufjs/aspromise/-/aspromise-1.1.2.tgz", + "integrity": "sha512-j+gKExEuLmKwvz3OgROXtrJ2UG2x8Ch2YZUxahh+s1F2HZ+wAceUNLkvy6zKCPVRkU++ZWQrdxsUeQXmcg4uoQ==", "dev": true, - "license": "MIT", - "dependencies": { - "@jridgewell/resolve-uri": "^3.0.3", - "@jridgewell/sourcemap-codec": "^1.4.10" - } + "license": "BSD-3-Clause" }, - "node_modules/@csstools/color-helpers": { - "version": "5.0.2", + "node_modules/@protobufjs/base64": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@protobufjs/base64/-/base64-1.1.2.tgz", + "integrity": "sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg==", "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/csstools" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - } - ], - "license": "MIT-0", - "engines": { - "node": ">=18" - } - }, - "node_modules/@csstools/css-calc": { - "version": "2.1.4", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/csstools" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - } - ], - "license": "MIT", - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "@csstools/css-parser-algorithms": "^3.0.5", - "@csstools/css-tokenizer": "^3.0.4" - } - }, - "node_modules/@csstools/css-color-parser": { - "version": "3.0.10", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/csstools" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - } - ], - "license": "MIT", - "dependencies": { - "@csstools/color-helpers": "^5.0.2", - "@csstools/css-calc": "^2.1.4" - }, - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "@csstools/css-parser-algorithms": "^3.0.5", - "@csstools/css-tokenizer": "^3.0.4" - } - }, - "node_modules/@csstools/css-parser-algorithms": { - "version": "3.0.5", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/csstools" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - } - ], - "license": "MIT", - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "@csstools/css-tokenizer": "^3.0.4" - } - }, - "node_modules/@csstools/css-tokenizer": { - "version": "3.0.4", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/csstools" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - } - ], - "license": "MIT", - "engines": { - "node": ">=18" - } - }, - "node_modules/@emotion/is-prop-valid": { - "version": "1.2.2", - "dev": true, - "license": "MIT", - "dependencies": { - "@emotion/memoize": "^0.8.1" - } + "license": "BSD-3-Clause" }, - "node_modules/@emotion/memoize": { - "version": "0.8.1", + "node_modules/@protobufjs/codegen": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/@protobufjs/codegen/-/codegen-2.0.4.tgz", + "integrity": "sha512-YyFaikqM5sH0ziFZCN3xDC7zeGaB/d0IUb9CATugHWbd1FRFwWwt4ld4OYMPWu5a3Xe01mGAULCdqhMlPl29Jg==", "dev": true, - "license": "MIT" + "license": "BSD-3-Clause" }, - "node_modules/@emotion/unitless": { - "version": "0.8.1", + "node_modules/@protobufjs/eventemitter": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/eventemitter/-/eventemitter-1.1.0.tgz", + "integrity": "sha512-j9ednRT81vYJ9OfVuXG6ERSTdEL1xVsNgqpkxMsbIabzSo3goCjDIveeGv5d03om39ML71RdmrGNjG5SReBP/Q==", "dev": true, - "license": "MIT" - }, - "node_modules/@esbuild/aix-ppc64": { - "version": "0.25.11", - "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.11.tgz", - "integrity": "sha512-Xt1dOL13m8u0WE8iplx9Ibbm+hFAO0GsU2P34UNoDGvZYkY8ifSiy6Zuc1lYxfG7svWE2fzqCUmFp5HCn51gJg==", - "cpu": [ - "ppc64" - ], - "license": "MIT", - "optional": true, - "os": [ - "aix" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/android-arm": { - "version": "0.25.11", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.11.tgz", - "integrity": "sha512-uoa7dU+Dt3HYsethkJ1k6Z9YdcHjTrSb5NUy66ZfZaSV8hEYGD5ZHbEMXnqLFlbBflLsl89Zke7CAdDJ4JI+Gg==", - "cpu": [ - "arm" - ], - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/android-arm64": { - "version": "0.25.11", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.11.tgz", - "integrity": "sha512-9slpyFBc4FPPz48+f6jyiXOx/Y4v34TUeDDXJpZqAWQn/08lKGeD8aDp9TMn9jDz2CiEuHwfhRmGBvpnd/PWIQ==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/android-x64": { - "version": "0.25.11", - "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.11.tgz", - "integrity": "sha512-Sgiab4xBjPU1QoPEIqS3Xx+R2lezu0LKIEcYe6pftr56PqPygbB7+szVnzoShbx64MUupqoE0KyRlN7gezbl8g==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/darwin-arm64": { - "version": "0.25.11", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.11.tgz", - "integrity": "sha512-VekY0PBCukppoQrycFxUqkCojnTQhdec0vevUL/EDOCnXd9LKWqD/bHwMPzigIJXPhC59Vd1WFIL57SKs2mg4w==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/darwin-x64": { - "version": "0.25.11", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.11.tgz", - "integrity": "sha512-+hfp3yfBalNEpTGp9loYgbknjR695HkqtY3d3/JjSRUyPg/xd6q+mQqIb5qdywnDxRZykIHs3axEqU6l1+oWEQ==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/freebsd-arm64": { - "version": "0.25.11", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.11.tgz", - "integrity": "sha512-CmKjrnayyTJF2eVuO//uSjl/K3KsMIeYeyN7FyDBjsR3lnSJHaXlVoAK8DZa7lXWChbuOk7NjAc7ygAwrnPBhA==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/freebsd-x64": { - "version": "0.25.11", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.11.tgz", - "integrity": "sha512-Dyq+5oscTJvMaYPvW3x3FLpi2+gSZTCE/1ffdwuM6G1ARang/mb3jvjxs0mw6n3Lsw84ocfo9CrNMqc5lTfGOw==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-arm": { - "version": "0.25.11", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.11.tgz", - "integrity": "sha512-TBMv6B4kCfrGJ8cUPo7vd6NECZH/8hPpBHHlYI3qzoYFvWu2AdTvZNuU/7hsbKWqu/COU7NIK12dHAAqBLLXgw==", - "cpu": [ - "arm" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-arm64": { - "version": "0.25.11", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.11.tgz", - "integrity": "sha512-Qr8AzcplUhGvdyUF08A1kHU3Vr2O88xxP0Tm8GcdVOUm25XYcMPp2YqSVHbLuXzYQMf9Bh/iKx7YPqECs6ffLA==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-ia32": { - "version": "0.25.11", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.11.tgz", - "integrity": "sha512-TmnJg8BMGPehs5JKrCLqyWTVAvielc615jbkOirATQvWWB1NMXY77oLMzsUjRLa0+ngecEmDGqt5jiDC6bfvOw==", - "cpu": [ - "ia32" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-loong64": { - "version": "0.25.11", - "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.11.tgz", - "integrity": "sha512-DIGXL2+gvDaXlaq8xruNXUJdT5tF+SBbJQKbWy/0J7OhU8gOHOzKmGIlfTTl6nHaCOoipxQbuJi7O++ldrxgMw==", - "cpu": [ - "loong64" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-mips64el": { - "version": "0.25.11", - "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.11.tgz", - "integrity": "sha512-Osx1nALUJu4pU43o9OyjSCXokFkFbyzjXb6VhGIJZQ5JZi8ylCQ9/LFagolPsHtgw6himDSyb5ETSfmp4rpiKQ==", - "cpu": [ - "mips64el" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-ppc64": { - "version": "0.25.11", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.11.tgz", - "integrity": "sha512-nbLFgsQQEsBa8XSgSTSlrnBSrpoWh7ioFDUmwo158gIm5NNP+17IYmNWzaIzWmgCxq56vfr34xGkOcZ7jX6CPw==", - "cpu": [ - "ppc64" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-riscv64": { - "version": "0.25.11", - "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.11.tgz", - "integrity": "sha512-HfyAmqZi9uBAbgKYP1yGuI7tSREXwIb438q0nqvlpxAOs3XnZ8RsisRfmVsgV486NdjD7Mw2UrFSw51lzUk1ww==", - "cpu": [ - "riscv64" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-s390x": { - "version": "0.25.11", - "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.11.tgz", - "integrity": "sha512-HjLqVgSSYnVXRisyfmzsH6mXqyvj0SA7pG5g+9W7ESgwA70AXYNpfKBqh1KbTxmQVaYxpzA/SvlB9oclGPbApw==", - "cpu": [ - "s390x" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-x64": { - "version": "0.25.11", - "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.11.tgz", - "integrity": "sha512-HSFAT4+WYjIhrHxKBwGmOOSpphjYkcswF449j6EjsjbinTZbp8PJtjsVK1XFJStdzXdy/jaddAep2FGY+wyFAQ==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/netbsd-arm64": { - "version": "0.25.11", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.11.tgz", - "integrity": "sha512-hr9Oxj1Fa4r04dNpWr3P8QKVVsjQhqrMSUzZzf+LZcYjZNqhA3IAfPQdEh1FLVUJSiu6sgAwp3OmwBfbFgG2Xg==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "netbsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/netbsd-x64": { - "version": "0.25.11", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.11.tgz", - "integrity": "sha512-u7tKA+qbzBydyj0vgpu+5h5AeudxOAGncb8N6C9Kh1N4n7wU1Xw1JDApsRjpShRpXRQlJLb9wY28ELpwdPcZ7A==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "netbsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/openbsd-arm64": { - "version": "0.25.11", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.11.tgz", - "integrity": "sha512-Qq6YHhayieor3DxFOoYM1q0q1uMFYb7cSpLD2qzDSvK1NAvqFi8Xgivv0cFC6J+hWVw2teCYltyy9/m/14ryHg==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "openbsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/openbsd-x64": { - "version": "0.25.11", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.11.tgz", - "integrity": "sha512-CN+7c++kkbrckTOz5hrehxWN7uIhFFlmS/hqziSFVWpAzpWrQoAG4chH+nN3Be+Kzv/uuo7zhX716x3Sn2Jduw==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "openbsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/openharmony-arm64": { - "version": "0.25.11", - "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.25.11.tgz", - "integrity": "sha512-rOREuNIQgaiR+9QuNkbkxubbp8MSO9rONmwP5nKncnWJ9v5jQ4JxFnLu4zDSRPf3x4u+2VN4pM4RdyIzDty/wQ==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "openharmony" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/sunos-x64": { - "version": "0.25.11", - "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.11.tgz", - "integrity": "sha512-nq2xdYaWxyg9DcIyXkZhcYulC6pQ2FuCgem3LI92IwMgIZ69KHeY8T4Y88pcwoLIjbed8n36CyKoYRDygNSGhA==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "sunos" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/win32-arm64": { - "version": "0.25.11", - "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.11.tgz", - "integrity": "sha512-3XxECOWJq1qMZ3MN8srCJ/QfoLpL+VaxD/WfNRm1O3B4+AZ/BnLVgFbUV3eiRYDMXetciH16dwPbbHqwe1uU0Q==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/win32-ia32": { - "version": "0.25.11", - "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.11.tgz", - "integrity": "sha512-3ukss6gb9XZ8TlRyJlgLn17ecsK4NSQTmdIXRASVsiS2sQ6zPPZklNJT5GR5tE/MUarymmy8kCEf5xPCNCqVOA==", - "cpu": [ - "ia32" - ], - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/win32-x64": { - "version": "0.25.11", - "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.11.tgz", - "integrity": "sha512-D7Hpz6A2L4hzsRpPaCYkQnGOotdUpDzSGRIv9I+1ITdHROSFUWW95ZPZWQmGka1Fg7W3zFJowyn9WGwMJ0+KPA==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@eslint-community/eslint-utils": { - "version": "4.9.0", - "dev": true, - "license": "MIT", - "dependencies": { - "eslint-visitor-keys": "^3.4.3" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - }, - "peerDependencies": { - "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" - } - }, - "node_modules/@eslint-community/regexpp": { - "version": "4.12.1", - "dev": true, - "license": "MIT", - "engines": { - "node": "^12.0.0 || ^14.0.0 || >=16.0.0" - } - }, - "node_modules/@eslint/config-array": { - "version": "0.21.1", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@eslint/object-schema": "^2.1.7", - "debug": "^4.3.1", - "minimatch": "^3.1.2" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - } - }, - "node_modules/@eslint/config-array/node_modules/brace-expansion": { - "version": "1.1.12", - "dev": true, - "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "node_modules/@eslint/config-array/node_modules/minimatch": { - "version": "3.1.2", - "dev": true, - "license": "ISC", - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, - "node_modules/@eslint/config-helpers": { - "version": "0.4.1", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@eslint/core": "^0.16.0" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - } - }, - "node_modules/@eslint/core": { - "version": "0.16.0", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@types/json-schema": "^7.0.15" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - } - }, - "node_modules/@eslint/eslintrc": { - "version": "3.3.1", - "dev": true, - "license": "MIT", - "dependencies": { - "ajv": "^6.12.4", - "debug": "^4.3.2", - "espree": "^10.0.1", - "globals": "^14.0.0", - "ignore": "^5.2.0", - "import-fresh": "^3.2.1", - "js-yaml": "^4.1.0", - "minimatch": "^3.1.2", - "strip-json-comments": "^3.1.1" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/@eslint/eslintrc/node_modules/ajv": { - "version": "6.12.6", - "dev": true, - "license": "MIT", - "dependencies": { - "fast-deep-equal": "^3.1.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", - "uri-js": "^4.2.2" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" - } - }, - "node_modules/@eslint/eslintrc/node_modules/brace-expansion": { - "version": "1.1.12", - "dev": true, - "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "node_modules/@eslint/eslintrc/node_modules/ignore": { - "version": "5.3.2", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 4" - } - }, - "node_modules/@eslint/eslintrc/node_modules/json-schema-traverse": { - "version": "0.4.1", - "dev": true, - "license": "MIT" - }, - "node_modules/@eslint/eslintrc/node_modules/minimatch": { - "version": "3.1.2", - "dev": true, - "license": "ISC", - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, - "node_modules/@eslint/js": { - "version": "9.38.0", - "dev": true, - "license": "MIT", - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "url": "https://eslint.org/donate" - } - }, - "node_modules/@eslint/object-schema": { - "version": "2.1.7", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - } - }, - "node_modules/@eslint/plugin-kit": { - "version": "0.4.0", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@eslint/core": "^0.16.0", - "levn": "^0.4.1" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - } - }, - "node_modules/@exodus/schemasafe": { - "version": "1.3.0", - "dev": true, - "license": "MIT" - }, - "node_modules/@faker-js/faker": { - "version": "7.6.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=14.0.0", - "npm": ">=6.0.0" - } - }, - "node_modules/@grpc/grpc-js": { - "version": "1.13.4", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@grpc/proto-loader": "^0.7.13", - "@js-sdsl/ordered-map": "^4.4.2" - }, - "engines": { - "node": ">=12.10.0" - } - }, - "node_modules/@grpc/proto-loader": { - "version": "0.7.15", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "lodash.camelcase": "^4.3.0", - "long": "^5.0.0", - "protobufjs": "^7.2.5", - "yargs": "^17.7.2" - }, - "bin": { - "proto-loader-gen-types": "build/bin/proto-loader-gen-types.js" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/@grpc/proto-loader/node_modules/cliui": { - "version": "8.0.1", - "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/@grpc/proto-loader/node_modules/wrap-ansi": { - "version": "7.0.0", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" - } - }, - "node_modules/@grpc/proto-loader/node_modules/yargs": { - "version": "17.7.2", - "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/@humanfs/core": { - "version": "0.19.1", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": ">=18.18.0" - } - }, - "node_modules/@humanfs/node": { - "version": "0.16.6", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@humanfs/core": "^0.19.1", - "@humanwhocodes/retry": "^0.3.0" - }, - "engines": { - "node": ">=18.18.0" - } - }, - "node_modules/@humanfs/node/node_modules/@humanwhocodes/retry": { - "version": "0.3.1", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": ">=18.18" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/nzakas" - } - }, - "node_modules/@humanwhocodes/module-importer": { - "version": "1.0.1", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": ">=12.22" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/nzakas" - } - }, - "node_modules/@humanwhocodes/momoa": { - "version": "2.0.4", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": ">=10.10.0" - } - }, - "node_modules/@humanwhocodes/retry": { - "version": "0.4.3", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": ">=18.18" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/nzakas" - } - }, - "node_modules/@inquirer/external-editor": { - "version": "1.0.1", - "dev": true, - "license": "MIT", - "dependencies": { - "chardet": "^2.1.0", - "iconv-lite": "^0.6.3" - }, - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "@types/node": ">=18" - }, - "peerDependenciesMeta": { - "@types/node": { - "optional": true - } - } - }, - "node_modules/@internal/datastore": { - "resolved": "internal/datastore", - "link": true - }, - "node_modules/@internal/helpers": { - "resolved": "internal/helpers", - "link": true - }, - "node_modules/@isaacs/balanced-match": { - "version": "4.0.1", - "dev": true, - "license": "MIT", - "engines": { - "node": "20 || >=22" - } - }, - "node_modules/@isaacs/brace-expansion": { - "version": "5.0.0", - "dev": true, - "license": "MIT", - "dependencies": { - "@isaacs/balanced-match": "^4.0.1" - }, - "engines": { - "node": "20 || >=22" - } - }, - "node_modules/@isaacs/cliui": { - "version": "8.0.2", - "dev": true, - "license": "ISC", - "dependencies": { - "string-width": "^5.1.2", - "string-width-cjs": "npm:string-width@^4.2.0", - "strip-ansi": "^7.0.1", - "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", - "wrap-ansi": "^8.1.0", - "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/@isaacs/cliui/node_modules/ansi-regex": { - "version": "6.1.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/ansi-regex?sponsor=1" - } - }, - "node_modules/@isaacs/cliui/node_modules/ansi-styles": { - "version": "6.2.1", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/@isaacs/cliui/node_modules/string-width": { - "version": "5.1.2", - "dev": true, - "license": "MIT", - "dependencies": { - "eastasianwidth": "^0.2.0", - "emoji-regex": "^9.2.2", - "strip-ansi": "^7.0.1" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/@isaacs/cliui/node_modules/strip-ansi": { - "version": "7.1.0", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-regex": "^6.0.1" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/strip-ansi?sponsor=1" - } - }, - "node_modules/@isaacs/cliui/node_modules/wrap-ansi": { - "version": "8.1.0", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-styles": "^6.1.0", - "string-width": "^5.0.1", - "strip-ansi": "^7.0.1" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" - } - }, - "node_modules/@istanbuljs/load-nyc-config": { - "version": "1.1.0", - "dev": true, - "license": "ISC", - "dependencies": { - "camelcase": "^5.3.1", - "find-up": "^4.1.0", - "get-package-type": "^0.1.0", - "js-yaml": "^3.13.1", - "resolve-from": "^5.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/@istanbuljs/load-nyc-config/node_modules/argparse": { - "version": "1.0.10", - "dev": true, - "license": "MIT", - "dependencies": { - "sprintf-js": "~1.0.2" - } - }, - "node_modules/@istanbuljs/load-nyc-config/node_modules/find-up": { - "version": "4.1.0", - "dev": true, - "license": "MIT", - "dependencies": { - "locate-path": "^5.0.0", - "path-exists": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/@istanbuljs/load-nyc-config/node_modules/js-yaml": { - "version": "3.14.1", - "dev": true, - "license": "MIT", - "dependencies": { - "argparse": "^1.0.7", - "esprima": "^4.0.0" - }, - "bin": { - "js-yaml": "bin/js-yaml.js" - } - }, - "node_modules/@istanbuljs/load-nyc-config/node_modules/locate-path": { - "version": "5.0.0", - "dev": true, - "license": "MIT", - "dependencies": { - "p-locate": "^4.1.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/@istanbuljs/load-nyc-config/node_modules/p-limit": { - "version": "2.3.0", - "dev": true, - "license": "MIT", - "dependencies": { - "p-try": "^2.0.0" - }, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/@istanbuljs/load-nyc-config/node_modules/p-locate": { - "version": "4.1.0", - "dev": true, - "license": "MIT", - "dependencies": { - "p-limit": "^2.2.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/@istanbuljs/load-nyc-config/node_modules/resolve-from": { - "version": "5.0.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/@istanbuljs/schema": { - "version": "0.1.3", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/@jest/console": { - "version": "29.7.0", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/types": "^29.6.3", - "@types/node": "*", - "chalk": "^4.0.0", - "jest-message-util": "^29.7.0", - "jest-util": "^29.7.0", - "slash": "^3.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@jest/core": { - "version": "30.2.0", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/console": "30.2.0", - "@jest/pattern": "30.0.1", - "@jest/reporters": "30.2.0", - "@jest/test-result": "30.2.0", - "@jest/transform": "30.2.0", - "@jest/types": "30.2.0", - "@types/node": "*", - "ansi-escapes": "^4.3.2", - "chalk": "^4.1.2", - "ci-info": "^4.2.0", - "exit-x": "^0.2.2", - "graceful-fs": "^4.2.11", - "jest-changed-files": "30.2.0", - "jest-config": "30.2.0", - "jest-haste-map": "30.2.0", - "jest-message-util": "30.2.0", - "jest-regex-util": "30.0.1", - "jest-resolve": "30.2.0", - "jest-resolve-dependencies": "30.2.0", - "jest-runner": "30.2.0", - "jest-runtime": "30.2.0", - "jest-snapshot": "30.2.0", - "jest-util": "30.2.0", - "jest-validate": "30.2.0", - "jest-watcher": "30.2.0", - "micromatch": "^4.0.8", - "pretty-format": "30.2.0", - "slash": "^3.0.0" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - }, - "peerDependencies": { - "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" - }, - "peerDependenciesMeta": { - "node-notifier": { - "optional": true - } - } - }, - "node_modules/@jest/core/node_modules/@jest/console": { - "version": "30.2.0", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/types": "30.2.0", - "@types/node": "*", - "chalk": "^4.1.2", - "jest-message-util": "30.2.0", - "jest-util": "30.2.0", - "slash": "^3.0.0" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/@jest/core/node_modules/@jest/schemas": { - "version": "30.0.5", - "dev": true, - "license": "MIT", - "dependencies": { - "@sinclair/typebox": "^0.34.0" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/@jest/core/node_modules/@jest/test-result": { - "version": "30.2.0", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/console": "30.2.0", - "@jest/types": "30.2.0", - "@types/istanbul-lib-coverage": "^2.0.6", - "collect-v8-coverage": "^1.0.2" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/@jest/core/node_modules/@jest/types": { - "version": "30.2.0", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/pattern": "30.0.1", - "@jest/schemas": "30.0.5", - "@types/istanbul-lib-coverage": "^2.0.6", - "@types/istanbul-reports": "^3.0.4", - "@types/node": "*", - "@types/yargs": "^17.0.33", - "chalk": "^4.1.2" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/@jest/core/node_modules/@sinclair/typebox": { - "version": "0.34.41", - "dev": true, - "license": "MIT" - }, - "node_modules/@jest/core/node_modules/ansi-styles": { - "version": "5.2.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/@jest/core/node_modules/jest-message-util": { - "version": "30.2.0", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/code-frame": "^7.27.1", - "@jest/types": "30.2.0", - "@types/stack-utils": "^2.0.3", - "chalk": "^4.1.2", - "graceful-fs": "^4.2.11", - "micromatch": "^4.0.8", - "pretty-format": "30.2.0", - "slash": "^3.0.0", - "stack-utils": "^2.0.6" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/@jest/core/node_modules/jest-util": { - "version": "30.2.0", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/types": "30.2.0", - "@types/node": "*", - "chalk": "^4.1.2", - "ci-info": "^4.2.0", - "graceful-fs": "^4.2.11", - "picomatch": "^4.0.2" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/@jest/core/node_modules/picomatch": { - "version": "4.0.3", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } - }, - "node_modules/@jest/core/node_modules/pretty-format": { - "version": "30.2.0", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/schemas": "30.0.5", - "ansi-styles": "^5.2.0", - "react-is": "^18.3.1" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/@jest/diff-sequences": { - "version": "30.0.1", - "dev": true, - "license": "MIT", - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/@jest/environment": { - "version": "30.2.0", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/fake-timers": "30.2.0", - "@jest/types": "30.2.0", - "@types/node": "*", - "jest-mock": "30.2.0" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/@jest/environment-jsdom-abstract": { - "version": "30.0.5", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/environment": "30.0.5", - "@jest/fake-timers": "30.0.5", - "@jest/types": "30.0.5", - "@types/jsdom": "^21.1.7", - "@types/node": "*", - "jest-mock": "30.0.5", - "jest-util": "30.0.5" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - }, - "peerDependencies": { - "canvas": "^3.0.0", - "jsdom": "*" - }, - "peerDependenciesMeta": { - "canvas": { - "optional": true - } - } - }, - "node_modules/@jest/environment-jsdom-abstract/node_modules/@jest/environment": { - "version": "30.0.5", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/fake-timers": "30.0.5", - "@jest/types": "30.0.5", - "@types/node": "*", - "jest-mock": "30.0.5" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/@jest/environment-jsdom-abstract/node_modules/@jest/fake-timers": { - "version": "30.0.5", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/types": "30.0.5", - "@sinonjs/fake-timers": "^13.0.0", - "@types/node": "*", - "jest-message-util": "30.0.5", - "jest-mock": "30.0.5", - "jest-util": "30.0.5" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/@jest/environment-jsdom-abstract/node_modules/@jest/fake-timers/node_modules/jest-message-util": { - "version": "30.0.5", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/code-frame": "^7.27.1", - "@jest/types": "30.0.5", - "@types/stack-utils": "^2.0.3", - "chalk": "^4.1.2", - "graceful-fs": "^4.2.11", - "micromatch": "^4.0.8", - "pretty-format": "30.0.5", - "slash": "^3.0.0", - "stack-utils": "^2.0.6" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/@jest/environment-jsdom-abstract/node_modules/@jest/schemas": { - "version": "30.0.5", - "dev": true, - "license": "MIT", - "dependencies": { - "@sinclair/typebox": "^0.34.0" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/@jest/environment-jsdom-abstract/node_modules/@jest/types": { - "version": "30.0.5", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/pattern": "30.0.1", - "@jest/schemas": "30.0.5", - "@types/istanbul-lib-coverage": "^2.0.6", - "@types/istanbul-reports": "^3.0.4", - "@types/node": "*", - "@types/yargs": "^17.0.33", - "chalk": "^4.1.2" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/@jest/environment-jsdom-abstract/node_modules/@sinclair/typebox": { - "version": "0.34.38", - "dev": true, - "license": "MIT" - }, - "node_modules/@jest/environment-jsdom-abstract/node_modules/ansi-styles": { - "version": "5.2.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/@jest/environment-jsdom-abstract/node_modules/jest-mock": { - "version": "30.0.5", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/types": "30.0.5", - "@types/node": "*", - "jest-util": "30.0.5" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/@jest/environment-jsdom-abstract/node_modules/jest-util": { - "version": "30.0.5", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/types": "30.0.5", - "@types/node": "*", - "chalk": "^4.1.2", - "ci-info": "^4.2.0", - "graceful-fs": "^4.2.11", - "picomatch": "^4.0.2" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/@jest/environment-jsdom-abstract/node_modules/picomatch": { - "version": "4.0.3", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } - }, - "node_modules/@jest/environment-jsdom-abstract/node_modules/pretty-format": { - "version": "30.0.5", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/schemas": "30.0.5", - "ansi-styles": "^5.2.0", - "react-is": "^18.3.1" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/@jest/environment/node_modules/@jest/schemas": { - "version": "30.0.5", - "dev": true, - "license": "MIT", - "dependencies": { - "@sinclair/typebox": "^0.34.0" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/@jest/environment/node_modules/@jest/types": { - "version": "30.2.0", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/pattern": "30.0.1", - "@jest/schemas": "30.0.5", - "@types/istanbul-lib-coverage": "^2.0.6", - "@types/istanbul-reports": "^3.0.4", - "@types/node": "*", - "@types/yargs": "^17.0.33", - "chalk": "^4.1.2" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/@jest/environment/node_modules/@sinclair/typebox": { - "version": "0.34.41", - "dev": true, - "license": "MIT" - }, - "node_modules/@jest/expect": { - "version": "30.2.0", - "dev": true, - "license": "MIT", - "dependencies": { - "expect": "30.2.0", - "jest-snapshot": "30.2.0" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/@jest/expect-utils": { - "version": "29.7.0", - "dev": true, - "license": "MIT", - "dependencies": { - "jest-get-type": "^29.6.3" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@jest/expect/node_modules/@jest/expect-utils": { - "version": "30.2.0", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/get-type": "30.1.0" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/@jest/expect/node_modules/@jest/schemas": { - "version": "30.0.5", - "dev": true, - "license": "MIT", - "dependencies": { - "@sinclair/typebox": "^0.34.0" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/@jest/expect/node_modules/@jest/types": { - "version": "30.2.0", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/pattern": "30.0.1", - "@jest/schemas": "30.0.5", - "@types/istanbul-lib-coverage": "^2.0.6", - "@types/istanbul-reports": "^3.0.4", - "@types/node": "*", - "@types/yargs": "^17.0.33", - "chalk": "^4.1.2" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/@jest/expect/node_modules/@sinclair/typebox": { - "version": "0.34.41", - "dev": true, - "license": "MIT" - }, - "node_modules/@jest/expect/node_modules/ansi-styles": { - "version": "5.2.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/@jest/expect/node_modules/expect": { - "version": "30.2.0", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/expect-utils": "30.2.0", - "@jest/get-type": "30.1.0", - "jest-matcher-utils": "30.2.0", - "jest-message-util": "30.2.0", - "jest-mock": "30.2.0", - "jest-util": "30.2.0" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/@jest/expect/node_modules/jest-diff": { - "version": "30.2.0", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/diff-sequences": "30.0.1", - "@jest/get-type": "30.1.0", - "chalk": "^4.1.2", - "pretty-format": "30.2.0" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/@jest/expect/node_modules/jest-matcher-utils": { - "version": "30.2.0", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/get-type": "30.1.0", - "chalk": "^4.1.2", - "jest-diff": "30.2.0", - "pretty-format": "30.2.0" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/@jest/expect/node_modules/jest-message-util": { - "version": "30.2.0", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/code-frame": "^7.27.1", - "@jest/types": "30.2.0", - "@types/stack-utils": "^2.0.3", - "chalk": "^4.1.2", - "graceful-fs": "^4.2.11", - "micromatch": "^4.0.8", - "pretty-format": "30.2.0", - "slash": "^3.0.0", - "stack-utils": "^2.0.6" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/@jest/expect/node_modules/jest-util": { - "version": "30.2.0", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/types": "30.2.0", - "@types/node": "*", - "chalk": "^4.1.2", - "ci-info": "^4.2.0", - "graceful-fs": "^4.2.11", - "picomatch": "^4.0.2" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/@jest/expect/node_modules/picomatch": { - "version": "4.0.3", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } - }, - "node_modules/@jest/expect/node_modules/pretty-format": { - "version": "30.2.0", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/schemas": "30.0.5", - "ansi-styles": "^5.2.0", - "react-is": "^18.3.1" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/@jest/fake-timers": { - "version": "30.2.0", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/types": "30.2.0", - "@sinonjs/fake-timers": "^13.0.0", - "@types/node": "*", - "jest-message-util": "30.2.0", - "jest-mock": "30.2.0", - "jest-util": "30.2.0" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/@jest/fake-timers/node_modules/@jest/schemas": { - "version": "30.0.5", - "dev": true, - "license": "MIT", - "dependencies": { - "@sinclair/typebox": "^0.34.0" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/@jest/fake-timers/node_modules/@jest/types": { - "version": "30.2.0", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/pattern": "30.0.1", - "@jest/schemas": "30.0.5", - "@types/istanbul-lib-coverage": "^2.0.6", - "@types/istanbul-reports": "^3.0.4", - "@types/node": "*", - "@types/yargs": "^17.0.33", - "chalk": "^4.1.2" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/@jest/fake-timers/node_modules/@sinclair/typebox": { - "version": "0.34.41", - "dev": true, - "license": "MIT" - }, - "node_modules/@jest/fake-timers/node_modules/ansi-styles": { - "version": "5.2.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/@jest/fake-timers/node_modules/jest-message-util": { - "version": "30.2.0", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/code-frame": "^7.27.1", - "@jest/types": "30.2.0", - "@types/stack-utils": "^2.0.3", - "chalk": "^4.1.2", - "graceful-fs": "^4.2.11", - "micromatch": "^4.0.8", - "pretty-format": "30.2.0", - "slash": "^3.0.0", - "stack-utils": "^2.0.6" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/@jest/fake-timers/node_modules/jest-util": { - "version": "30.2.0", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/types": "30.2.0", - "@types/node": "*", - "chalk": "^4.1.2", - "ci-info": "^4.2.0", - "graceful-fs": "^4.2.11", - "picomatch": "^4.0.2" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/@jest/fake-timers/node_modules/picomatch": { - "version": "4.0.3", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } - }, - "node_modules/@jest/fake-timers/node_modules/pretty-format": { - "version": "30.2.0", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/schemas": "30.0.5", - "ansi-styles": "^5.2.0", - "react-is": "^18.3.1" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/@jest/get-type": { - "version": "30.1.0", - "dev": true, - "license": "MIT", - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/@jest/globals": { - "version": "30.2.0", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/environment": "30.2.0", - "@jest/expect": "30.2.0", - "@jest/types": "30.2.0", - "jest-mock": "30.2.0" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/@jest/globals/node_modules/@jest/schemas": { - "version": "30.0.5", - "dev": true, - "license": "MIT", - "dependencies": { - "@sinclair/typebox": "^0.34.0" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/@jest/globals/node_modules/@jest/types": { - "version": "30.2.0", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/pattern": "30.0.1", - "@jest/schemas": "30.0.5", - "@types/istanbul-lib-coverage": "^2.0.6", - "@types/istanbul-reports": "^3.0.4", - "@types/node": "*", - "@types/yargs": "^17.0.33", - "chalk": "^4.1.2" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/@jest/globals/node_modules/@sinclair/typebox": { - "version": "0.34.41", - "dev": true, - "license": "MIT" - }, - "node_modules/@jest/pattern": { - "version": "30.0.1", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/node": "*", - "jest-regex-util": "30.0.1" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/@jest/reporters": { - "version": "30.2.0", - "dev": true, - "license": "MIT", - "dependencies": { - "@bcoe/v8-coverage": "^0.2.3", - "@jest/console": "30.2.0", - "@jest/test-result": "30.2.0", - "@jest/transform": "30.2.0", - "@jest/types": "30.2.0", - "@jridgewell/trace-mapping": "^0.3.25", - "@types/node": "*", - "chalk": "^4.1.2", - "collect-v8-coverage": "^1.0.2", - "exit-x": "^0.2.2", - "glob": "^10.3.10", - "graceful-fs": "^4.2.11", - "istanbul-lib-coverage": "^3.0.0", - "istanbul-lib-instrument": "^6.0.0", - "istanbul-lib-report": "^3.0.0", - "istanbul-lib-source-maps": "^5.0.0", - "istanbul-reports": "^3.1.3", - "jest-message-util": "30.2.0", - "jest-util": "30.2.0", - "jest-worker": "30.2.0", - "slash": "^3.0.0", - "string-length": "^4.0.2", - "v8-to-istanbul": "^9.0.1" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - }, - "peerDependencies": { - "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" - }, - "peerDependenciesMeta": { - "node-notifier": { - "optional": true - } - } - }, - "node_modules/@jest/reporters/node_modules/@jest/console": { - "version": "30.2.0", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/types": "30.2.0", - "@types/node": "*", - "chalk": "^4.1.2", - "jest-message-util": "30.2.0", - "jest-util": "30.2.0", - "slash": "^3.0.0" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/@jest/reporters/node_modules/@jest/schemas": { - "version": "30.0.5", - "dev": true, - "license": "MIT", - "dependencies": { - "@sinclair/typebox": "^0.34.0" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/@jest/reporters/node_modules/@jest/test-result": { - "version": "30.2.0", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/console": "30.2.0", - "@jest/types": "30.2.0", - "@types/istanbul-lib-coverage": "^2.0.6", - "collect-v8-coverage": "^1.0.2" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/@jest/reporters/node_modules/@jest/types": { - "version": "30.2.0", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/pattern": "30.0.1", - "@jest/schemas": "30.0.5", - "@types/istanbul-lib-coverage": "^2.0.6", - "@types/istanbul-reports": "^3.0.4", - "@types/node": "*", - "@types/yargs": "^17.0.33", - "chalk": "^4.1.2" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/@jest/reporters/node_modules/@sinclair/typebox": { - "version": "0.34.41", - "dev": true, - "license": "MIT" - }, - "node_modules/@jest/reporters/node_modules/ansi-styles": { - "version": "5.2.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/@jest/reporters/node_modules/glob": { - "version": "10.4.5", - "dev": true, - "license": "ISC", - "dependencies": { - "foreground-child": "^3.1.0", - "jackspeak": "^3.1.2", - "minimatch": "^9.0.4", - "minipass": "^7.1.2", - "package-json-from-dist": "^1.0.0", - "path-scurry": "^1.11.1" - }, - "bin": { - "glob": "dist/esm/bin.mjs" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/@jest/reporters/node_modules/jackspeak": { - "version": "3.4.3", - "dev": true, - "license": "BlueOak-1.0.0", - "dependencies": { - "@isaacs/cliui": "^8.0.2" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - }, - "optionalDependencies": { - "@pkgjs/parseargs": "^0.11.0" - } - }, - "node_modules/@jest/reporters/node_modules/jest-message-util": { - "version": "30.2.0", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/code-frame": "^7.27.1", - "@jest/types": "30.2.0", - "@types/stack-utils": "^2.0.3", - "chalk": "^4.1.2", - "graceful-fs": "^4.2.11", - "micromatch": "^4.0.8", - "pretty-format": "30.2.0", - "slash": "^3.0.0", - "stack-utils": "^2.0.6" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/@jest/reporters/node_modules/jest-util": { - "version": "30.2.0", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/types": "30.2.0", - "@types/node": "*", - "chalk": "^4.1.2", - "ci-info": "^4.2.0", - "graceful-fs": "^4.2.11", - "picomatch": "^4.0.2" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/@jest/reporters/node_modules/lru-cache": { - "version": "10.4.3", - "dev": true, - "license": "ISC" - }, - "node_modules/@jest/reporters/node_modules/minimatch": { - "version": "9.0.5", - "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/@jest/reporters/node_modules/path-scurry": { - "version": "1.11.1", - "dev": true, - "license": "BlueOak-1.0.0", - "dependencies": { - "lru-cache": "^10.2.0", - "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" - }, - "engines": { - "node": ">=16 || 14 >=14.18" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/@jest/reporters/node_modules/picomatch": { - "version": "4.0.3", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } - }, - "node_modules/@jest/reporters/node_modules/pretty-format": { - "version": "30.2.0", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/schemas": "30.0.5", - "ansi-styles": "^5.2.0", - "react-is": "^18.3.1" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/@jest/schemas": { - "version": "29.6.3", - "dev": true, - "license": "MIT", - "dependencies": { - "@sinclair/typebox": "^0.27.8" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@jest/snapshot-utils": { - "version": "30.2.0", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/types": "30.2.0", - "chalk": "^4.1.2", - "graceful-fs": "^4.2.11", - "natural-compare": "^1.4.0" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/@jest/snapshot-utils/node_modules/@jest/schemas": { - "version": "30.0.5", - "dev": true, - "license": "MIT", - "dependencies": { - "@sinclair/typebox": "^0.34.0" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/@jest/snapshot-utils/node_modules/@jest/types": { - "version": "30.2.0", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/pattern": "30.0.1", - "@jest/schemas": "30.0.5", - "@types/istanbul-lib-coverage": "^2.0.6", - "@types/istanbul-reports": "^3.0.4", - "@types/node": "*", - "@types/yargs": "^17.0.33", - "chalk": "^4.1.2" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/@jest/snapshot-utils/node_modules/@sinclair/typebox": { - "version": "0.34.41", - "dev": true, - "license": "MIT" - }, - "node_modules/@jest/source-map": { - "version": "30.0.1", - "dev": true, - "license": "MIT", - "dependencies": { - "@jridgewell/trace-mapping": "^0.3.25", - "callsites": "^3.1.0", - "graceful-fs": "^4.2.11" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/@jest/test-result": { - "version": "29.7.0", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/console": "^29.7.0", - "@jest/types": "^29.6.3", - "@types/istanbul-lib-coverage": "^2.0.0", - "collect-v8-coverage": "^1.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@jest/test-sequencer": { - "version": "30.2.0", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/test-result": "30.2.0", - "graceful-fs": "^4.2.11", - "jest-haste-map": "30.2.0", - "slash": "^3.0.0" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/@jest/test-sequencer/node_modules/@jest/console": { - "version": "30.2.0", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/types": "30.2.0", - "@types/node": "*", - "chalk": "^4.1.2", - "jest-message-util": "30.2.0", - "jest-util": "30.2.0", - "slash": "^3.0.0" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/@jest/test-sequencer/node_modules/@jest/schemas": { - "version": "30.0.5", - "dev": true, - "license": "MIT", - "dependencies": { - "@sinclair/typebox": "^0.34.0" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/@jest/test-sequencer/node_modules/@jest/test-result": { - "version": "30.2.0", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/console": "30.2.0", - "@jest/types": "30.2.0", - "@types/istanbul-lib-coverage": "^2.0.6", - "collect-v8-coverage": "^1.0.2" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/@jest/test-sequencer/node_modules/@jest/types": { - "version": "30.2.0", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/pattern": "30.0.1", - "@jest/schemas": "30.0.5", - "@types/istanbul-lib-coverage": "^2.0.6", - "@types/istanbul-reports": "^3.0.4", - "@types/node": "*", - "@types/yargs": "^17.0.33", - "chalk": "^4.1.2" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/@jest/test-sequencer/node_modules/@sinclair/typebox": { - "version": "0.34.41", - "dev": true, - "license": "MIT" - }, - "node_modules/@jest/test-sequencer/node_modules/ansi-styles": { - "version": "5.2.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/@jest/test-sequencer/node_modules/jest-message-util": { - "version": "30.2.0", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/code-frame": "^7.27.1", - "@jest/types": "30.2.0", - "@types/stack-utils": "^2.0.3", - "chalk": "^4.1.2", - "graceful-fs": "^4.2.11", - "micromatch": "^4.0.8", - "pretty-format": "30.2.0", - "slash": "^3.0.0", - "stack-utils": "^2.0.6" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/@jest/test-sequencer/node_modules/jest-util": { - "version": "30.2.0", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/types": "30.2.0", - "@types/node": "*", - "chalk": "^4.1.2", - "ci-info": "^4.2.0", - "graceful-fs": "^4.2.11", - "picomatch": "^4.0.2" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/@jest/test-sequencer/node_modules/picomatch": { - "version": "4.0.3", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } - }, - "node_modules/@jest/test-sequencer/node_modules/pretty-format": { - "version": "30.2.0", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/schemas": "30.0.5", - "ansi-styles": "^5.2.0", - "react-is": "^18.3.1" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/@jest/transform": { - "version": "30.2.0", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/core": "^7.27.4", - "@jest/types": "30.2.0", - "@jridgewell/trace-mapping": "^0.3.25", - "babel-plugin-istanbul": "^7.0.1", - "chalk": "^4.1.2", - "convert-source-map": "^2.0.0", - "fast-json-stable-stringify": "^2.1.0", - "graceful-fs": "^4.2.11", - "jest-haste-map": "30.2.0", - "jest-regex-util": "30.0.1", - "jest-util": "30.2.0", - "micromatch": "^4.0.8", - "pirates": "^4.0.7", - "slash": "^3.0.0", - "write-file-atomic": "^5.0.1" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/@jest/transform/node_modules/@jest/schemas": { - "version": "30.0.5", - "dev": true, - "license": "MIT", - "dependencies": { - "@sinclair/typebox": "^0.34.0" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/@jest/transform/node_modules/@jest/types": { - "version": "30.2.0", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/pattern": "30.0.1", - "@jest/schemas": "30.0.5", - "@types/istanbul-lib-coverage": "^2.0.6", - "@types/istanbul-reports": "^3.0.4", - "@types/node": "*", - "@types/yargs": "^17.0.33", - "chalk": "^4.1.2" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/@jest/transform/node_modules/@sinclair/typebox": { - "version": "0.34.41", - "dev": true, - "license": "MIT" - }, - "node_modules/@jest/transform/node_modules/jest-util": { - "version": "30.2.0", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/types": "30.2.0", - "@types/node": "*", - "chalk": "^4.1.2", - "ci-info": "^4.2.0", - "graceful-fs": "^4.2.11", - "picomatch": "^4.0.2" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/@jest/transform/node_modules/picomatch": { - "version": "4.0.3", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } - }, - "node_modules/@jest/types": { - "version": "29.6.3", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/schemas": "^29.6.3", - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^17.0.8", - "chalk": "^4.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@jridgewell/gen-mapping": { - "version": "0.3.13", - "dev": true, - "license": "MIT", - "dependencies": { - "@jridgewell/sourcemap-codec": "^1.5.0", - "@jridgewell/trace-mapping": "^0.3.24" - } - }, - "node_modules/@jridgewell/resolve-uri": { - "version": "3.1.2", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@jridgewell/sourcemap-codec": { - "version": "1.5.5", - "dev": true, - "license": "MIT" - }, - "node_modules/@jridgewell/trace-mapping": { - "version": "0.3.30", - "dev": true, - "license": "MIT", - "dependencies": { - "@jridgewell/resolve-uri": "^3.1.0", - "@jridgewell/sourcemap-codec": "^1.4.14" - } - }, - "node_modules/@js-sdsl/ordered-map": { - "version": "4.4.2", - "dev": true, - "license": "MIT", - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/js-sdsl" - } - }, - "node_modules/@jsep-plugin/assignment": { - "version": "1.3.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 10.16.0" - }, - "peerDependencies": { - "jsep": "^0.4.0||^1.0.0" - } - }, - "node_modules/@jsep-plugin/regex": { - "version": "1.0.4", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 10.16.0" - }, - "peerDependencies": { - "jsep": "^0.4.0||^1.0.0" - } - }, - "node_modules/@lukeed/csprng": { - "version": "1.1.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/@nestjs/axios": { - "version": "4.0.1", - "dev": true, - "license": "MIT", - "peerDependencies": { - "@nestjs/common": "^10.0.0 || ^11.0.0", - "axios": "^1.3.1", - "rxjs": "^7.0.0" - } - }, - "node_modules/@nestjs/common": { - "version": "11.1.6", - "dev": true, - "license": "MIT", - "dependencies": { - "file-type": "21.0.0", - "iterare": "1.2.1", - "load-esm": "1.0.2", - "tslib": "2.8.1", - "uid": "2.0.2" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/nest" - }, - "peerDependencies": { - "class-transformer": ">=0.4.1", - "class-validator": ">=0.13.2", - "reflect-metadata": "^0.1.12 || ^0.2.0", - "rxjs": "^7.1.0" - }, - "peerDependenciesMeta": { - "class-transformer": { - "optional": true - }, - "class-validator": { - "optional": true - } - } - }, - "node_modules/@nestjs/core": { - "version": "11.1.6", - "dev": true, - "hasInstallScript": true, - "license": "MIT", - "dependencies": { - "@nuxt/opencollective": "0.4.1", - "fast-safe-stringify": "2.1.1", - "iterare": "1.2.1", - "path-to-regexp": "8.2.0", - "tslib": "2.8.1", - "uid": "2.0.2" - }, - "engines": { - "node": ">= 20" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/nest" - }, - "peerDependencies": { - "@nestjs/common": "^11.0.0", - "@nestjs/microservices": "^11.0.0", - "@nestjs/platform-express": "^11.0.0", - "@nestjs/websockets": "^11.0.0", - "reflect-metadata": "^0.1.12 || ^0.2.0", - "rxjs": "^7.1.0" - }, - "peerDependenciesMeta": { - "@nestjs/microservices": { - "optional": true - }, - "@nestjs/platform-express": { - "optional": true - }, - "@nestjs/websockets": { - "optional": true - } - } - }, - "node_modules/@next/eslint-plugin-next": { - "version": "15.4.6", - "dev": true, - "license": "MIT", - "dependencies": { - "fast-glob": "3.3.1" - } - }, - "node_modules/@next/eslint-plugin-next/node_modules/fast-glob": { - "version": "3.3.1", - "dev": true, - "license": "MIT", - "dependencies": { - "@nodelib/fs.stat": "^2.0.2", - "@nodelib/fs.walk": "^1.2.3", - "glob-parent": "^5.1.2", - "merge2": "^1.3.0", - "micromatch": "^4.0.4" - }, - "engines": { - "node": ">=8.6.0" - } - }, - "node_modules/@nodelib/fs.scandir": { - "version": "2.1.5", - "dev": true, - "license": "MIT", - "dependencies": { - "@nodelib/fs.stat": "2.0.5", - "run-parallel": "^1.1.9" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/@nodelib/fs.stat": { - "version": "2.0.5", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 8" - } - }, - "node_modules/@nodelib/fs.walk": { - "version": "1.2.8", - "dev": true, - "license": "MIT", - "dependencies": { - "@nodelib/fs.scandir": "2.1.5", - "fastq": "^1.6.0" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/@nolyfill/is-core-module": { - "version": "1.0.39", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12.4.0" - } - }, - "node_modules/@nuxt/opencollective": { - "version": "0.4.1", - "dev": true, - "license": "MIT", - "dependencies": { - "consola": "^3.2.3" - }, - "bin": { - "opencollective": "bin/opencollective.js" - }, - "engines": { - "node": "^14.18.0 || >=16.10.0", - "npm": ">=5.10.0" - } - }, - "node_modules/@nuxtjs/opencollective": { - "version": "0.3.2", - "dev": true, - "license": "MIT", - "dependencies": { - "chalk": "^4.1.0", - "consola": "^2.15.0", - "node-fetch": "^2.6.1" - }, - "bin": { - "opencollective": "bin/opencollective.js" - }, - "engines": { - "node": ">=8.0.0", - "npm": ">=5.0.0" - } - }, - "node_modules/@nuxtjs/opencollective/node_modules/consola": { - "version": "2.15.3", - "dev": true, - "license": "MIT" - }, - "node_modules/@openapitools/openapi-generator-cli": { - "version": "2.23.4", - "dev": true, - "hasInstallScript": true, - "license": "Apache-2.0", - "dependencies": { - "@nestjs/axios": "4.0.1", - "@nestjs/common": "11.1.6", - "@nestjs/core": "11.1.6", - "@nuxtjs/opencollective": "0.3.2", - "axios": "1.12.2", - "chalk": "4.1.2", - "commander": "8.3.0", - "compare-versions": "6.1.1", - "concurrently": "9.2.1", - "console.table": "0.10.0", - "fs-extra": "11.3.2", - "glob": "11.0.3", - "inquirer": "8.2.7", - "proxy-agent": "6.5.0", - "reflect-metadata": "0.2.2", - "rxjs": "7.8.2", - "tslib": "2.8.1" - }, - "bin": { - "openapi-generator-cli": "main.js" - }, - "engines": { - "node": ">=16" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/openapi_generator" - } - }, - "node_modules/@opentelemetry/api": { - "version": "1.9.0", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": ">=8.0.0" - } - }, - "node_modules/@opentelemetry/api-logs": { - "version": "0.53.0", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@opentelemetry/api": "^1.0.0" - }, - "engines": { - "node": ">=14" - } - }, - "node_modules/@opentelemetry/context-async-hooks": { - "version": "1.26.0", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": ">=14" - }, - "peerDependencies": { - "@opentelemetry/api": ">=1.0.0 <1.10.0" - } - }, - "node_modules/@opentelemetry/core": { - "version": "1.26.0", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@opentelemetry/semantic-conventions": "1.27.0" - }, - "engines": { - "node": ">=14" - }, - "peerDependencies": { - "@opentelemetry/api": ">=1.0.0 <1.10.0" - } - }, - "node_modules/@opentelemetry/exporter-trace-otlp-http": { - "version": "0.53.0", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@opentelemetry/core": "1.26.0", - "@opentelemetry/otlp-exporter-base": "0.53.0", - "@opentelemetry/otlp-transformer": "0.53.0", - "@opentelemetry/resources": "1.26.0", - "@opentelemetry/sdk-trace-base": "1.26.0" - }, - "engines": { - "node": ">=14" - }, - "peerDependencies": { - "@opentelemetry/api": "^1.0.0" - } - }, - "node_modules/@opentelemetry/otlp-exporter-base": { - "version": "0.53.0", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@opentelemetry/core": "1.26.0", - "@opentelemetry/otlp-transformer": "0.53.0" - }, - "engines": { - "node": ">=14" - }, - "peerDependencies": { - "@opentelemetry/api": "^1.0.0" - } - }, - "node_modules/@opentelemetry/otlp-transformer": { - "version": "0.53.0", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@opentelemetry/api-logs": "0.53.0", - "@opentelemetry/core": "1.26.0", - "@opentelemetry/resources": "1.26.0", - "@opentelemetry/sdk-logs": "0.53.0", - "@opentelemetry/sdk-metrics": "1.26.0", - "@opentelemetry/sdk-trace-base": "1.26.0", - "protobufjs": "^7.3.0" - }, - "engines": { - "node": ">=14" - }, - "peerDependencies": { - "@opentelemetry/api": "^1.3.0" - } - }, - "node_modules/@opentelemetry/propagator-b3": { - "version": "1.26.0", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@opentelemetry/core": "1.26.0" - }, - "engines": { - "node": ">=14" - }, - "peerDependencies": { - "@opentelemetry/api": ">=1.0.0 <1.10.0" - } - }, - "node_modules/@opentelemetry/propagator-jaeger": { - "version": "1.26.0", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@opentelemetry/core": "1.26.0" - }, - "engines": { - "node": ">=14" - }, - "peerDependencies": { - "@opentelemetry/api": ">=1.0.0 <1.10.0" - } - }, - "node_modules/@opentelemetry/resources": { - "version": "1.26.0", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@opentelemetry/core": "1.26.0", - "@opentelemetry/semantic-conventions": "1.27.0" - }, - "engines": { - "node": ">=14" - }, - "peerDependencies": { - "@opentelemetry/api": ">=1.0.0 <1.10.0" - } - }, - "node_modules/@opentelemetry/sdk-logs": { - "version": "0.53.0", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@opentelemetry/api-logs": "0.53.0", - "@opentelemetry/core": "1.26.0", - "@opentelemetry/resources": "1.26.0" - }, - "engines": { - "node": ">=14" - }, - "peerDependencies": { - "@opentelemetry/api": ">=1.4.0 <1.10.0" - } - }, - "node_modules/@opentelemetry/sdk-metrics": { - "version": "1.26.0", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@opentelemetry/core": "1.26.0", - "@opentelemetry/resources": "1.26.0" - }, - "engines": { - "node": ">=14" - }, - "peerDependencies": { - "@opentelemetry/api": ">=1.3.0 <1.10.0" - } - }, - "node_modules/@opentelemetry/sdk-trace-base": { - "version": "1.26.0", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@opentelemetry/core": "1.26.0", - "@opentelemetry/resources": "1.26.0", - "@opentelemetry/semantic-conventions": "1.27.0" - }, - "engines": { - "node": ">=14" - }, - "peerDependencies": { - "@opentelemetry/api": ">=1.0.0 <1.10.0" - } - }, - "node_modules/@opentelemetry/sdk-trace-node": { - "version": "1.26.0", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@opentelemetry/context-async-hooks": "1.26.0", - "@opentelemetry/core": "1.26.0", - "@opentelemetry/propagator-b3": "1.26.0", - "@opentelemetry/propagator-jaeger": "1.26.0", - "@opentelemetry/sdk-trace-base": "1.26.0", - "semver": "^7.5.2" - }, - "engines": { - "node": ">=14" - }, - "peerDependencies": { - "@opentelemetry/api": ">=1.0.0 <1.10.0" - } - }, - "node_modules/@opentelemetry/semantic-conventions": { - "version": "1.27.0", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": ">=14" - } - }, - "node_modules/@pkgjs/parseargs": { - "version": "0.11.0", - "dev": true, - "license": "MIT", - "optional": true, - "engines": { - "node": ">=14" - } - }, - "node_modules/@pkgr/core": { - "version": "0.2.9", - "dev": true, - "license": "MIT", - "engines": { - "node": "^12.20.0 || ^14.18.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/pkgr" - } - }, - "node_modules/@playwright/test": { - "version": "1.55.1", - "license": "Apache-2.0", - "dependencies": { - "playwright": "1.55.1" - }, - "bin": { - "playwright": "cli.js" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/@protobufjs/aspromise": { - "version": "1.1.2", - "dev": true, - "license": "BSD-3-Clause" - }, - "node_modules/@protobufjs/base64": { - "version": "1.1.2", - "dev": true, - "license": "BSD-3-Clause" - }, - "node_modules/@protobufjs/codegen": { - "version": "2.0.4", - "dev": true, - "license": "BSD-3-Clause" - }, - "node_modules/@protobufjs/eventemitter": { - "version": "1.1.0", - "dev": true, - "license": "BSD-3-Clause" - }, - "node_modules/@protobufjs/fetch": { - "version": "1.1.0", - "dev": true, - "license": "BSD-3-Clause", - "dependencies": { - "@protobufjs/aspromise": "^1.1.1", - "@protobufjs/inquire": "^1.1.0" - } - }, - "node_modules/@protobufjs/float": { - "version": "1.0.2", - "dev": true, - "license": "BSD-3-Clause" - }, - "node_modules/@protobufjs/inquire": { - "version": "1.1.0", - "dev": true, - "license": "BSD-3-Clause" - }, - "node_modules/@protobufjs/path": { - "version": "1.1.2", - "dev": true, - "license": "BSD-3-Clause" - }, - "node_modules/@protobufjs/pool": { - "version": "1.1.0", - "dev": true, - "license": "BSD-3-Clause" - }, - "node_modules/@protobufjs/utf8": { - "version": "1.1.0", - "dev": true, - "license": "BSD-3-Clause" - }, - "node_modules/@redocly/ajv": { - "version": "8.11.3", - "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-replace": "^1.0.1" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" - } - }, - "node_modules/@redocly/cli": { - "version": "1.34.5", - "dev": true, - "license": "MIT", - "dependencies": { - "@opentelemetry/api": "1.9.0", - "@opentelemetry/exporter-trace-otlp-http": "0.53.0", - "@opentelemetry/resources": "1.26.0", - "@opentelemetry/sdk-trace-node": "1.26.0", - "@opentelemetry/semantic-conventions": "1.27.0", - "@redocly/config": "^0.22.0", - "@redocly/openapi-core": "1.34.5", - "@redocly/respect-core": "1.34.5", - "abort-controller": "^3.0.0", - "chokidar": "^3.5.1", - "colorette": "^1.2.0", - "core-js": "^3.32.1", - "dotenv": "16.4.7", - "form-data": "^4.0.4", - "get-port-please": "^3.0.1", - "glob": "^7.1.6", - "handlebars": "^4.7.6", - "mobx": "^6.0.4", - "pluralize": "^8.0.0", - "react": "^17.0.0 || ^18.2.0 || ^19.0.0", - "react-dom": "^17.0.0 || ^18.2.0 || ^19.0.0", - "redoc": "2.5.0", - "semver": "^7.5.2", - "simple-websocket": "^9.0.0", - "styled-components": "^6.0.7", - "yargs": "17.0.1" - }, - "bin": { - "openapi": "bin/cli.js", - "redocly": "bin/cli.js" - }, - "engines": { - "node": ">=18.17.0", - "npm": ">=9.5.0" - } - }, - "node_modules/@redocly/cli/node_modules/brace-expansion": { - "version": "1.1.12", - "dev": true, - "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "node_modules/@redocly/cli/node_modules/glob": { - "version": "7.2.3", - "dev": true, - "license": "ISC", - "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.1.1", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - }, - "engines": { - "node": "*" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/@redocly/cli/node_modules/minimatch": { - "version": "3.1.2", - "dev": true, - "license": "ISC", - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, - "node_modules/@redocly/config": { - "version": "0.22.2", - "dev": true, - "license": "MIT" - }, - "node_modules/@redocly/openapi-core": { - "version": "1.34.5", - "dev": true, - "license": "MIT", - "dependencies": { - "@redocly/ajv": "^8.11.2", - "@redocly/config": "^0.22.0", - "colorette": "^1.2.0", - "https-proxy-agent": "^7.0.5", - "js-levenshtein": "^1.1.6", - "js-yaml": "^4.1.0", - "minimatch": "^5.0.1", - "pluralize": "^8.0.0", - "yaml-ast-parser": "0.0.43" - }, - "engines": { - "node": ">=18.17.0", - "npm": ">=9.5.0" - } - }, - "node_modules/@redocly/respect-core": { - "version": "1.34.5", - "dev": true, - "license": "MIT", - "dependencies": { - "@faker-js/faker": "^7.6.0", - "@redocly/ajv": "8.11.2", - "@redocly/openapi-core": "1.34.5", - "better-ajv-errors": "^1.2.0", - "colorette": "^2.0.20", - "concat-stream": "^2.0.0", - "cookie": "^0.7.2", - "dotenv": "16.4.7", - "form-data": "^4.0.4", - "jest-diff": "^29.3.1", - "jest-matcher-utils": "^29.3.1", - "js-yaml": "4.1.0", - "json-pointer": "^0.6.2", - "jsonpath-plus": "^10.0.6", - "open": "^10.1.0", - "openapi-sampler": "^1.6.1", - "outdent": "^0.8.0", - "set-cookie-parser": "^2.3.5", - "undici": "^6.21.1" - }, - "engines": { - "node": ">=18.17.0", - "npm": ">=9.5.0" - } - }, - "node_modules/@redocly/respect-core/node_modules/@redocly/ajv": { - "version": "8.11.2", - "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-replace": "^1.0.1" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" - } - }, - "node_modules/@redocly/respect-core/node_modules/colorette": { - "version": "2.0.20", - "dev": true, - "license": "MIT" - }, - "node_modules/@rtsao/scc": { - "version": "1.1.0", - "dev": true, - "license": "MIT" - }, - "node_modules/@rushstack/eslint-patch": { - "version": "1.12.0", - "dev": true, - "license": "MIT" - }, - "node_modules/@sinclair/typebox": { - "version": "0.27.8", - "dev": true, - "license": "MIT" - }, - "node_modules/@sinonjs/commons": { - "version": "3.0.1", - "dev": true, - "license": "BSD-3-Clause", - "dependencies": { - "type-detect": "4.0.8" - } - }, - "node_modules/@sinonjs/fake-timers": { - "version": "13.0.5", - "dev": true, - "license": "BSD-3-Clause", - "dependencies": { - "@sinonjs/commons": "^3.0.1" - } - }, - "node_modules/@smithy/abort-controller": { - "version": "4.2.0", - "license": "Apache-2.0", - "dependencies": { - "@smithy/types": "^4.6.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@smithy/chunked-blob-reader": { - "version": "5.1.0", - "license": "Apache-2.0", - "dependencies": { - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@smithy/chunked-blob-reader-native": { - "version": "4.1.0", - "license": "Apache-2.0", - "dependencies": { - "@smithy/util-base64": "^4.1.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@smithy/config-resolver": { - "version": "4.3.0", - "license": "Apache-2.0", - "dependencies": { - "@smithy/node-config-provider": "^4.3.0", - "@smithy/types": "^4.6.0", - "@smithy/util-config-provider": "^4.2.0", - "@smithy/util-middleware": "^4.2.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@smithy/core": { - "version": "3.15.0", - "license": "Apache-2.0", - "dependencies": { - "@smithy/middleware-serde": "^4.2.0", - "@smithy/protocol-http": "^5.3.0", - "@smithy/types": "^4.6.0", - "@smithy/util-base64": "^4.3.0", - "@smithy/util-body-length-browser": "^4.2.0", - "@smithy/util-middleware": "^4.2.0", - "@smithy/util-stream": "^4.5.0", - "@smithy/util-utf8": "^4.2.0", - "@smithy/uuid": "^1.1.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@smithy/credential-provider-imds": { - "version": "4.2.0", - "license": "Apache-2.0", - "dependencies": { - "@smithy/node-config-provider": "^4.3.0", - "@smithy/property-provider": "^4.2.0", - "@smithy/types": "^4.6.0", - "@smithy/url-parser": "^4.2.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@smithy/eventstream-codec": { - "version": "4.1.1", - "license": "Apache-2.0", - "dependencies": { - "@aws-crypto/crc32": "5.2.0", - "@smithy/types": "^4.5.0", - "@smithy/util-hex-encoding": "^4.1.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@smithy/eventstream-serde-browser": { - "version": "4.1.1", - "license": "Apache-2.0", - "dependencies": { - "@smithy/eventstream-serde-universal": "^4.1.1", - "@smithy/types": "^4.5.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@smithy/eventstream-serde-config-resolver": { - "version": "4.2.1", - "license": "Apache-2.0", - "dependencies": { - "@smithy/types": "^4.5.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@smithy/eventstream-serde-node": { - "version": "4.1.1", - "license": "Apache-2.0", - "dependencies": { - "@smithy/eventstream-serde-universal": "^4.1.1", - "@smithy/types": "^4.5.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@smithy/eventstream-serde-universal": { - "version": "4.1.1", - "license": "Apache-2.0", - "dependencies": { - "@smithy/eventstream-codec": "^4.1.1", - "@smithy/types": "^4.5.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@smithy/fetch-http-handler": { - "version": "5.3.1", - "license": "Apache-2.0", - "dependencies": { - "@smithy/protocol-http": "^5.3.0", - "@smithy/querystring-builder": "^4.2.0", - "@smithy/types": "^4.6.0", - "@smithy/util-base64": "^4.3.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@smithy/hash-blob-browser": { - "version": "4.1.1", - "license": "Apache-2.0", - "dependencies": { - "@smithy/chunked-blob-reader": "^5.1.0", - "@smithy/chunked-blob-reader-native": "^4.1.0", - "@smithy/types": "^4.5.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@smithy/hash-node": { - "version": "4.2.0", - "license": "Apache-2.0", - "dependencies": { - "@smithy/types": "^4.6.0", - "@smithy/util-buffer-from": "^4.2.0", - "@smithy/util-utf8": "^4.2.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@smithy/hash-stream-node": { - "version": "4.1.1", - "license": "Apache-2.0", - "dependencies": { - "@smithy/types": "^4.5.0", - "@smithy/util-utf8": "^4.1.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@smithy/invalid-dependency": { - "version": "4.2.0", - "license": "Apache-2.0", - "dependencies": { - "@smithy/types": "^4.6.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@smithy/is-array-buffer": { - "version": "4.2.0", - "license": "Apache-2.0", - "dependencies": { - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@smithy/md5-js": { - "version": "4.1.1", - "license": "Apache-2.0", - "dependencies": { - "@smithy/types": "^4.5.0", - "@smithy/util-utf8": "^4.1.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@smithy/middleware-content-length": { - "version": "4.2.0", - "license": "Apache-2.0", - "dependencies": { - "@smithy/protocol-http": "^5.3.0", - "@smithy/types": "^4.6.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@smithy/middleware-endpoint": { - "version": "4.3.1", - "license": "Apache-2.0", - "dependencies": { - "@smithy/core": "^3.15.0", - "@smithy/middleware-serde": "^4.2.0", - "@smithy/node-config-provider": "^4.3.0", - "@smithy/shared-ini-file-loader": "^4.3.0", - "@smithy/types": "^4.6.0", - "@smithy/url-parser": "^4.2.0", - "@smithy/util-middleware": "^4.2.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@smithy/middleware-retry": { - "version": "4.4.1", - "license": "Apache-2.0", - "dependencies": { - "@smithy/node-config-provider": "^4.3.0", - "@smithy/protocol-http": "^5.3.0", - "@smithy/service-error-classification": "^4.2.0", - "@smithy/smithy-client": "^4.7.1", - "@smithy/types": "^4.6.0", - "@smithy/util-middleware": "^4.2.0", - "@smithy/util-retry": "^4.2.0", - "@smithy/uuid": "^1.1.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@smithy/middleware-serde": { - "version": "4.2.0", - "license": "Apache-2.0", - "dependencies": { - "@smithy/protocol-http": "^5.3.0", - "@smithy/types": "^4.6.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@smithy/middleware-stack": { - "version": "4.2.0", - "license": "Apache-2.0", - "dependencies": { - "@smithy/types": "^4.6.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@smithy/node-config-provider": { - "version": "4.3.0", - "license": "Apache-2.0", - "dependencies": { - "@smithy/property-provider": "^4.2.0", - "@smithy/shared-ini-file-loader": "^4.3.0", - "@smithy/types": "^4.6.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@smithy/node-http-handler": { - "version": "4.3.0", - "license": "Apache-2.0", - "dependencies": { - "@smithy/abort-controller": "^4.2.0", - "@smithy/protocol-http": "^5.3.0", - "@smithy/querystring-builder": "^4.2.0", - "@smithy/types": "^4.6.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@smithy/property-provider": { - "version": "4.2.0", - "license": "Apache-2.0", - "dependencies": { - "@smithy/types": "^4.6.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@smithy/protocol-http": { - "version": "5.3.0", - "license": "Apache-2.0", - "dependencies": { - "@smithy/types": "^4.6.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@smithy/querystring-builder": { - "version": "4.2.0", - "license": "Apache-2.0", - "dependencies": { - "@smithy/types": "^4.6.0", - "@smithy/util-uri-escape": "^4.2.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@smithy/querystring-parser": { - "version": "4.2.0", - "license": "Apache-2.0", - "dependencies": { - "@smithy/types": "^4.6.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@smithy/service-error-classification": { - "version": "4.2.0", - "license": "Apache-2.0", - "dependencies": { - "@smithy/types": "^4.6.0" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@smithy/shared-ini-file-loader": { - "version": "4.3.0", - "license": "Apache-2.0", - "dependencies": { - "@smithy/types": "^4.6.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } + "license": "BSD-3-Clause" }, - "node_modules/@smithy/signature-v4": { - "version": "5.3.0", - "license": "Apache-2.0", + "node_modules/@protobufjs/fetch": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/fetch/-/fetch-1.1.0.tgz", + "integrity": "sha512-lljVXpqXebpsijW71PZaCYeIcE5on1w5DlQy5WH6GLbFryLUrBD4932W/E2BSpfRJWseIL4v/KPgBFxDOIdKpQ==", + "dev": true, + "license": "BSD-3-Clause", "dependencies": { - "@smithy/is-array-buffer": "^4.2.0", - "@smithy/protocol-http": "^5.3.0", - "@smithy/types": "^4.6.0", - "@smithy/util-hex-encoding": "^4.2.0", - "@smithy/util-middleware": "^4.2.0", - "@smithy/util-uri-escape": "^4.2.0", - "@smithy/util-utf8": "^4.2.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" + "@protobufjs/aspromise": "^1.1.1", + "@protobufjs/inquire": "^1.1.0" } }, - "node_modules/@smithy/smithy-client": { - "version": "4.7.1", - "license": "Apache-2.0", - "dependencies": { - "@smithy/core": "^3.15.0", - "@smithy/middleware-endpoint": "^4.3.1", - "@smithy/middleware-stack": "^4.2.0", - "@smithy/protocol-http": "^5.3.0", - "@smithy/types": "^4.6.0", - "@smithy/util-stream": "^4.5.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } + "node_modules/@protobufjs/float": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@protobufjs/float/-/float-1.0.2.tgz", + "integrity": "sha512-Ddb+kVXlXst9d+R9PfTIxh1EdNkgoRe5tOX6t01f1lYWOvJnSPDBlG241QLzcyPdoNTsblLUdujGSE4RzrTZGQ==", + "dev": true, + "license": "BSD-3-Clause" }, - "node_modules/@smithy/types": { - "version": "4.6.0", - "license": "Apache-2.0", - "dependencies": { - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } + "node_modules/@protobufjs/inquire": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/inquire/-/inquire-1.1.0.tgz", + "integrity": "sha512-kdSefcPdruJiFMVSbn801t4vFK7KB/5gd2fYvrxhuJYg8ILrmn9SKSX2tZdV6V+ksulWqS7aXjBcRXl3wHoD9Q==", + "dev": true, + "license": "BSD-3-Clause" }, - "node_modules/@smithy/url-parser": { - "version": "4.2.0", - "license": "Apache-2.0", - "dependencies": { - "@smithy/querystring-parser": "^4.2.0", - "@smithy/types": "^4.6.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } + "node_modules/@protobufjs/path": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@protobufjs/path/-/path-1.1.2.tgz", + "integrity": "sha512-6JOcJ5Tm08dOHAbdR3GrvP+yUUfkjG5ePsHYczMFLq3ZmMkAD98cDgcT2iA1lJ9NVwFd4tH/iSSoe44YWkltEA==", + "dev": true, + "license": "BSD-3-Clause" }, - "node_modules/@smithy/util-base64": { - "version": "4.3.0", - "license": "Apache-2.0", - "dependencies": { - "@smithy/util-buffer-from": "^4.2.0", - "@smithy/util-utf8": "^4.2.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } + "node_modules/@protobufjs/pool": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/pool/-/pool-1.1.0.tgz", + "integrity": "sha512-0kELaGSIDBKvcgS4zkjz1PeddatrjYcmMWOlAuAPwAeccUrPHdUqo/J6LiymHHEiJT5NrF1UVwxY14f+fy4WQw==", + "dev": true, + "license": "BSD-3-Clause" }, - "node_modules/@smithy/util-body-length-browser": { - "version": "4.2.0", - "license": "Apache-2.0", - "dependencies": { - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } + "node_modules/@protobufjs/utf8": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/utf8/-/utf8-1.1.0.tgz", + "integrity": "sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw==", + "dev": true, + "license": "BSD-3-Clause" }, - "node_modules/@smithy/util-body-length-node": { - "version": "4.2.1", - "license": "Apache-2.0", + "node_modules/@redocly/ajv": { + "version": "8.17.1", + "resolved": "https://registry.npmjs.org/@redocly/ajv/-/ajv-8.17.1.tgz", + "integrity": "sha512-EDtsGZS964mf9zAUXAl9Ew16eYbeyAFWhsPr0fX6oaJxgd8rApYlPBf0joyhnUHz88WxrigyFtTaqqzXNzPgqw==", + "dev": true, + "license": "MIT", "dependencies": { - "tslib": "^2.6.2" + "fast-deep-equal": "^3.1.3", + "fast-uri": "^3.0.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2" }, - "engines": { - "node": ">=18.0.0" + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" } }, - "node_modules/@smithy/util-buffer-from": { - "version": "4.2.0", - "license": "Apache-2.0", + "node_modules/@redocly/cli": { + "version": "2.13.0", + "resolved": "https://registry.npmjs.org/@redocly/cli/-/cli-2.13.0.tgz", + "integrity": "sha512-VOGh8p5gKy+u94SbvMGaHvDM6TPw668D9iQkNSztoi4T5sj3ZwM7Y8Z3yZnMqC5s5epDcLAMq4jCO8UVn5ZWHg==", + "dev": true, + "license": "MIT", "dependencies": { - "@smithy/is-array-buffer": "^4.2.0", - "tslib": "^2.6.2" + "@opentelemetry/exporter-trace-otlp-http": "0.202.0", + "@opentelemetry/resources": "2.0.1", + "@opentelemetry/sdk-trace-node": "2.0.1", + "@opentelemetry/semantic-conventions": "1.34.0", + "@redocly/openapi-core": "2.13.0", + "@redocly/respect-core": "2.13.0", + "abort-controller": "^3.0.0", + "chokidar": "^3.5.1", + "colorette": "^1.2.0", + "cookie": "^0.7.2", + "dotenv": "16.4.7", + "form-data": "^4.0.4", + "glob": "^11.0.1", + "handlebars": "^4.7.6", + "https-proxy-agent": "^7.0.5", + "mobx": "^6.0.4", + "pluralize": "^8.0.0", + "react": "^17.0.0 || ^18.2.0 || ^19.2.1", + "react-dom": "^17.0.0 || ^18.2.0 || ^19.2.1", + "redoc": "2.5.1", + "semver": "^7.5.2", + "set-cookie-parser": "^2.3.5", + "simple-websocket": "^9.0.0", + "styled-components": "^6.0.7", + "ulid": "^3.0.1", + "undici": "^6.21.3", + "yargs": "17.0.1" }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@smithy/util-config-provider": { - "version": "4.2.0", - "license": "Apache-2.0", - "dependencies": { - "tslib": "^2.6.2" + "bin": { + "openapi": "bin/cli.js", + "redocly": "bin/cli.js" }, "engines": { - "node": ">=18.0.0" + "node": ">=22.12.0 || >=20.19.0 <21.0.0", + "npm": ">=10" } }, - "node_modules/@smithy/util-defaults-mode-browser": { - "version": "4.3.0", - "license": "Apache-2.0", + "node_modules/@redocly/cli/node_modules/cliui": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", + "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", + "dev": true, + "license": "ISC", "dependencies": { - "@smithy/property-provider": "^4.2.0", - "@smithy/smithy-client": "^4.7.1", - "@smithy/types": "^4.6.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^7.0.0" } }, - "node_modules/@smithy/util-defaults-mode-node": { - "version": "4.2.1", - "license": "Apache-2.0", - "dependencies": { - "@smithy/config-resolver": "^4.3.0", - "@smithy/credential-provider-imds": "^4.2.0", - "@smithy/node-config-provider": "^4.3.0", - "@smithy/property-provider": "^4.2.0", - "@smithy/smithy-client": "^4.7.1", - "@smithy/types": "^4.6.0", - "tslib": "^2.6.2" - }, + "node_modules/@redocly/cli/node_modules/dotenv": { + "version": "16.4.7", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.4.7.tgz", + "integrity": "sha512-47qPchRCykZC03FhkYAhrvwU4xDBFIj1QPqaarj6mdM/hgUzfPHcpkHJOn3mJAufFeeAxAzeGsr5X0M4k6fLZQ==", + "dev": true, + "license": "BSD-2-Clause", "engines": { - "node": ">=18.0.0" + "node": ">=12" + }, + "funding": { + "url": "https://dotenvx.com" } }, - "node_modules/@smithy/util-endpoints": { - "version": "3.2.0", - "license": "Apache-2.0", + "node_modules/@redocly/cli/node_modules/glob": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-11.1.0.tgz", + "integrity": "sha512-vuNwKSaKiqm7g0THUBu2x7ckSs3XJLXE+2ssL7/MfTGPLLcrJQ/4Uq1CjPTtO5cCIiRxqvN6Twy1qOwhL0Xjcw==", + "dev": true, + "license": "BlueOak-1.0.0", "dependencies": { - "@smithy/node-config-provider": "^4.3.0", - "@smithy/types": "^4.6.0", - "tslib": "^2.6.2" + "foreground-child": "^3.3.1", + "jackspeak": "^4.1.1", + "minimatch": "^10.1.1", + "minipass": "^7.1.2", + "package-json-from-dist": "^1.0.0", + "path-scurry": "^2.0.0" + }, + "bin": { + "glob": "dist/esm/bin.mjs" }, "engines": { - "node": ">=18.0.0" + "node": "20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/@smithy/util-hex-encoding": { - "version": "4.2.0", - "license": "Apache-2.0", + "node_modules/@redocly/cli/node_modules/jackspeak": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-4.1.1.tgz", + "integrity": "sha512-zptv57P3GpL+O0I7VdMJNBZCu+BPHVQUk55Ft8/QCJjTVxrnJHuVuX/0Bl2A6/+2oyR/ZMEuFKwmzqqZ/U5nPQ==", + "dev": true, + "license": "BlueOak-1.0.0", "dependencies": { - "tslib": "^2.6.2" + "@isaacs/cliui": "^8.0.2" }, "engines": { - "node": ">=18.0.0" + "node": "20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/@smithy/util-middleware": { - "version": "4.2.0", - "license": "Apache-2.0", + "node_modules/@redocly/cli/node_modules/minimatch": { + "version": "10.1.1", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.1.1.tgz", + "integrity": "sha512-enIvLvRAFZYXJzkCYG5RKmPfrFArdLv+R+lbQ53BmIMLIry74bjKzX6iHAm8WYamJkhSSEabrWN5D97XnKObjQ==", + "dev": true, + "license": "BlueOak-1.0.0", "dependencies": { - "@smithy/types": "^4.6.0", - "tslib": "^2.6.2" + "@isaacs/brace-expansion": "^5.0.0" }, "engines": { - "node": ">=18.0.0" + "node": "20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/@smithy/util-retry": { - "version": "4.2.0", - "license": "Apache-2.0", + "node_modules/@redocly/cli/node_modules/wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "license": "MIT", "dependencies": { - "@smithy/service-error-classification": "^4.2.0", - "@smithy/types": "^4.6.0", - "tslib": "^2.6.2" + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" }, "engines": { - "node": ">=18.0.0" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" } }, - "node_modules/@smithy/util-stream": { - "version": "4.5.0", - "license": "Apache-2.0", + "node_modules/@redocly/cli/node_modules/yargs": { + "version": "17.0.1", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.0.1.tgz", + "integrity": "sha512-xBBulfCc8Y6gLFcrPvtqKz9hz8SO0l1Ni8GgDekvBX2ro0HRQImDGnikfc33cgzcYUSncapnNcZDjVFIH3f6KQ==", + "dev": true, + "license": "MIT", "dependencies": { - "@smithy/fetch-http-handler": "^5.3.1", - "@smithy/node-http-handler": "^4.3.0", - "@smithy/types": "^4.6.0", - "@smithy/util-base64": "^4.3.0", - "@smithy/util-buffer-from": "^4.2.0", - "@smithy/util-hex-encoding": "^4.2.0", - "@smithy/util-utf8": "^4.2.0", - "tslib": "^2.6.2" + "cliui": "^7.0.2", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.0", + "y18n": "^5.0.5", + "yargs-parser": "^20.2.2" }, "engines": { - "node": ">=18.0.0" + "node": ">=12" } }, - "node_modules/@smithy/util-uri-escape": { - "version": "4.2.0", - "license": "Apache-2.0", - "dependencies": { - "tslib": "^2.6.2" - }, + "node_modules/@redocly/cli/node_modules/yargs-parser": { + "version": "20.2.9", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz", + "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==", + "dev": true, + "license": "ISC", "engines": { - "node": ">=18.0.0" + "node": ">=10" } }, - "node_modules/@smithy/util-utf8": { - "version": "4.2.0", - "license": "Apache-2.0", + "node_modules/@redocly/config": { + "version": "0.41.0", + "resolved": "https://registry.npmjs.org/@redocly/config/-/config-0.41.0.tgz", + "integrity": "sha512-8yJ2e+ex8KVF25zijdpDbAEjyubk7NLfHsLI8h0MUnLEo2iEg6rTCDT9Qw71XDqd5UlXvfJb0Z0h6dd+Y6pWLw==", + "dev": true, + "license": "MIT", "dependencies": { - "@smithy/util-buffer-from": "^4.2.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" + "json-schema-to-ts": "2.7.2" } }, - "node_modules/@smithy/util-waiter": { - "version": "4.1.1", - "license": "Apache-2.0", + "node_modules/@redocly/openapi-core": { + "version": "2.13.0", + "resolved": "https://registry.npmjs.org/@redocly/openapi-core/-/openapi-core-2.13.0.tgz", + "integrity": "sha512-xQ4z5tsrXbIa4EfCniHv1zZ4etmQ0lpRcxy750iOamV5A/+19mgbPtD+UQCoT18puDAjcnOgpX7x2ha72qKrnw==", + "dev": true, + "license": "MIT", "dependencies": { - "@smithy/abort-controller": "^4.1.1", - "@smithy/types": "^4.5.0", - "tslib": "^2.6.2" + "@redocly/ajv": "^8.17.1", + "@redocly/config": "^0.41.0", + "ajv-formats": "^3.0.1", + "colorette": "^1.2.0", + "js-levenshtein": "^1.1.6", + "js-yaml": "^4.1.0", + "picomatch": "^4.0.3", + "pluralize": "^8.0.0", + "yaml-ast-parser": "0.0.43" }, "engines": { - "node": ">=18.0.0" + "node": ">=22.12.0 || >=20.19.0 <21.0.0", + "npm": ">=10" } }, - "node_modules/@smithy/uuid": { - "version": "1.1.0", - "license": "Apache-2.0", + "node_modules/@redocly/respect-core": { + "version": "2.13.0", + "resolved": "https://registry.npmjs.org/@redocly/respect-core/-/respect-core-2.13.0.tgz", + "integrity": "sha512-35OidNXWkmmsJiwgX+tFw7FaU8usZVvZ/lFBFNJga65pivEvaDlfiwKxIRTzM4iuNbc2FRvP2q30dlGAztv0tg==", + "dev": true, + "license": "MIT", "dependencies": { - "tslib": "^2.6.2" + "@faker-js/faker": "^7.6.0", + "@noble/hashes": "^1.8.0", + "@redocly/ajv": "8.17.1", + "@redocly/openapi-core": "2.13.0", + "better-ajv-errors": "^1.2.0", + "colorette": "^2.0.20", + "json-pointer": "^0.6.2", + "jsonpath-rfc9535": "1.3.0", + "openapi-sampler": "^1.6.1", + "outdent": "^0.8.0" }, "engines": { - "node": ">=18.0.0" + "node": ">=22.12.0 || >=20.19.0 <21.0.0", + "npm": ">=10" } }, - "node_modules/@stylistic/eslint-plugin": { - "version": "3.1.0", + "node_modules/@redocly/respect-core/node_modules/colorette": { + "version": "2.0.20", + "resolved": "https://registry.npmjs.org/colorette/-/colorette-2.0.20.tgz", + "integrity": "sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==", + "dev": true, + "license": "MIT" + }, + "node_modules/@rollup/plugin-commonjs": { + "version": "22.0.2", + "resolved": "https://registry.npmjs.org/@rollup/plugin-commonjs/-/plugin-commonjs-22.0.2.tgz", + "integrity": "sha512-//NdP6iIwPbMTcazYsiBMbJW7gfmpHom33u1beiIoHDEM0Q9clvtQB1T0efvMqHeKsGohiHo97BCPCkBXdscwg==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/utils": "^8.13.0", - "eslint-visitor-keys": "^4.2.0", - "espree": "^10.3.0", - "estraverse": "^5.3.0", - "picomatch": "^4.0.2" + "@rollup/pluginutils": "^3.1.0", + "commondir": "^1.0.1", + "estree-walker": "^2.0.1", + "glob": "^7.1.6", + "is-reference": "^1.2.1", + "magic-string": "^0.25.7", + "resolve": "^1.17.0" }, "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + "node": ">= 12.0.0" }, "peerDependencies": { - "eslint": ">=8.40.0" + "rollup": "^2.68.0" } }, - "node_modules/@stylistic/eslint-plugin/node_modules/eslint-visitor-keys": { - "version": "4.2.1", + "node_modules/@rollup/plugin-commonjs/node_modules/brace-expansion": { + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", + "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", "dev": true, - "license": "Apache-2.0", + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/@rollup/plugin-commonjs/node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "deprecated": "Glob versions prior to v9 are no longer supported", + "dev": true, + "license": "ISC", + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + "node": "*" }, "funding": { - "url": "https://opencollective.com/eslint" + "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/@stylistic/eslint-plugin/node_modules/picomatch": { - "version": "4.0.3", + "node_modules/@rollup/plugin-commonjs/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", "dev": true, - "license": "MIT", - "engines": { - "node": ">=12" + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" + "engines": { + "node": "*" } }, - "node_modules/@tokenizer/inflate": { - "version": "0.2.7", + "node_modules/@rollup/pluginutils": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-3.1.0.tgz", + "integrity": "sha512-GksZ6pr6TpIjHm8h9lSQ8pi8BE9VeubNT0OMJ3B5uZJ8pz73NPiqOtCog/x2/QzM1ENChPKxMDhiQuRHsqc+lg==", "dev": true, "license": "MIT", "dependencies": { - "debug": "^4.4.0", - "fflate": "^0.8.2", - "token-types": "^6.0.0" + "@types/estree": "0.0.39", + "estree-walker": "^1.0.1", + "picomatch": "^2.2.2" }, "engines": { - "node": ">=18" + "node": ">= 8.0.0" }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/Borewit" + "peerDependencies": { + "rollup": "^1.20.0||^2.0.0" } }, - "node_modules/@tokenizer/token": { - "version": "0.3.0", - "dev": true, - "license": "MIT" - }, - "node_modules/@tootallnate/quickjs-emscripten": { - "version": "0.23.0", - "dev": true, - "license": "MIT" - }, - "node_modules/@tsconfig/node10": { - "version": "1.0.11", + "node_modules/@rollup/pluginutils/node_modules/@types/estree": { + "version": "0.0.39", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-0.0.39.tgz", + "integrity": "sha512-EYNwp3bU+98cpU4lAWYYL7Zz+2gryWH1qbdDTidVd6hkiR6weksdbMadyXKXNPEkQFhXM+hVO9ZygomHXp+AIw==", "dev": true, "license": "MIT" }, - "node_modules/@tsconfig/node12": { - "version": "1.0.11", + "node_modules/@rollup/pluginutils/node_modules/estree-walker": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-1.0.1.tgz", + "integrity": "sha512-1fMXF3YP4pZZVozF8j/ZLfvnR8NSIljt56UhbZ5PeeDmmGHpgpdwQt7ITlGvYaQukCvuBRMLEiKiYC+oeIg4cg==", "dev": true, "license": "MIT" }, - "node_modules/@tsconfig/node14": { - "version": "1.0.3", + "node_modules/@rollup/pluginutils/node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", "dev": true, - "license": "MIT" + "license": "MIT", + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } }, - "node_modules/@tsconfig/node16": { - "version": "1.0.4", + "node_modules/@rtsao/scc": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@rtsao/scc/-/scc-1.1.0.tgz", + "integrity": "sha512-zt6OdqaDoOnJ1ZYsCYGt9YmWzDXl4vQdKTyJev62gFhRGKdx7mcT54V9KIjg+d2wi9EXsPvAPKe7i7WjfVWB8g==", "dev": true, "license": "MIT" }, - "node_modules/@tsconfig/node22": { - "version": "22.0.2", + "node_modules/@rushstack/eslint-patch": { + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/@rushstack/eslint-patch/-/eslint-patch-1.15.0.tgz", + "integrity": "sha512-ojSshQPKwVvSMR8yT2L/QtUkV5SXi/IfDiJ4/8d6UbTPjiHVmxZzUAzGD8Tzks1b9+qQkZa0isUOvYObedITaw==", "dev": true, "license": "MIT" }, - "node_modules/@types/aws-lambda": { - "version": "8.10.152", + "node_modules/@sinclair/typebox": { + "version": "0.34.41", + "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.34.41.tgz", + "integrity": "sha512-6gS8pZzSXdyRHTIqoqSVknxolr1kzfy4/CeDnrzsVz8TTIWUbOBr6gnzOmTYJ3eXQNh4IYHIGi5aIL7sOZ2G/g==", "dev": true, "license": "MIT" }, - "node_modules/@types/babel__core": { - "version": "7.20.5", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/parser": "^7.20.7", - "@babel/types": "^7.20.7", - "@types/babel__generator": "*", - "@types/babel__template": "*", - "@types/babel__traverse": "*" - } - }, - "node_modules/@types/babel__generator": { - "version": "7.27.0", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/types": "^7.0.0" - } - }, - "node_modules/@types/babel__template": { - "version": "7.4.4", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/parser": "^7.1.0", - "@babel/types": "^7.0.0" - } - }, - "node_modules/@types/babel__traverse": { - "version": "7.28.0", + "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": "MIT", + "license": "BSD-3-Clause", "dependencies": { - "@babel/types": "^7.28.2" + "type-detect": "4.0.8" } }, - "node_modules/@types/docker-modem": { - "version": "3.0.6", + "node_modules/@sinonjs/fake-timers": { + "version": "13.0.5", + "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-13.0.5.tgz", + "integrity": "sha512-36/hTbH2uaWuGVERyC6da9YwGWnzUZXuPro/F2LfsdOsLnCojz/iSH8MxUt/FD2S5XBSVPhmArFUXcpCQ2Hkiw==", "dev": true, - "license": "MIT", + "license": "BSD-3-Clause", "dependencies": { - "@types/node": "*", - "@types/ssh2": "*" + "@sinonjs/commons": "^3.0.1" } }, - "node_modules/@types/dockerode": { - "version": "3.3.42", - "dev": true, - "license": "MIT", + "node_modules/@smithy/abort-controller": { + "version": "4.2.7", + "resolved": "https://registry.npmjs.org/@smithy/abort-controller/-/abort-controller-4.2.7.tgz", + "integrity": "sha512-rzMY6CaKx2qxrbYbqjXWS0plqEy7LOdKHS0bg4ixJ6aoGDPNUcLWk/FRNuCILh7GKLG9TFUXYYeQQldMBBwuyw==", + "license": "Apache-2.0", "dependencies": { - "@types/docker-modem": "*", - "@types/node": "*", - "@types/ssh2": "*" + "@smithy/types": "^4.11.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" } }, - "node_modules/@types/estree": { - "version": "1.0.8", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/graceful-fs": { - "version": "4.1.9", - "dev": true, - "license": "MIT", + "node_modules/@smithy/chunked-blob-reader": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@smithy/chunked-blob-reader/-/chunked-blob-reader-5.2.0.tgz", + "integrity": "sha512-WmU0TnhEAJLWvfSeMxBNe5xtbselEO8+4wG0NtZeL8oR21WgH1xiO37El+/Y+H/Ie4SCwBy3MxYWmOYaGgZueA==", + "license": "Apache-2.0", "dependencies": { - "@types/node": "*" + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" } }, - "node_modules/@types/istanbul-lib-coverage": { - "version": "2.0.6", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/istanbul-lib-report": { - "version": "3.0.3", - "dev": true, - "license": "MIT", + "node_modules/@smithy/chunked-blob-reader-native": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/@smithy/chunked-blob-reader-native/-/chunked-blob-reader-native-4.2.1.tgz", + "integrity": "sha512-lX9Ay+6LisTfpLid2zZtIhSEjHMZoAR5hHCR4H7tBz/Zkfr5ea8RcQ7Tk4mi0P76p4cN+Btz16Ffno7YHpKXnQ==", + "license": "Apache-2.0", "dependencies": { - "@types/istanbul-lib-coverage": "*" + "@smithy/util-base64": "^4.3.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" } }, - "node_modules/@types/istanbul-reports": { - "version": "3.0.4", - "dev": true, - "license": "MIT", + "node_modules/@smithy/config-resolver": { + "version": "4.4.5", + "resolved": "https://registry.npmjs.org/@smithy/config-resolver/-/config-resolver-4.4.5.tgz", + "integrity": "sha512-HAGoUAFYsUkoSckuKbCPayECeMim8pOu+yLy1zOxt1sifzEbrsRpYa+mKcMdiHKMeiqOibyPG0sFJnmaV/OGEg==", + "license": "Apache-2.0", "dependencies": { - "@types/istanbul-lib-report": "*" + "@smithy/node-config-provider": "^4.3.7", + "@smithy/types": "^4.11.0", + "@smithy/util-config-provider": "^4.2.0", + "@smithy/util-endpoints": "^3.2.7", + "@smithy/util-middleware": "^4.2.7", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" } }, - "node_modules/@types/jest": { - "version": "30.0.0", - "resolved": "https://registry.npmjs.org/@types/jest/-/jest-30.0.0.tgz", - "integrity": "sha512-XTYugzhuwqWjws0CVz8QpM36+T+Dz5mTEBKhNs/esGLnCIlGdRy+Dq78NRjd7ls7r8BC8ZRMOrKlkO1hU0JOwA==", - "dev": true, - "license": "MIT", + "node_modules/@smithy/core": { + "version": "3.20.0", + "resolved": "https://registry.npmjs.org/@smithy/core/-/core-3.20.0.tgz", + "integrity": "sha512-WsSHCPq/neD5G/MkK4csLI5Y5Pkd9c1NMfpYEKeghSGaD4Ja1qLIohRQf2D5c1Uy5aXp76DeKHkzWZ9KAlHroQ==", + "license": "Apache-2.0", "dependencies": { - "expect": "^30.0.0", - "pretty-format": "^30.0.0" + "@smithy/middleware-serde": "^4.2.8", + "@smithy/protocol-http": "^5.3.7", + "@smithy/types": "^4.11.0", + "@smithy/util-base64": "^4.3.0", + "@smithy/util-body-length-browser": "^4.2.0", + "@smithy/util-middleware": "^4.2.7", + "@smithy/util-stream": "^4.5.8", + "@smithy/util-utf8": "^4.2.0", + "@smithy/uuid": "^1.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" } }, - "node_modules/@types/jest/node_modules/@jest/expect-utils": { - "version": "30.2.0", - "dev": true, - "license": "MIT", + "node_modules/@smithy/credential-provider-imds": { + "version": "4.2.7", + "resolved": "https://registry.npmjs.org/@smithy/credential-provider-imds/-/credential-provider-imds-4.2.7.tgz", + "integrity": "sha512-CmduWdCiILCRNbQWFR0OcZlUPVtyE49Sr8yYL0rZQ4D/wKxiNzBNS/YHemvnbkIWj623fplgkexUd/c9CAKdoA==", + "license": "Apache-2.0", "dependencies": { - "@jest/get-type": "30.1.0" + "@smithy/node-config-provider": "^4.3.7", + "@smithy/property-provider": "^4.2.7", + "@smithy/types": "^4.11.0", + "@smithy/url-parser": "^4.2.7", + "tslib": "^2.6.2" }, "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + "node": ">=18.0.0" } }, - "node_modules/@types/jest/node_modules/@jest/schemas": { - "version": "30.0.5", - "dev": true, - "license": "MIT", + "node_modules/@smithy/eventstream-codec": { + "version": "4.2.7", + "resolved": "https://registry.npmjs.org/@smithy/eventstream-codec/-/eventstream-codec-4.2.7.tgz", + "integrity": "sha512-DrpkEoM3j9cBBWhufqBwnbbn+3nf1N9FP6xuVJ+e220jbactKuQgaZwjwP5CP1t+O94brm2JgVMD2atMGX3xIQ==", + "license": "Apache-2.0", "dependencies": { - "@sinclair/typebox": "^0.34.0" + "@aws-crypto/crc32": "5.2.0", + "@smithy/types": "^4.11.0", + "@smithy/util-hex-encoding": "^4.2.0", + "tslib": "^2.6.2" }, "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + "node": ">=18.0.0" } }, - "node_modules/@types/jest/node_modules/@jest/types": { - "version": "30.2.0", - "dev": true, - "license": "MIT", + "node_modules/@smithy/eventstream-serde-browser": { + "version": "4.2.7", + "resolved": "https://registry.npmjs.org/@smithy/eventstream-serde-browser/-/eventstream-serde-browser-4.2.7.tgz", + "integrity": "sha512-ujzPk8seYoDBmABDE5YqlhQZAXLOrtxtJLrbhHMKjBoG5b4dK4i6/mEU+6/7yXIAkqOO8sJ6YxZl+h0QQ1IJ7g==", + "license": "Apache-2.0", "dependencies": { - "@jest/pattern": "30.0.1", - "@jest/schemas": "30.0.5", - "@types/istanbul-lib-coverage": "^2.0.6", - "@types/istanbul-reports": "^3.0.4", - "@types/node": "*", - "@types/yargs": "^17.0.33", - "chalk": "^4.1.2" + "@smithy/eventstream-serde-universal": "^4.2.7", + "@smithy/types": "^4.11.0", + "tslib": "^2.6.2" }, "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + "node": ">=18.0.0" } }, - "node_modules/@types/jest/node_modules/@sinclair/typebox": { - "version": "0.34.41", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/jest/node_modules/ansi-styles": { - "version": "5.2.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10" + "node_modules/@smithy/eventstream-serde-config-resolver": { + "version": "4.3.7", + "resolved": "https://registry.npmjs.org/@smithy/eventstream-serde-config-resolver/-/eventstream-serde-config-resolver-4.3.7.tgz", + "integrity": "sha512-x7BtAiIPSaNaWuzm24Q/mtSkv+BrISO/fmheiJ39PKRNH3RmH2Hph/bUKSOBOBC9unqfIYDhKTHwpyZycLGPVQ==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.11.0", + "tslib": "^2.6.2" }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" + "engines": { + "node": ">=18.0.0" } }, - "node_modules/@types/jest/node_modules/expect": { - "version": "30.2.0", - "dev": true, - "license": "MIT", + "node_modules/@smithy/eventstream-serde-node": { + "version": "4.2.7", + "resolved": "https://registry.npmjs.org/@smithy/eventstream-serde-node/-/eventstream-serde-node-4.2.7.tgz", + "integrity": "sha512-roySCtHC5+pQq5lK4be1fZ/WR6s/AxnPaLfCODIPArtN2du8s5Ot4mKVK3pPtijL/L654ws592JHJ1PbZFF6+A==", + "license": "Apache-2.0", "dependencies": { - "@jest/expect-utils": "30.2.0", - "@jest/get-type": "30.1.0", - "jest-matcher-utils": "30.2.0", - "jest-message-util": "30.2.0", - "jest-mock": "30.2.0", - "jest-util": "30.2.0" + "@smithy/eventstream-serde-universal": "^4.2.7", + "@smithy/types": "^4.11.0", + "tslib": "^2.6.2" }, "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + "node": ">=18.0.0" } }, - "node_modules/@types/jest/node_modules/jest-diff": { - "version": "30.2.0", - "dev": true, - "license": "MIT", + "node_modules/@smithy/eventstream-serde-universal": { + "version": "4.2.7", + "resolved": "https://registry.npmjs.org/@smithy/eventstream-serde-universal/-/eventstream-serde-universal-4.2.7.tgz", + "integrity": "sha512-QVD+g3+icFkThoy4r8wVFZMsIP08taHVKjE6Jpmz8h5CgX/kk6pTODq5cht0OMtcapUx+xrPzUTQdA+TmO0m1g==", + "license": "Apache-2.0", "dependencies": { - "@jest/diff-sequences": "30.0.1", - "@jest/get-type": "30.1.0", - "chalk": "^4.1.2", - "pretty-format": "30.2.0" + "@smithy/eventstream-codec": "^4.2.7", + "@smithy/types": "^4.11.0", + "tslib": "^2.6.2" }, "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + "node": ">=18.0.0" } }, - "node_modules/@types/jest/node_modules/jest-matcher-utils": { - "version": "30.2.0", - "dev": true, - "license": "MIT", + "node_modules/@smithy/fetch-http-handler": { + "version": "5.3.8", + "resolved": "https://registry.npmjs.org/@smithy/fetch-http-handler/-/fetch-http-handler-5.3.8.tgz", + "integrity": "sha512-h/Fi+o7mti4n8wx1SR6UHWLaakwHRx29sizvp8OOm7iqwKGFneT06GCSFhml6Bha5BT6ot5pj3CYZnCHhGC2Rg==", + "license": "Apache-2.0", "dependencies": { - "@jest/get-type": "30.1.0", - "chalk": "^4.1.2", - "jest-diff": "30.2.0", - "pretty-format": "30.2.0" + "@smithy/protocol-http": "^5.3.7", + "@smithy/querystring-builder": "^4.2.7", + "@smithy/types": "^4.11.0", + "@smithy/util-base64": "^4.3.0", + "tslib": "^2.6.2" }, "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + "node": ">=18.0.0" } }, - "node_modules/@types/jest/node_modules/jest-message-util": { - "version": "30.2.0", - "dev": true, - "license": "MIT", + "node_modules/@smithy/hash-blob-browser": { + "version": "4.2.8", + "resolved": "https://registry.npmjs.org/@smithy/hash-blob-browser/-/hash-blob-browser-4.2.8.tgz", + "integrity": "sha512-07InZontqsM1ggTCPSRgI7d8DirqRrnpL7nIACT4PW0AWrgDiHhjGZzbAE5UtRSiU0NISGUYe7/rri9ZeWyDpw==", + "license": "Apache-2.0", "dependencies": { - "@babel/code-frame": "^7.27.1", - "@jest/types": "30.2.0", - "@types/stack-utils": "^2.0.3", - "chalk": "^4.1.2", - "graceful-fs": "^4.2.11", - "micromatch": "^4.0.8", - "pretty-format": "30.2.0", - "slash": "^3.0.0", - "stack-utils": "^2.0.6" + "@smithy/chunked-blob-reader": "^5.2.0", + "@smithy/chunked-blob-reader-native": "^4.2.1", + "@smithy/types": "^4.11.0", + "tslib": "^2.6.2" }, "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + "node": ">=18.0.0" } }, - "node_modules/@types/jest/node_modules/jest-util": { - "version": "30.2.0", - "dev": true, - "license": "MIT", + "node_modules/@smithy/hash-node": { + "version": "4.2.7", + "resolved": "https://registry.npmjs.org/@smithy/hash-node/-/hash-node-4.2.7.tgz", + "integrity": "sha512-PU/JWLTBCV1c8FtB8tEFnY4eV1tSfBc7bDBADHfn1K+uRbPgSJ9jnJp0hyjiFN2PMdPzxsf1Fdu0eo9fJ760Xw==", + "license": "Apache-2.0", "dependencies": { - "@jest/types": "30.2.0", - "@types/node": "*", - "chalk": "^4.1.2", - "ci-info": "^4.2.0", - "graceful-fs": "^4.2.11", - "picomatch": "^4.0.2" + "@smithy/types": "^4.11.0", + "@smithy/util-buffer-from": "^4.2.0", + "@smithy/util-utf8": "^4.2.0", + "tslib": "^2.6.2" }, "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + "node": ">=18.0.0" } }, - "node_modules/@types/jest/node_modules/picomatch": { - "version": "4.0.3", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12" + "node_modules/@smithy/hash-stream-node": { + "version": "4.2.7", + "resolved": "https://registry.npmjs.org/@smithy/hash-stream-node/-/hash-stream-node-4.2.7.tgz", + "integrity": "sha512-ZQVoAwNYnFMIbd4DUc517HuwNelJUY6YOzwqrbcAgCnVn+79/OK7UjwA93SPpdTOpKDVkLIzavWm/Ck7SmnDPQ==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.11.0", + "@smithy/util-utf8": "^4.2.0", + "tslib": "^2.6.2" }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" + "engines": { + "node": ">=18.0.0" } }, - "node_modules/@types/jest/node_modules/pretty-format": { - "version": "30.2.0", - "dev": true, - "license": "MIT", + "node_modules/@smithy/invalid-dependency": { + "version": "4.2.7", + "resolved": "https://registry.npmjs.org/@smithy/invalid-dependency/-/invalid-dependency-4.2.7.tgz", + "integrity": "sha512-ncvgCr9a15nPlkhIUx3CU4d7E7WEuVJOV7fS7nnK2hLtPK9tYRBkMHQbhXU1VvvKeBm/O0x26OEoBq+ngFpOEQ==", + "license": "Apache-2.0", "dependencies": { - "@jest/schemas": "30.0.5", - "ansi-styles": "^5.2.0", - "react-is": "^18.3.1" + "@smithy/types": "^4.11.0", + "tslib": "^2.6.2" }, "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + "node": ">=18.0.0" } }, - "node_modules/@types/js-yaml": { - "version": "4.0.9", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/jsdom": { - "version": "21.1.7", - "dev": true, - "license": "MIT", + "node_modules/@smithy/is-array-buffer": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/@smithy/is-array-buffer/-/is-array-buffer-4.2.0.tgz", + "integrity": "sha512-DZZZBvC7sjcYh4MazJSGiWMI2L7E0oCiRHREDzIxi/M2LY79/21iXt6aPLHge82wi5LsuRF5A06Ds3+0mlh6CQ==", + "license": "Apache-2.0", "dependencies": { - "@types/node": "*", - "@types/tough-cookie": "*", - "parse5": "^7.0.0" + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" } }, - "node_modules/@types/json-schema": { - "version": "7.0.15", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/json5": { - "version": "0.0.29", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/node": { - "version": "24.2.1", - "dev": true, - "license": "MIT", + "node_modules/@smithy/md5-js": { + "version": "4.2.7", + "resolved": "https://registry.npmjs.org/@smithy/md5-js/-/md5-js-4.2.7.tgz", + "integrity": "sha512-Wv6JcUxtOLTnxvNjDnAiATUsk8gvA6EeS8zzHig07dotpByYsLot+m0AaQEniUBjx97AC41MQR4hW0baraD1Xw==", + "license": "Apache-2.0", "dependencies": { - "undici-types": "~7.10.0" + "@smithy/types": "^4.11.0", + "@smithy/util-utf8": "^4.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" } }, - "node_modules/@types/ssh2": { - "version": "1.15.5", - "dev": true, - "license": "MIT", + "node_modules/@smithy/middleware-content-length": { + "version": "4.2.7", + "resolved": "https://registry.npmjs.org/@smithy/middleware-content-length/-/middleware-content-length-4.2.7.tgz", + "integrity": "sha512-GszfBfCcvt7kIbJ41LuNa5f0wvQCHhnGx/aDaZJCCT05Ld6x6U2s0xsc/0mBFONBZjQJp2U/0uSJ178OXOwbhg==", + "license": "Apache-2.0", "dependencies": { - "@types/node": "^18.11.18" + "@smithy/protocol-http": "^5.3.7", + "@smithy/types": "^4.11.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" } }, - "node_modules/@types/ssh2-streams": { - "version": "0.1.12", - "dev": true, - "license": "MIT", + "node_modules/@smithy/middleware-endpoint": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/@smithy/middleware-endpoint/-/middleware-endpoint-4.4.1.tgz", + "integrity": "sha512-gpLspUAoe6f1M6H0u4cVuFzxZBrsGZmjx2O9SigurTx4PbntYa4AJ+o0G0oGm1L2oSX6oBhcGHwrfJHup2JnJg==", + "license": "Apache-2.0", "dependencies": { - "@types/node": "*" + "@smithy/core": "^3.20.0", + "@smithy/middleware-serde": "^4.2.8", + "@smithy/node-config-provider": "^4.3.7", + "@smithy/shared-ini-file-loader": "^4.4.2", + "@smithy/types": "^4.11.0", + "@smithy/url-parser": "^4.2.7", + "@smithy/util-middleware": "^4.2.7", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" } }, - "node_modules/@types/ssh2/node_modules/@types/node": { - "version": "18.19.122", - "dev": true, - "license": "MIT", + "node_modules/@smithy/middleware-retry": { + "version": "4.4.17", + "resolved": "https://registry.npmjs.org/@smithy/middleware-retry/-/middleware-retry-4.4.17.tgz", + "integrity": "sha512-MqbXK6Y9uq17h+4r0ogu/sBT6V/rdV+5NvYL7ZV444BKfQygYe8wAhDrVXagVebN6w2RE0Fm245l69mOsPGZzg==", + "license": "Apache-2.0", "dependencies": { - "undici-types": "~5.26.4" + "@smithy/node-config-provider": "^4.3.7", + "@smithy/protocol-http": "^5.3.7", + "@smithy/service-error-classification": "^4.2.7", + "@smithy/smithy-client": "^4.10.2", + "@smithy/types": "^4.11.0", + "@smithy/util-middleware": "^4.2.7", + "@smithy/util-retry": "^4.2.7", + "@smithy/uuid": "^1.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" } }, - "node_modules/@types/ssh2/node_modules/undici-types": { - "version": "5.26.5", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/stack-utils": { - "version": "2.0.3", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/stylis": { - "version": "4.2.5", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/tough-cookie": { - "version": "4.0.5", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/trusted-types": { - "version": "2.0.7", - "dev": true, - "license": "MIT", - "optional": true - }, - "node_modules/@types/uuid": { - "version": "9.0.8", - "license": "MIT" - }, - "node_modules/@types/yargs": { - "version": "17.0.33", - "dev": true, - "license": "MIT", + "node_modules/@smithy/middleware-serde": { + "version": "4.2.8", + "resolved": "https://registry.npmjs.org/@smithy/middleware-serde/-/middleware-serde-4.2.8.tgz", + "integrity": "sha512-8rDGYen5m5+NV9eHv9ry0sqm2gI6W7mc1VSFMtn6Igo25S507/HaOX9LTHAS2/J32VXD0xSzrY0H5FJtOMS4/w==", + "license": "Apache-2.0", "dependencies": { - "@types/yargs-parser": "*" + "@smithy/protocol-http": "^5.3.7", + "@smithy/types": "^4.11.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" } }, - "node_modules/@types/yargs-parser": { - "version": "21.0.3", - "dev": true, - "license": "MIT" - }, - "node_modules/@typescript-eslint/eslint-plugin": { - "version": "8.46.2", - "dev": true, - "license": "MIT", + "node_modules/@smithy/middleware-stack": { + "version": "4.2.7", + "resolved": "https://registry.npmjs.org/@smithy/middleware-stack/-/middleware-stack-4.2.7.tgz", + "integrity": "sha512-bsOT0rJ+HHlZd9crHoS37mt8qRRN/h9jRve1SXUhVbkRzu0QaNYZp1i1jha4n098tsvROjcwfLlfvcFuJSXEsw==", + "license": "Apache-2.0", "dependencies": { - "@eslint-community/regexpp": "^4.10.0", - "@typescript-eslint/scope-manager": "8.46.2", - "@typescript-eslint/type-utils": "8.46.2", - "@typescript-eslint/utils": "8.46.2", - "@typescript-eslint/visitor-keys": "8.46.2", - "graphemer": "^1.4.0", - "ignore": "^7.0.0", - "natural-compare": "^1.4.0", - "ts-api-utils": "^2.1.0" + "@smithy/types": "^4.11.0", + "tslib": "^2.6.2" }, "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "@typescript-eslint/parser": "^8.46.2", - "eslint": "^8.57.0 || ^9.0.0", - "typescript": ">=4.8.4 <6.0.0" + "node": ">=18.0.0" } }, - "node_modules/@typescript-eslint/parser": { - "version": "8.46.2", - "dev": true, - "license": "MIT", + "node_modules/@smithy/node-config-provider": { + "version": "4.3.7", + "resolved": "https://registry.npmjs.org/@smithy/node-config-provider/-/node-config-provider-4.3.7.tgz", + "integrity": "sha512-7r58wq8sdOcrwWe+klL9y3bc4GW1gnlfnFOuL7CXa7UzfhzhxKuzNdtqgzmTV+53lEp9NXh5hY/S4UgjLOzPfw==", + "license": "Apache-2.0", "dependencies": { - "@typescript-eslint/scope-manager": "8.46.2", - "@typescript-eslint/types": "8.46.2", - "@typescript-eslint/typescript-estree": "8.46.2", - "@typescript-eslint/visitor-keys": "8.46.2", - "debug": "^4.3.4" + "@smithy/property-provider": "^4.2.7", + "@smithy/shared-ini-file-loader": "^4.4.2", + "@smithy/types": "^4.11.0", + "tslib": "^2.6.2" }, "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "eslint": "^8.57.0 || ^9.0.0", - "typescript": ">=4.8.4 <6.0.0" + "node": ">=18.0.0" } }, - "node_modules/@typescript-eslint/project-service": { - "version": "8.46.2", - "dev": true, - "license": "MIT", + "node_modules/@smithy/node-http-handler": { + "version": "4.4.7", + "resolved": "https://registry.npmjs.org/@smithy/node-http-handler/-/node-http-handler-4.4.7.tgz", + "integrity": "sha512-NELpdmBOO6EpZtWgQiHjoShs1kmweaiNuETUpuup+cmm/xJYjT4eUjfhrXRP4jCOaAsS3c3yPsP3B+K+/fyPCQ==", + "license": "Apache-2.0", "dependencies": { - "@typescript-eslint/tsconfig-utils": "^8.46.2", - "@typescript-eslint/types": "^8.46.2", - "debug": "^4.3.4" + "@smithy/abort-controller": "^4.2.7", + "@smithy/protocol-http": "^5.3.7", + "@smithy/querystring-builder": "^4.2.7", + "@smithy/types": "^4.11.0", + "tslib": "^2.6.2" }, "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "typescript": ">=4.8.4 <6.0.0" + "node": ">=18.0.0" } }, - "node_modules/@typescript-eslint/scope-manager": { - "version": "8.46.2", - "dev": true, - "license": "MIT", + "node_modules/@smithy/property-provider": { + "version": "4.2.7", + "resolved": "https://registry.npmjs.org/@smithy/property-provider/-/property-provider-4.2.7.tgz", + "integrity": "sha512-jmNYKe9MGGPoSl/D7JDDs1C8b3dC8f/w78LbaVfoTtWy4xAd5dfjaFG9c9PWPihY4ggMQNQSMtzU77CNgAJwmA==", + "license": "Apache-2.0", "dependencies": { - "@typescript-eslint/types": "8.46.2", - "@typescript-eslint/visitor-keys": "8.46.2" + "@smithy/types": "^4.11.0", + "tslib": "^2.6.2" }, "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" + "node": ">=18.0.0" } }, - "node_modules/@typescript-eslint/tsconfig-utils": { - "version": "8.46.2", - "dev": true, - "license": "MIT", - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" + "node_modules/@smithy/protocol-http": { + "version": "5.3.7", + "resolved": "https://registry.npmjs.org/@smithy/protocol-http/-/protocol-http-5.3.7.tgz", + "integrity": "sha512-1r07pb994I20dD/c2seaZhoCuNYm0rWrvBxhCQ70brNh11M5Ml2ew6qJVo0lclB3jMIXirD4s2XRXRe7QEi0xA==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.11.0", + "tslib": "^2.6.2" }, - "peerDependencies": { - "typescript": ">=4.8.4 <6.0.0" + "engines": { + "node": ">=18.0.0" } }, - "node_modules/@typescript-eslint/type-utils": { - "version": "8.46.2", - "dev": true, - "license": "MIT", + "node_modules/@smithy/querystring-builder": { + "version": "4.2.7", + "resolved": "https://registry.npmjs.org/@smithy/querystring-builder/-/querystring-builder-4.2.7.tgz", + "integrity": "sha512-eKONSywHZxK4tBxe2lXEysh8wbBdvDWiA+RIuaxZSgCMmA0zMgoDpGLJhnyj+c0leOQprVnXOmcB4m+W9Rw7sg==", + "license": "Apache-2.0", "dependencies": { - "@typescript-eslint/types": "8.46.2", - "@typescript-eslint/typescript-estree": "8.46.2", - "@typescript-eslint/utils": "8.46.2", - "debug": "^4.3.4", - "ts-api-utils": "^2.1.0" + "@smithy/types": "^4.11.0", + "@smithy/util-uri-escape": "^4.2.0", + "tslib": "^2.6.2" }, "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "eslint": "^8.57.0 || ^9.0.0", - "typescript": ">=4.8.4 <6.0.0" + "node": ">=18.0.0" } }, - "node_modules/@typescript-eslint/types": { - "version": "8.46.2", - "dev": true, - "license": "MIT", - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + "node_modules/@smithy/querystring-parser": { + "version": "4.2.7", + "resolved": "https://registry.npmjs.org/@smithy/querystring-parser/-/querystring-parser-4.2.7.tgz", + "integrity": "sha512-3X5ZvzUHmlSTHAXFlswrS6EGt8fMSIxX/c3Rm1Pni3+wYWB6cjGocmRIoqcQF9nU5OgGmL0u7l9m44tSUpfj9w==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.11.0", + "tslib": "^2.6.2" }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" + "engines": { + "node": ">=18.0.0" } }, - "node_modules/@typescript-eslint/typescript-estree": { - "version": "8.46.2", - "dev": true, - "license": "MIT", + "node_modules/@smithy/service-error-classification": { + "version": "4.2.7", + "resolved": "https://registry.npmjs.org/@smithy/service-error-classification/-/service-error-classification-4.2.7.tgz", + "integrity": "sha512-YB7oCbukqEb2Dlh3340/8g8vNGbs/QsNNRms+gv3N2AtZz9/1vSBx6/6tpwQpZMEJFs7Uq8h4mmOn48ZZ72MkA==", + "license": "Apache-2.0", "dependencies": { - "@typescript-eslint/project-service": "8.46.2", - "@typescript-eslint/tsconfig-utils": "8.46.2", - "@typescript-eslint/types": "8.46.2", - "@typescript-eslint/visitor-keys": "8.46.2", - "debug": "^4.3.4", - "fast-glob": "^3.3.2", - "is-glob": "^4.0.3", - "minimatch": "^9.0.4", - "semver": "^7.6.0", - "ts-api-utils": "^2.1.0" + "@smithy/types": "^4.11.0" }, "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "typescript": ">=4.8.4 <6.0.0" + "node": ">=18.0.0" } }, - "node_modules/@typescript-eslint/typescript-estree/node_modules/minimatch": { - "version": "9.0.5", - "dev": true, - "license": "ISC", + "node_modules/@smithy/shared-ini-file-loader": { + "version": "4.4.2", + "resolved": "https://registry.npmjs.org/@smithy/shared-ini-file-loader/-/shared-ini-file-loader-4.4.2.tgz", + "integrity": "sha512-M7iUUff/KwfNunmrgtqBfvZSzh3bmFgv/j/t1Y1dQ+8dNo34br1cqVEqy6v0mYEgi0DkGO7Xig0AnuOaEGVlcg==", + "license": "Apache-2.0", "dependencies": { - "brace-expansion": "^2.0.1" + "@smithy/types": "^4.11.0", + "tslib": "^2.6.2" }, "engines": { - "node": ">=16 || 14 >=14.17" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" + "node": ">=18.0.0" } }, - "node_modules/@typescript-eslint/utils": { - "version": "8.46.2", - "dev": true, - "license": "MIT", + "node_modules/@smithy/signature-v4": { + "version": "5.3.7", + "resolved": "https://registry.npmjs.org/@smithy/signature-v4/-/signature-v4-5.3.7.tgz", + "integrity": "sha512-9oNUlqBlFZFOSdxgImA6X5GFuzE7V2H7VG/7E70cdLhidFbdtvxxt81EHgykGK5vq5D3FafH//X+Oy31j3CKOg==", + "license": "Apache-2.0", "dependencies": { - "@eslint-community/eslint-utils": "^4.7.0", - "@typescript-eslint/scope-manager": "8.46.2", - "@typescript-eslint/types": "8.46.2", - "@typescript-eslint/typescript-estree": "8.46.2" + "@smithy/is-array-buffer": "^4.2.0", + "@smithy/protocol-http": "^5.3.7", + "@smithy/types": "^4.11.0", + "@smithy/util-hex-encoding": "^4.2.0", + "@smithy/util-middleware": "^4.2.7", + "@smithy/util-uri-escape": "^4.2.0", + "@smithy/util-utf8": "^4.2.0", + "tslib": "^2.6.2" }, "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "eslint": "^8.57.0 || ^9.0.0", - "typescript": ">=4.8.4 <6.0.0" + "node": ">=18.0.0" } }, - "node_modules/@typescript-eslint/visitor-keys": { - "version": "8.46.2", - "dev": true, - "license": "MIT", + "node_modules/@smithy/smithy-client": { + "version": "4.10.2", + "resolved": "https://registry.npmjs.org/@smithy/smithy-client/-/smithy-client-4.10.2.tgz", + "integrity": "sha512-D5z79xQWpgrGpAHb054Fn2CCTQZpog7JELbVQ6XAvXs5MNKWf28U9gzSBlJkOyMl9LA1TZEjRtwvGXfP0Sl90g==", + "license": "Apache-2.0", "dependencies": { - "@typescript-eslint/types": "8.46.2", - "eslint-visitor-keys": "^4.2.1" + "@smithy/core": "^3.20.0", + "@smithy/middleware-endpoint": "^4.4.1", + "@smithy/middleware-stack": "^4.2.7", + "@smithy/protocol-http": "^5.3.7", + "@smithy/types": "^4.11.0", + "@smithy/util-stream": "^4.5.8", + "tslib": "^2.6.2" }, "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" + "node": ">=18.0.0" } }, - "node_modules/@typescript-eslint/visitor-keys/node_modules/eslint-visitor-keys": { - "version": "4.2.1", - "dev": true, + "node_modules/@smithy/types": { + "version": "4.11.0", + "resolved": "https://registry.npmjs.org/@smithy/types/-/types-4.11.0.tgz", + "integrity": "sha512-mlrmL0DRDVe3mNrjTcVcZEgkFmufITfUAPBEA+AHYiIeYyJebso/He1qLbP3PssRe22KUzLRpQSdBPbXdgZ2VA==", "license": "Apache-2.0", - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + "dependencies": { + "tslib": "^2.6.2" }, - "funding": { - "url": "https://opencollective.com/eslint" + "engines": { + "node": ">=18.0.0" } }, - "node_modules/@ungap/structured-clone": { - "version": "1.3.0", - "dev": true, - "license": "ISC" - }, - "node_modules/@unrs/resolver-binding-linux-x64-gnu": { - "version": "1.11.1", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@unrs/resolver-binding-linux-x64-musl": { - "version": "1.11.1", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@zeit/schemas": { - "version": "2.36.0", - "license": "MIT" - }, - "node_modules/abort-controller": { - "version": "3.0.0", - "dev": true, - "license": "MIT", + "node_modules/@smithy/url-parser": { + "version": "4.2.7", + "resolved": "https://registry.npmjs.org/@smithy/url-parser/-/url-parser-4.2.7.tgz", + "integrity": "sha512-/RLtVsRV4uY3qPWhBDsjwahAtt3x2IsMGnP5W1b2VZIe+qgCqkLxI1UOHDZp1Q1QSOrdOR32MF3Ph2JfWT1VHg==", + "license": "Apache-2.0", "dependencies": { - "event-target-shim": "^5.0.0" + "@smithy/querystring-parser": "^4.2.7", + "@smithy/types": "^4.11.0", + "tslib": "^2.6.2" }, "engines": { - "node": ">=6.5" + "node": ">=18.0.0" } }, - "node_modules/acorn": { - "version": "8.15.0", - "dev": true, - "license": "MIT", - "bin": { - "acorn": "bin/acorn" + "node_modules/@smithy/util-base64": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/@smithy/util-base64/-/util-base64-4.3.0.tgz", + "integrity": "sha512-GkXZ59JfyxsIwNTWFnjmFEI8kZpRNIBfxKjv09+nkAWPt/4aGaEWMM04m4sxgNVWkbt2MdSvE3KF/PfX4nFedQ==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/util-buffer-from": "^4.2.0", + "@smithy/util-utf8": "^4.2.0", + "tslib": "^2.6.2" }, "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/acorn-jsx": { - "version": "5.3.2", - "dev": true, - "license": "MIT", - "peerDependencies": { - "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" + "node": ">=18.0.0" } }, - "node_modules/acorn-walk": { - "version": "8.3.4", - "dev": true, - "license": "MIT", + "node_modules/@smithy/util-body-length-browser": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/@smithy/util-body-length-browser/-/util-body-length-browser-4.2.0.tgz", + "integrity": "sha512-Fkoh/I76szMKJnBXWPdFkQJl2r9SjPt3cMzLdOB6eJ4Pnpas8hVoWPYemX/peO0yrrvldgCUVJqOAjUrOLjbxg==", + "license": "Apache-2.0", "dependencies": { - "acorn": "^8.11.0" + "tslib": "^2.6.2" }, "engines": { - "node": ">=0.4.0" + "node": ">=18.0.0" } }, - "node_modules/agent-base": { - "version": "7.1.4", - "dev": true, - "license": "MIT", + "node_modules/@smithy/util-body-length-node": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/@smithy/util-body-length-node/-/util-body-length-node-4.2.1.tgz", + "integrity": "sha512-h53dz/pISVrVrfxV1iqXlx5pRg3V2YWFcSQyPyXZRrZoZj4R4DeWRDo1a7dd3CPTcFi3kE+98tuNyD2axyZReA==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, "engines": { - "node": ">= 14" + "node": ">=18.0.0" } }, - "node_modules/ajv": { - "version": "8.17.1", - "license": "MIT", + "node_modules/@smithy/util-buffer-from": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/@smithy/util-buffer-from/-/util-buffer-from-4.2.0.tgz", + "integrity": "sha512-kAY9hTKulTNevM2nlRtxAG2FQ3B2OR6QIrPY3zE5LqJy1oxzmgBGsHLWTcNhWXKchgA0WHW+mZkQrng/pgcCew==", + "license": "Apache-2.0", "dependencies": { - "fast-deep-equal": "^3.1.3", - "fast-uri": "^3.0.1", - "json-schema-traverse": "^1.0.0", - "require-from-string": "^2.0.2" + "@smithy/is-array-buffer": "^4.2.0", + "tslib": "^2.6.2" }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" + "engines": { + "node": ">=18.0.0" } }, - "node_modules/ansi-align": { - "version": "3.0.1", - "license": "ISC", + "node_modules/@smithy/util-config-provider": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/@smithy/util-config-provider/-/util-config-provider-4.2.0.tgz", + "integrity": "sha512-YEjpl6XJ36FTKmD+kRJJWYvrHeUvm5ykaUS5xK+6oXffQPHeEM4/nXlZPe+Wu0lsgRUcNZiliYNh/y7q9c2y6Q==", + "license": "Apache-2.0", "dependencies": { - "string-width": "^4.1.0" + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" } }, - "node_modules/ansi-colors": { - "version": "4.1.3", - "dev": true, - "license": "MIT", + "node_modules/@smithy/util-defaults-mode-browser": { + "version": "4.3.16", + "resolved": "https://registry.npmjs.org/@smithy/util-defaults-mode-browser/-/util-defaults-mode-browser-4.3.16.tgz", + "integrity": "sha512-/eiSP3mzY3TsvUOYMeL4EqUX6fgUOj2eUOU4rMMgVbq67TiRLyxT7Xsjxq0bW3OwuzK009qOwF0L2OgJqperAQ==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/property-provider": "^4.2.7", + "@smithy/smithy-client": "^4.10.2", + "@smithy/types": "^4.11.0", + "tslib": "^2.6.2" + }, "engines": { - "node": ">=6" + "node": ">=18.0.0" } }, - "node_modules/ansi-escapes": { - "version": "4.3.2", - "dev": true, - "license": "MIT", + "node_modules/@smithy/util-defaults-mode-node": { + "version": "4.2.19", + "resolved": "https://registry.npmjs.org/@smithy/util-defaults-mode-node/-/util-defaults-mode-node-4.2.19.tgz", + "integrity": "sha512-3a4+4mhf6VycEJyHIQLypRbiwG6aJvbQAeRAVXydMmfweEPnLLabRbdyo/Pjw8Rew9vjsh5WCdhmDaHkQnhhhA==", + "license": "Apache-2.0", "dependencies": { - "type-fest": "^0.21.3" + "@smithy/config-resolver": "^4.4.5", + "@smithy/credential-provider-imds": "^4.2.7", + "@smithy/node-config-provider": "^4.3.7", + "@smithy/property-provider": "^4.2.7", + "@smithy/smithy-client": "^4.10.2", + "@smithy/types": "^4.11.0", + "tslib": "^2.6.2" }, "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">=18.0.0" } }, - "node_modules/ansi-regex": { - "version": "5.0.1", - "license": "MIT", + "node_modules/@smithy/util-endpoints": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/@smithy/util-endpoints/-/util-endpoints-3.2.7.tgz", + "integrity": "sha512-s4ILhyAvVqhMDYREeTS68R43B1V5aenV5q/V1QpRQJkCXib5BPRo4s7uNdzGtIKxaPHCfU/8YkvPAEvTpxgspg==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/node-config-provider": "^4.3.7", + "@smithy/types": "^4.11.0", + "tslib": "^2.6.2" + }, "engines": { - "node": ">=8" + "node": ">=18.0.0" } }, - "node_modules/ansi-styles": { - "version": "4.3.0", - "license": "MIT", + "node_modules/@smithy/util-hex-encoding": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/@smithy/util-hex-encoding/-/util-hex-encoding-4.2.0.tgz", + "integrity": "sha512-CCQBwJIvXMLKxVbO88IukazJD9a4kQ9ZN7/UMGBjBcJYvatpWk+9g870El4cB8/EJxfe+k+y0GmR9CAzkF+Nbw==", + "license": "Apache-2.0", "dependencies": { - "color-convert": "^2.0.1" + "tslib": "^2.6.2" }, "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" + "node": ">=18.0.0" } }, - "node_modules/anymatch": { - "version": "3.1.3", - "dev": true, - "license": "ISC", + "node_modules/@smithy/util-middleware": { + "version": "4.2.7", + "resolved": "https://registry.npmjs.org/@smithy/util-middleware/-/util-middleware-4.2.7.tgz", + "integrity": "sha512-i1IkpbOae6NvIKsEeLLM9/2q4X+M90KV3oCFgWQI4q0Qz+yUZvsr+gZPdAEAtFhWQhAHpTsJO8DRJPuwVyln+w==", + "license": "Apache-2.0", "dependencies": { - "normalize-path": "^3.0.0", - "picomatch": "^2.0.4" + "@smithy/types": "^4.11.0", + "tslib": "^2.6.2" }, "engines": { - "node": ">= 8" + "node": ">=18.0.0" } }, - "node_modules/arch": { - "version": "2.2.0", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "MIT" - }, - "node_modules/archiver": { - "version": "7.0.1", - "dev": true, - "license": "MIT", + "node_modules/@smithy/util-retry": { + "version": "4.2.7", + "resolved": "https://registry.npmjs.org/@smithy/util-retry/-/util-retry-4.2.7.tgz", + "integrity": "sha512-SvDdsQyF5CIASa4EYVT02LukPHVzAgUA4kMAuZ97QJc2BpAqZfA4PINB8/KOoCXEw9tsuv/jQjMeaHFvxdLNGg==", + "license": "Apache-2.0", "dependencies": { - "archiver-utils": "^5.0.2", - "async": "^3.2.4", - "buffer-crc32": "^1.0.0", - "readable-stream": "^4.0.0", - "readdir-glob": "^1.1.2", - "tar-stream": "^3.0.0", - "zip-stream": "^6.0.1" + "@smithy/service-error-classification": "^4.2.7", + "@smithy/types": "^4.11.0", + "tslib": "^2.6.2" }, "engines": { - "node": ">= 14" + "node": ">=18.0.0" } }, - "node_modules/archiver-utils": { - "version": "5.0.2", - "dev": true, - "license": "MIT", + "node_modules/@smithy/util-stream": { + "version": "4.5.8", + "resolved": "https://registry.npmjs.org/@smithy/util-stream/-/util-stream-4.5.8.tgz", + "integrity": "sha512-ZnnBhTapjM0YPGUSmOs0Mcg/Gg87k503qG4zU2v/+Js2Gu+daKOJMeqcQns8ajepY8tgzzfYxl6kQyZKml6O2w==", + "license": "Apache-2.0", "dependencies": { - "glob": "^10.0.0", - "graceful-fs": "^4.2.0", - "is-stream": "^2.0.1", - "lazystream": "^1.0.0", - "lodash": "^4.17.15", - "normalize-path": "^3.0.0", - "readable-stream": "^4.0.0" + "@smithy/fetch-http-handler": "^5.3.8", + "@smithy/node-http-handler": "^4.4.7", + "@smithy/types": "^4.11.0", + "@smithy/util-base64": "^4.3.0", + "@smithy/util-buffer-from": "^4.2.0", + "@smithy/util-hex-encoding": "^4.2.0", + "@smithy/util-utf8": "^4.2.0", + "tslib": "^2.6.2" }, "engines": { - "node": ">= 14" + "node": ">=18.0.0" } }, - "node_modules/archiver-utils/node_modules/buffer": { - "version": "6.0.3", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "MIT", + "node_modules/@smithy/util-uri-escape": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/@smithy/util-uri-escape/-/util-uri-escape-4.2.0.tgz", + "integrity": "sha512-igZpCKV9+E/Mzrpq6YacdTQ0qTiLm85gD6N/IrmyDvQFA4UnU3d5g3m8tMT/6zG/vVkWSU+VxeUyGonL62DuxA==", + "license": "Apache-2.0", "dependencies": { - "base64-js": "^1.3.1", - "ieee754": "^1.2.1" + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" } }, - "node_modules/archiver-utils/node_modules/glob": { - "version": "10.4.5", - "dev": true, - "license": "ISC", + "node_modules/@smithy/util-utf8": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/@smithy/util-utf8/-/util-utf8-4.2.0.tgz", + "integrity": "sha512-zBPfuzoI8xyBtR2P6WQj63Rz8i3AmfAaJLuNG8dWsfvPe8lO4aCPYLn879mEgHndZH1zQ2oXmG8O1GGzzaoZiw==", + "license": "Apache-2.0", "dependencies": { - "foreground-child": "^3.1.0", - "jackspeak": "^3.1.2", - "minimatch": "^9.0.4", - "minipass": "^7.1.2", - "package-json-from-dist": "^1.0.0", - "path-scurry": "^1.11.1" - }, - "bin": { - "glob": "dist/esm/bin.mjs" + "@smithy/util-buffer-from": "^4.2.0", + "tslib": "^2.6.2" }, - "funding": { - "url": "https://github.com/sponsors/isaacs" + "engines": { + "node": ">=18.0.0" } }, - "node_modules/archiver-utils/node_modules/jackspeak": { - "version": "3.4.3", - "dev": true, - "license": "BlueOak-1.0.0", + "node_modules/@smithy/util-waiter": { + "version": "4.2.7", + "resolved": "https://registry.npmjs.org/@smithy/util-waiter/-/util-waiter-4.2.7.tgz", + "integrity": "sha512-vHJFXi9b7kUEpHWUCY3Twl+9NPOZvQ0SAi+Ewtn48mbiJk4JY9MZmKQjGB4SCvVb9WPiSphZJYY6RIbs+grrzw==", + "license": "Apache-2.0", "dependencies": { - "@isaacs/cliui": "^8.0.2" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" + "@smithy/abort-controller": "^4.2.7", + "@smithy/types": "^4.11.0", + "tslib": "^2.6.2" }, - "optionalDependencies": { - "@pkgjs/parseargs": "^0.11.0" + "engines": { + "node": ">=18.0.0" } }, - "node_modules/archiver-utils/node_modules/lru-cache": { - "version": "10.4.3", - "dev": true, - "license": "ISC" - }, - "node_modules/archiver-utils/node_modules/minimatch": { - "version": "9.0.5", - "dev": true, - "license": "ISC", + "node_modules/@smithy/uuid": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@smithy/uuid/-/uuid-1.1.0.tgz", + "integrity": "sha512-4aUIteuyxtBUhVdiQqcDhKFitwfd9hqoSDYY2KRXiWtgoWJ9Bmise+KfEPDiVHWeJepvF8xJO9/9+WDIciMFFw==", + "license": "Apache-2.0", "dependencies": { - "brace-expansion": "^2.0.1" + "tslib": "^2.6.2" }, "engines": { - "node": ">=16 || 14 >=14.17" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" + "node": ">=18.0.0" } }, - "node_modules/archiver-utils/node_modules/path-scurry": { - "version": "1.11.1", + "node_modules/@stoplight/better-ajv-errors": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@stoplight/better-ajv-errors/-/better-ajv-errors-1.0.3.tgz", + "integrity": "sha512-0p9uXkuB22qGdNfy3VeEhxkU5uwvp/KrBTAbrLBURv6ilxIVwanKwjMc41lQfIVgPGcOkmLbTolfFrSsueu7zA==", "dev": true, - "license": "BlueOak-1.0.0", + "license": "Apache-2.0", "dependencies": { - "lru-cache": "^10.2.0", - "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" + "jsonpointer": "^5.0.0", + "leven": "^3.1.0" }, "engines": { - "node": ">=16 || 14 >=14.18" + "node": "^12.20 || >= 14.13" }, - "funding": { - "url": "https://github.com/sponsors/isaacs" + "peerDependencies": { + "ajv": ">=8" } }, - "node_modules/archiver-utils/node_modules/readable-stream": { - "version": "4.7.0", + "node_modules/@stoplight/json": { + "version": "3.21.7", + "resolved": "https://registry.npmjs.org/@stoplight/json/-/json-3.21.7.tgz", + "integrity": "sha512-xcJXgKFqv/uCEgtGlPxy3tPA+4I+ZI4vAuMJ885+ThkTHFVkC+0Fm58lA9NlsyjnkpxFh4YiQWpH+KefHdbA0A==", "dev": true, - "license": "MIT", + "license": "Apache-2.0", "dependencies": { - "abort-controller": "^3.0.0", - "buffer": "^6.0.3", - "events": "^3.3.0", - "process": "^0.11.10", - "string_decoder": "^1.3.0" + "@stoplight/ordered-object-literal": "^1.0.3", + "@stoplight/path": "^1.3.2", + "@stoplight/types": "^13.6.0", + "jsonc-parser": "~2.2.1", + "lodash": "^4.17.21", + "safe-stable-stringify": "^1.1" }, "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + "node": ">=8.3.0" } }, - "node_modules/archiver/node_modules/buffer": { - "version": "6.0.3", + "node_modules/@stoplight/json-ref-readers": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/@stoplight/json-ref-readers/-/json-ref-readers-1.2.2.tgz", + "integrity": "sha512-nty0tHUq2f1IKuFYsLM4CXLZGHdMn+X/IwEUIpeSOXt0QjMUbL0Em57iJUDzz+2MkWG83smIigNZ3fauGjqgdQ==", "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "MIT", + "license": "Apache-2.0", "dependencies": { - "base64-js": "^1.3.1", - "ieee754": "^1.2.1" + "node-fetch": "^2.6.0", + "tslib": "^1.14.1" + }, + "engines": { + "node": ">=8.3.0" } }, - "node_modules/archiver/node_modules/readable-stream": { - "version": "4.7.0", + "node_modules/@stoplight/json-ref-readers/node_modules/tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", "dev": true, - "license": "MIT", + "license": "0BSD" + }, + "node_modules/@stoplight/json-ref-resolver": { + "version": "3.1.6", + "resolved": "https://registry.npmjs.org/@stoplight/json-ref-resolver/-/json-ref-resolver-3.1.6.tgz", + "integrity": "sha512-YNcWv3R3n3U6iQYBsFOiWSuRGE5su1tJSiX6pAPRVk7dP0L7lqCteXGzuVRQ0gMZqUl8v1P0+fAKxF6PLo9B5A==", + "dev": true, + "license": "Apache-2.0", "dependencies": { - "abort-controller": "^3.0.0", - "buffer": "^6.0.3", - "events": "^3.3.0", - "process": "^0.11.10", - "string_decoder": "^1.3.0" + "@stoplight/json": "^3.21.0", + "@stoplight/path": "^1.3.2", + "@stoplight/types": "^12.3.0 || ^13.0.0", + "@types/urijs": "^1.19.19", + "dependency-graph": "~0.11.0", + "fast-memoize": "^2.5.2", + "immer": "^9.0.6", + "lodash": "^4.17.21", + "tslib": "^2.6.0", + "urijs": "^1.19.11" }, "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + "node": ">=8.3.0" } }, - "node_modules/arg": { - "version": "5.0.2", + "node_modules/@stoplight/json/node_modules/jsonc-parser": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-2.2.1.tgz", + "integrity": "sha512-o6/yDBYccGvTz1+QFevz6l6OBZ2+fMVu2JZ9CIhzsYRX4mjaK5IyX9eldUdCmga16zlgQxyrj5pt9kzuj2C02w==", + "dev": true, "license": "MIT" }, - "node_modules/argparse": { - "version": "2.0.1", - "license": "Python-2.0" + "node_modules/@stoplight/json/node_modules/safe-stable-stringify": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/safe-stable-stringify/-/safe-stable-stringify-1.1.1.tgz", + "integrity": "sha512-ERq4hUjKDbJfE4+XtZLFPCDi8Vb1JqaxAPTxWFLBx8XcAlf9Bda/ZJdVezs/NAfsMQScyIlUMx+Yeu7P7rx5jw==", + "dev": true, + "license": "MIT" }, - "node_modules/aria-query": { - "version": "5.3.2", + "node_modules/@stoplight/ordered-object-literal": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/@stoplight/ordered-object-literal/-/ordered-object-literal-1.0.5.tgz", + "integrity": "sha512-COTiuCU5bgMUtbIFBuyyh2/yVVzlr5Om0v5utQDgBCuQUOPgU1DwoffkTfg4UBQOvByi5foF4w4T+H9CoRe5wg==", "dev": true, "license": "Apache-2.0", "engines": { - "node": ">= 0.4" + "node": ">=8" } }, - "node_modules/array-buffer-byte-length": { - "version": "1.0.2", + "node_modules/@stoplight/path": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/@stoplight/path/-/path-1.3.2.tgz", + "integrity": "sha512-lyIc6JUlUA8Ve5ELywPC8I2Sdnh1zc1zmbYgVarhXIp9YeAB0ReeqmGEOWNtlHkbP2DAA1AL65Wfn2ncjK/jtQ==", "dev": true, - "license": "MIT", - "dependencies": { - "call-bound": "^1.0.3", - "is-array-buffer": "^3.0.5" - }, + "license": "Apache-2.0", "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": ">=8" } }, - "node_modules/array-includes": { - "version": "3.1.9", + "node_modules/@stoplight/spectral-cli": { + "version": "6.15.0", + "resolved": "https://registry.npmjs.org/@stoplight/spectral-cli/-/spectral-cli-6.15.0.tgz", + "integrity": "sha512-FVeQIuqQQnnLfa8vy+oatTKUve7uU+3SaaAfdjpX/B+uB1NcfkKRJYhKT9wMEehDRaMPL5AKIRYMCFerdEbIpw==", "dev": true, - "license": "MIT", + "license": "Apache-2.0", "dependencies": { - "call-bind": "^1.0.8", - "call-bound": "^1.0.4", - "define-properties": "^1.2.1", - "es-abstract": "^1.24.0", - "es-object-atoms": "^1.1.1", - "get-intrinsic": "^1.3.0", - "is-string": "^1.1.1", - "math-intrinsics": "^1.1.0" + "@stoplight/json": "~3.21.0", + "@stoplight/path": "1.3.2", + "@stoplight/spectral-core": "^1.19.5", + "@stoplight/spectral-formatters": "^1.4.1", + "@stoplight/spectral-parsers": "^1.0.4", + "@stoplight/spectral-ref-resolver": "^1.0.4", + "@stoplight/spectral-ruleset-bundler": "^1.6.0", + "@stoplight/spectral-ruleset-migrator": "^1.11.0", + "@stoplight/spectral-rulesets": ">=1", + "@stoplight/spectral-runtime": "^1.1.2", + "@stoplight/types": "^13.6.0", + "chalk": "4.1.2", + "fast-glob": "~3.2.12", + "hpagent": "~1.2.0", + "lodash": "~4.17.21", + "pony-cause": "^1.1.1", + "stacktracey": "^2.1.8", + "tslib": "^2.8.1", + "yargs": "~17.7.2" }, - "engines": { - "node": ">= 0.4" + "bin": { + "spectral": "dist/index.js" }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "engines": { + "node": "^16.20 || ^18.18 || >= 20.17" } }, - "node_modules/array.prototype.findlast": { - "version": "1.2.5", + "node_modules/@stoplight/spectral-cli/node_modules/fast-glob": { + "version": "3.2.12", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.12.tgz", + "integrity": "sha512-DVj4CQIYYow0BlaelwK1pHl5n5cRSJfM60UA0zK891sVInoPri2Ekj7+e1CT3/3qxXenpI+nBBmQAcJPJgaj4w==", "dev": true, "license": "MIT", "dependencies": { - "call-bind": "^1.0.7", - "define-properties": "^1.2.1", - "es-abstract": "^1.23.2", - "es-errors": "^1.3.0", - "es-object-atoms": "^1.0.0", - "es-shim-unscopables": "^1.0.2" + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.4" }, "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": ">=8.6.0" } }, - "node_modules/array.prototype.findlastindex": { - "version": "1.2.6", + "node_modules/@stoplight/spectral-core": { + "version": "1.20.0", + "resolved": "https://registry.npmjs.org/@stoplight/spectral-core/-/spectral-core-1.20.0.tgz", + "integrity": "sha512-5hBP81nCC1zn1hJXL/uxPNRKNcB+/pEIHgCjPRpl/w/qy9yC9ver04tw1W0l/PMiv0UeB5dYgozXVQ4j5a6QQQ==", "dev": true, - "license": "MIT", + "license": "Apache-2.0", "dependencies": { - "call-bind": "^1.0.8", - "call-bound": "^1.0.4", - "define-properties": "^1.2.1", - "es-abstract": "^1.23.9", - "es-errors": "^1.3.0", - "es-object-atoms": "^1.1.1", - "es-shim-unscopables": "^1.1.0" + "@stoplight/better-ajv-errors": "1.0.3", + "@stoplight/json": "~3.21.0", + "@stoplight/path": "1.3.2", + "@stoplight/spectral-parsers": "^1.0.0", + "@stoplight/spectral-ref-resolver": "^1.0.4", + "@stoplight/spectral-runtime": "^1.1.2", + "@stoplight/types": "~13.6.0", + "@types/es-aggregate-error": "^1.0.2", + "@types/json-schema": "^7.0.11", + "ajv": "^8.17.1", + "ajv-errors": "~3.0.0", + "ajv-formats": "~2.1.1", + "es-aggregate-error": "^1.0.7", + "jsonpath-plus": "^10.3.0", + "lodash": "~4.17.21", + "lodash.topath": "^4.5.2", + "minimatch": "3.1.2", + "nimma": "0.2.3", + "pony-cause": "^1.1.1", + "simple-eval": "1.0.1", + "tslib": "^2.8.1" }, "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": "^16.20 || ^18.18 || >= 20.17" } }, - "node_modules/array.prototype.flat": { - "version": "1.3.3", + "node_modules/@stoplight/spectral-core/node_modules/@stoplight/types": { + "version": "13.6.0", + "resolved": "https://registry.npmjs.org/@stoplight/types/-/types-13.6.0.tgz", + "integrity": "sha512-dzyuzvUjv3m1wmhPfq82lCVYGcXG0xUYgqnWfCq3PCVR4BKFhjdkHrnJ+jIDoMKvXb05AZP/ObQF6+NpDo29IQ==", "dev": true, - "license": "MIT", + "license": "Apache-2.0", "dependencies": { - "call-bind": "^1.0.8", - "define-properties": "^1.2.1", - "es-abstract": "^1.23.5", - "es-shim-unscopables": "^1.0.2" + "@types/json-schema": "^7.0.4", + "utility-types": "^3.10.0" }, "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": "^12.20 || >=14.13" } }, - "node_modules/array.prototype.flatmap": { - "version": "1.3.3", + "node_modules/@stoplight/spectral-core/node_modules/ajv-formats": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-2.1.1.tgz", + "integrity": "sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==", "dev": true, "license": "MIT", "dependencies": { - "call-bind": "^1.0.8", - "define-properties": "^1.2.1", - "es-abstract": "^1.23.5", - "es-shim-unscopables": "^1.0.2" + "ajv": "^8.0.0" }, - "engines": { - "node": ">= 0.4" + "peerDependencies": { + "ajv": "^8.0.0" }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "peerDependenciesMeta": { + "ajv": { + "optional": true + } } }, - "node_modules/array.prototype.tosorted": { - "version": "1.1.4", + "node_modules/@stoplight/spectral-core/node_modules/brace-expansion": { + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", + "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", "dev": true, "license": "MIT", "dependencies": { - "call-bind": "^1.0.7", - "define-properties": "^1.2.1", - "es-abstract": "^1.23.3", - "es-errors": "^1.3.0", - "es-shim-unscopables": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" } }, - "node_modules/arraybuffer.prototype.slice": { - "version": "1.0.4", + "node_modules/@stoplight/spectral-core/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", "dev": true, - "license": "MIT", + "license": "ISC", "dependencies": { - "array-buffer-byte-length": "^1.0.1", - "call-bind": "^1.0.8", - "define-properties": "^1.2.1", - "es-abstract": "^1.23.5", - "es-errors": "^1.3.0", - "get-intrinsic": "^1.2.6", - "is-array-buffer": "^3.0.4" + "brace-expansion": "^1.1.7" }, "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/asn1": { - "version": "0.2.6", - "dev": true, - "license": "MIT", - "dependencies": { - "safer-buffer": "~2.1.0" + "node": "*" } }, - "node_modules/ast-types": { - "version": "0.13.4", + "node_modules/@stoplight/spectral-formats": { + "version": "1.8.2", + "resolved": "https://registry.npmjs.org/@stoplight/spectral-formats/-/spectral-formats-1.8.2.tgz", + "integrity": "sha512-c06HB+rOKfe7tuxg0IdKDEA5XnjL2vrn/m/OVIIxtINtBzphZrOgtRn7epQ5bQF5SWp84Ue7UJWaGgDwVngMFw==", "dev": true, - "license": "MIT", + "license": "Apache-2.0", "dependencies": { - "tslib": "^2.0.1" + "@stoplight/json": "^3.17.0", + "@stoplight/spectral-core": "^1.19.2", + "@types/json-schema": "^7.0.7", + "tslib": "^2.8.1" }, "engines": { - "node": ">=4" - } - }, - "node_modules/ast-types-flow": { - "version": "0.0.8", - "dev": true, - "license": "MIT" - }, - "node_modules/async": { - "version": "3.2.6", - "dev": true, - "license": "MIT" - }, - "node_modules/async-function": { - "version": "1.0.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.4" + "node": "^16.20 || ^18.18 || >= 20.17" } }, - "node_modules/async-lock": { - "version": "1.4.1", - "dev": true, - "license": "MIT" - }, - "node_modules/asynckit": { - "version": "0.4.0", + "node_modules/@stoplight/spectral-formatters": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@stoplight/spectral-formatters/-/spectral-formatters-1.5.0.tgz", + "integrity": "sha512-lR7s41Z00Mf8TdXBBZQ3oi2uR8wqAtR6NO0KA8Ltk4FSpmAy0i6CKUmJG9hZQjanTnGmwpQkT/WP66p1GY3iXA==", "dev": true, - "license": "MIT" - }, - "node_modules/atomic-sleep": { - "version": "1.0.0", - "license": "MIT", + "license": "Apache-2.0", + "dependencies": { + "@stoplight/path": "^1.3.2", + "@stoplight/spectral-core": "^1.19.4", + "@stoplight/spectral-runtime": "^1.1.2", + "@stoplight/types": "^13.15.0", + "@types/markdown-escape": "^1.1.3", + "chalk": "4.1.2", + "cliui": "7.0.4", + "lodash": "^4.17.21", + "markdown-escape": "^2.0.0", + "node-sarif-builder": "^2.0.3", + "strip-ansi": "6.0", + "text-table": "^0.2.0", + "tslib": "^2.8.1" + }, "engines": { - "node": ">=8.0.0" + "node": "^16.20 || ^18.18 || >= 20.17" } }, - "node_modules/available-typed-arrays": { - "version": "1.0.7", + "node_modules/@stoplight/spectral-formatters/node_modules/cliui": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", + "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^7.0.0" + } + }, + "node_modules/@stoplight/spectral-formatters/node_modules/wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", "dev": true, "license": "MIT", "dependencies": { - "possible-typed-array-names": "^1.0.0" + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" }, "engines": { - "node": ">= 0.4" + "node": ">=10" }, "funding": { - "url": "https://github.com/sponsors/ljharb" + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" } }, - "node_modules/axe-core": { - "version": "4.10.3", + "node_modules/@stoplight/spectral-functions": { + "version": "1.10.1", + "resolved": "https://registry.npmjs.org/@stoplight/spectral-functions/-/spectral-functions-1.10.1.tgz", + "integrity": "sha512-obu8ZfoHxELOapfGsCJixKZXZcffjg+lSoNuttpmUFuDzVLT3VmH8QkPXfOGOL5Pz80BR35ClNAToDkdnYIURg==", "dev": true, - "license": "MPL-2.0", + "license": "Apache-2.0", + "dependencies": { + "@stoplight/better-ajv-errors": "1.0.3", + "@stoplight/json": "^3.17.1", + "@stoplight/spectral-core": "^1.19.4", + "@stoplight/spectral-formats": "^1.8.1", + "@stoplight/spectral-runtime": "^1.1.2", + "ajv": "^8.17.1", + "ajv-draft-04": "~1.0.0", + "ajv-errors": "~3.0.0", + "ajv-formats": "~2.1.1", + "lodash": "~4.17.21", + "tslib": "^2.8.1" + }, "engines": { - "node": ">=4" + "node": "^16.20 || ^18.18 || >= 20.17" } }, - "node_modules/axios": { - "version": "1.12.2", + "node_modules/@stoplight/spectral-functions/node_modules/ajv-formats": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-2.1.1.tgz", + "integrity": "sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==", "dev": true, "license": "MIT", "dependencies": { - "follow-redirects": "^1.15.6", - "form-data": "^4.0.4", - "proxy-from-env": "^1.1.0" + "ajv": "^8.0.0" + }, + "peerDependencies": { + "ajv": "^8.0.0" + }, + "peerDependenciesMeta": { + "ajv": { + "optional": true + } } }, - "node_modules/axobject-query": { - "version": "4.1.0", + "node_modules/@stoplight/spectral-parsers": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/@stoplight/spectral-parsers/-/spectral-parsers-1.0.5.tgz", + "integrity": "sha512-ANDTp2IHWGvsQDAY85/jQi9ZrF4mRrA5bciNHX+PUxPr4DwS6iv4h+FVWJMVwcEYdpyoIdyL+SRmHdJfQEPmwQ==", "dev": true, "license": "Apache-2.0", + "dependencies": { + "@stoplight/json": "~3.21.0", + "@stoplight/types": "^14.1.1", + "@stoplight/yaml": "~4.3.0", + "tslib": "^2.8.1" + }, "engines": { - "node": ">= 0.4" + "node": "^16.20 || ^18.18 || >= 20.17" } }, - "node_modules/b4a": { - "version": "1.6.7", - "dev": true, - "license": "Apache-2.0" - }, - "node_modules/babel-jest": { - "version": "30.2.0", + "node_modules/@stoplight/spectral-parsers/node_modules/@stoplight/types": { + "version": "14.1.1", + "resolved": "https://registry.npmjs.org/@stoplight/types/-/types-14.1.1.tgz", + "integrity": "sha512-/kjtr+0t0tjKr+heVfviO9FrU/uGLc+QNX3fHJc19xsCNYqU7lVhaXxDmEID9BZTjG+/r9pK9xP/xU02XGg65g==", "dev": true, - "license": "MIT", + "license": "Apache-2.0", "dependencies": { - "@jest/transform": "30.2.0", - "@types/babel__core": "^7.20.5", - "babel-plugin-istanbul": "^7.0.1", - "babel-preset-jest": "30.2.0", - "chalk": "^4.1.2", - "graceful-fs": "^4.2.11", - "slash": "^3.0.0" + "@types/json-schema": "^7.0.4", + "utility-types": "^3.10.0" }, "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - }, - "peerDependencies": { - "@babel/core": "^7.11.0 || ^8.0.0-0" + "node": "^12.20 || >=14.13" } }, - "node_modules/babel-plugin-istanbul": { - "version": "7.0.1", + "node_modules/@stoplight/spectral-ref-resolver": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/@stoplight/spectral-ref-resolver/-/spectral-ref-resolver-1.0.5.tgz", + "integrity": "sha512-gj3TieX5a9zMW29z3mBlAtDOCgN3GEc1VgZnCVlr5irmR4Qi5LuECuFItAq4pTn5Zu+sW5bqutsCH7D4PkpyAA==", "dev": true, - "license": "BSD-3-Clause", - "workspaces": [ - "test/babel-8" - ], + "license": "Apache-2.0", "dependencies": { - "@babel/helper-plugin-utils": "^7.0.0", - "@istanbuljs/load-nyc-config": "^1.0.0", - "@istanbuljs/schema": "^0.1.3", - "istanbul-lib-instrument": "^6.0.2", - "test-exclude": "^6.0.0" + "@stoplight/json-ref-readers": "1.2.2", + "@stoplight/json-ref-resolver": "~3.1.6", + "@stoplight/spectral-runtime": "^1.1.2", + "dependency-graph": "0.11.0", + "tslib": "^2.8.1" }, "engines": { - "node": ">=12" + "node": "^16.20 || ^18.18 || >= 20.17" } }, - "node_modules/babel-plugin-jest-hoist": { - "version": "30.2.0", + "node_modules/@stoplight/spectral-ruleset-bundler": { + "version": "1.6.3", + "resolved": "https://registry.npmjs.org/@stoplight/spectral-ruleset-bundler/-/spectral-ruleset-bundler-1.6.3.tgz", + "integrity": "sha512-AQFRO6OCKg8SZJUupnr3+OzI1LrMieDTEUHsYgmaRpNiDRPvzImE3bzM1KyQg99q58kTQyZ8kpr7sG8Lp94RRA==", "dev": true, - "license": "MIT", + "license": "Apache-2.0", "dependencies": { - "@types/babel__core": "^7.20.5" + "@rollup/plugin-commonjs": "~22.0.2", + "@stoplight/path": "1.3.2", + "@stoplight/spectral-core": ">=1", + "@stoplight/spectral-formats": "^1.8.1", + "@stoplight/spectral-functions": ">=1", + "@stoplight/spectral-parsers": ">=1", + "@stoplight/spectral-ref-resolver": "^1.0.4", + "@stoplight/spectral-ruleset-migrator": "^1.9.6", + "@stoplight/spectral-rulesets": ">=1", + "@stoplight/spectral-runtime": "^1.1.2", + "@stoplight/types": "^13.6.0", + "@types/node": "*", + "pony-cause": "1.1.1", + "rollup": "~2.79.2", + "tslib": "^2.8.1", + "validate-npm-package-name": "3.0.0" }, "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + "node": "^16.20 || ^18.18 || >= 20.17" } }, - "node_modules/babel-preset-current-node-syntax": { - "version": "1.2.0", + "node_modules/@stoplight/spectral-ruleset-migrator": { + "version": "1.11.3", + "resolved": "https://registry.npmjs.org/@stoplight/spectral-ruleset-migrator/-/spectral-ruleset-migrator-1.11.3.tgz", + "integrity": "sha512-+9Y1zFxYmSsneT5FPkgS1IlRQs0VgtdMT77f5xf6vzje9ezyhfs7oXwbZOCSZjEJew8iVZBKQtiOFndcBrdtqg==", "dev": true, - "license": "MIT", + "license": "Apache-2.0", "dependencies": { - "@babel/plugin-syntax-async-generators": "^7.8.4", - "@babel/plugin-syntax-bigint": "^7.8.3", - "@babel/plugin-syntax-class-properties": "^7.12.13", - "@babel/plugin-syntax-class-static-block": "^7.14.5", - "@babel/plugin-syntax-import-attributes": "^7.24.7", - "@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", - "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3", - "@babel/plugin-syntax-numeric-separator": "^7.10.4", - "@babel/plugin-syntax-object-rest-spread": "^7.8.3", - "@babel/plugin-syntax-optional-catch-binding": "^7.8.3", - "@babel/plugin-syntax-optional-chaining": "^7.8.3", - "@babel/plugin-syntax-private-property-in-object": "^7.14.5", - "@babel/plugin-syntax-top-level-await": "^7.14.5" + "@stoplight/json": "~3.21.0", + "@stoplight/ordered-object-literal": "~1.0.4", + "@stoplight/path": "1.3.2", + "@stoplight/spectral-functions": "^1.9.1", + "@stoplight/spectral-runtime": "^1.1.2", + "@stoplight/types": "^13.6.0", + "@stoplight/yaml": "~4.2.3", + "@types/node": "*", + "ajv": "^8.17.1", + "ast-types": "0.14.2", + "astring": "^1.9.0", + "reserved": "0.1.2", + "tslib": "^2.8.1", + "validate-npm-package-name": "3.0.0" }, - "peerDependencies": { - "@babel/core": "^7.0.0 || ^8.0.0-0" + "engines": { + "node": "^16.20 || ^18.18 || >= 20.17" } }, - "node_modules/babel-preset-jest": { - "version": "30.2.0", + "node_modules/@stoplight/spectral-ruleset-migrator/node_modules/@stoplight/yaml": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/@stoplight/yaml/-/yaml-4.2.3.tgz", + "integrity": "sha512-Mx01wjRAR9C7yLMUyYFTfbUf5DimEpHMkRDQ1PKLe9dfNILbgdxyrncsOXM3vCpsQ1Hfj4bPiGl+u4u6e9Akqw==", "dev": true, - "license": "MIT", + "license": "Apache-2.0", "dependencies": { - "babel-plugin-jest-hoist": "30.2.0", - "babel-preset-current-node-syntax": "^1.2.0" + "@stoplight/ordered-object-literal": "^1.0.1", + "@stoplight/types": "^13.0.0", + "@stoplight/yaml-ast-parser": "0.0.48", + "tslib": "^2.2.0" }, "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - }, - "peerDependencies": { - "@babel/core": "^7.11.0 || ^8.0.0-beta.1" + "node": ">=10.8" } }, - "node_modules/balanced-match": { - "version": "1.0.2", - "license": "MIT" + "node_modules/@stoplight/spectral-ruleset-migrator/node_modules/@stoplight/yaml-ast-parser": { + "version": "0.0.48", + "resolved": "https://registry.npmjs.org/@stoplight/yaml-ast-parser/-/yaml-ast-parser-0.0.48.tgz", + "integrity": "sha512-sV+51I7WYnLJnKPn2EMWgS4EUfoP4iWEbrWwbXsj0MZCB/xOK8j6+C9fntIdOM50kpx45ZLC3s6kwKivWuqvyg==", + "dev": true, + "license": "Apache-2.0" }, - "node_modules/bare-events": { - "version": "2.6.1", + "node_modules/@stoplight/spectral-ruleset-migrator/node_modules/ast-types": { + "version": "0.14.2", + "resolved": "https://registry.npmjs.org/ast-types/-/ast-types-0.14.2.tgz", + "integrity": "sha512-O0yuUDnZeQDL+ncNGlJ78BiO4jnYI3bvMsD5prT0/nsgijG/LpNBIr63gTjVTNsiGkgQhiyCShTgxt8oXOrklA==", "dev": true, - "license": "Apache-2.0", - "optional": true + "license": "MIT", + "dependencies": { + "tslib": "^2.0.1" + }, + "engines": { + "node": ">=4" + } }, - "node_modules/bare-fs": { - "version": "4.2.0", + "node_modules/@stoplight/spectral-rulesets": { + "version": "1.22.0", + "resolved": "https://registry.npmjs.org/@stoplight/spectral-rulesets/-/spectral-rulesets-1.22.0.tgz", + "integrity": "sha512-l2EY2jiKKLsvnPfGy+pXC0LeGsbJzcQP5G/AojHgf+cwN//VYxW1Wvv4WKFx/CLmLxc42mJYF2juwWofjWYNIQ==", "dev": true, "license": "Apache-2.0", - "optional": true, "dependencies": { - "bare-events": "^2.5.4", - "bare-path": "^3.0.0", - "bare-stream": "^2.6.4" + "@asyncapi/specs": "^6.8.0", + "@stoplight/better-ajv-errors": "1.0.3", + "@stoplight/json": "^3.17.0", + "@stoplight/spectral-core": "^1.19.4", + "@stoplight/spectral-formats": "^1.8.1", + "@stoplight/spectral-functions": "^1.9.1", + "@stoplight/spectral-runtime": "^1.1.2", + "@stoplight/types": "^13.6.0", + "@types/json-schema": "^7.0.7", + "ajv": "^8.17.1", + "ajv-formats": "~2.1.1", + "json-schema-traverse": "^1.0.0", + "leven": "3.1.0", + "lodash": "~4.17.21", + "tslib": "^2.8.1" }, "engines": { - "bare": ">=1.16.0" + "node": "^16.20 || ^18.18 || >= 20.17" + } + }, + "node_modules/@stoplight/spectral-rulesets/node_modules/ajv-formats": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-2.1.1.tgz", + "integrity": "sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ajv": "^8.0.0" }, "peerDependencies": { - "bare-buffer": "*" + "ajv": "^8.0.0" }, "peerDependenciesMeta": { - "bare-buffer": { + "ajv": { "optional": true } } }, - "node_modules/bare-os": { - "version": "3.6.1", + "node_modules/@stoplight/spectral-runtime": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/@stoplight/spectral-runtime/-/spectral-runtime-1.1.4.tgz", + "integrity": "sha512-YHbhX3dqW0do6DhiPSgSGQzr6yQLlWybhKwWx0cqxjMwxej3TqLv3BXMfIUYFKKUqIwH4Q2mV8rrMM8qD2N0rQ==", "dev": true, "license": "Apache-2.0", - "optional": true, + "dependencies": { + "@stoplight/json": "^3.20.1", + "@stoplight/path": "^1.3.2", + "@stoplight/types": "^13.6.0", + "abort-controller": "^3.0.0", + "lodash": "^4.17.21", + "node-fetch": "^2.7.0", + "tslib": "^2.8.1" + }, "engines": { - "bare": ">=1.14.0" + "node": "^16.20 || ^18.18 || >= 20.17" } }, - "node_modules/bare-path": { - "version": "3.0.0", + "node_modules/@stoplight/types": { + "version": "13.20.0", + "resolved": "https://registry.npmjs.org/@stoplight/types/-/types-13.20.0.tgz", + "integrity": "sha512-2FNTv05If7ib79VPDA/r9eUet76jewXFH2y2K5vuge6SXbRHtWBhcaRmu+6QpF4/WRNoJj5XYRSwLGXDxysBGA==", "dev": true, "license": "Apache-2.0", - "optional": true, "dependencies": { - "bare-os": "^3.0.1" + "@types/json-schema": "^7.0.4", + "utility-types": "^3.10.0" + }, + "engines": { + "node": "^12.20 || >=14.13" } }, - "node_modules/bare-stream": { - "version": "2.7.0", + "node_modules/@stoplight/yaml": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/@stoplight/yaml/-/yaml-4.3.0.tgz", + "integrity": "sha512-JZlVFE6/dYpP9tQmV0/ADfn32L9uFarHWxfcRhReKUnljz1ZiUM5zpX+PH8h5CJs6lao3TuFqnPm9IJJCEkE2w==", "dev": true, "license": "Apache-2.0", - "optional": true, "dependencies": { - "streamx": "^2.21.0" + "@stoplight/ordered-object-literal": "^1.0.5", + "@stoplight/types": "^14.1.1", + "@stoplight/yaml-ast-parser": "0.0.50", + "tslib": "^2.2.0" + }, + "engines": { + "node": ">=10.8" + } + }, + "node_modules/@stoplight/yaml-ast-parser": { + "version": "0.0.50", + "resolved": "https://registry.npmjs.org/@stoplight/yaml-ast-parser/-/yaml-ast-parser-0.0.50.tgz", + "integrity": "sha512-Pb6M8TDO9DtSVla9yXSTAxmo9GVEouq5P40DWXdOie69bXogZTkgvopCq+yEvTMA0F6PEvdJmbtTV3ccIp11VQ==", + "dev": true, + "license": "Apache-2.0" + }, + "node_modules/@stoplight/yaml/node_modules/@stoplight/types": { + "version": "14.1.1", + "resolved": "https://registry.npmjs.org/@stoplight/types/-/types-14.1.1.tgz", + "integrity": "sha512-/kjtr+0t0tjKr+heVfviO9FrU/uGLc+QNX3fHJc19xsCNYqU7lVhaXxDmEID9BZTjG+/r9pK9xP/xU02XGg65g==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@types/json-schema": "^7.0.4", + "utility-types": "^3.10.0" + }, + "engines": { + "node": "^12.20 || >=14.13" + } + }, + "node_modules/@stylistic/eslint-plugin": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@stylistic/eslint-plugin/-/eslint-plugin-3.1.0.tgz", + "integrity": "sha512-pA6VOrOqk0+S8toJYhQGv2MWpQQR0QpeUo9AhNkC49Y26nxBQ/nH1rta9bUU1rPw2fJ1zZEMV5oCX5AazT7J2g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/utils": "^8.13.0", + "eslint-visitor-keys": "^4.2.0", + "espree": "^10.3.0", + "estraverse": "^5.3.0", + "picomatch": "^4.0.2" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "peerDependencies": { - "bare-buffer": "*", - "bare-events": "*" + "eslint": ">=8.40.0" + } + }, + "node_modules/@tokenizer/inflate": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/@tokenizer/inflate/-/inflate-0.3.1.tgz", + "integrity": "sha512-4oeoZEBQdLdt5WmP/hx1KZ6D3/Oid/0cUb2nk4F0pTDAWy+KCH3/EnAkZF/bvckWo8I33EqBm01lIPgmgc8rCA==", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "^4.4.1", + "fflate": "^0.8.2", + "token-types": "^6.0.0" }, - "peerDependenciesMeta": { - "bare-buffer": { - "optional": true - }, - "bare-events": { - "optional": true - } + "engines": { + "node": ">=18" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Borewit" } }, - "node_modules/base64-js": { - "version": "1.5.1", + "node_modules/@tokenizer/token": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/@tokenizer/token/-/token-0.3.0.tgz", + "integrity": "sha512-OvjF+z51L3ov0OyAU0duzsYuvO01PH7x4t6DJx+guahgTnBHkhJdG7soQeTSFLWN3efnHyibZ4Z8l2EuWwJN3A==", "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], "license": "MIT" }, - "node_modules/baseline-browser-mapping": { - "version": "2.8.20", + "node_modules/@tootallnate/quickjs-emscripten": { + "version": "0.23.0", + "resolved": "https://registry.npmjs.org/@tootallnate/quickjs-emscripten/-/quickjs-emscripten-0.23.0.tgz", + "integrity": "sha512-C5Mc6rdnsaJDjO3UpGW/CQTHtCKaYlScZTly4JIu97Jxo/odCiH0ITnDXSJPTOrEKk/ycSZ0AOgTmkDtkOsvIA==", "dev": true, - "license": "Apache-2.0", - "bin": { - "baseline-browser-mapping": "dist/cli.js" - } + "license": "MIT" }, - "node_modules/basic-ftp": { - "version": "5.0.5", + "node_modules/@tsconfig/node10": { + "version": "1.0.12", + "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.12.tgz", + "integrity": "sha512-UCYBaeFvM11aU2y3YPZ//O5Rhj+xKyzy7mvcIoAjASbigy8mHMryP5cK7dgjlz2hWxh1g5pLw084E0a/wlUSFQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@tsconfig/node12": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.11.tgz", + "integrity": "sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==", "dev": true, - "license": "MIT", - "engines": { - "node": ">=10.0.0" - } + "license": "MIT" }, - "node_modules/bcrypt-pbkdf": { - "version": "1.0.2", + "node_modules/@tsconfig/node14": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.3.tgz", + "integrity": "sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==", "dev": true, - "license": "BSD-3-Clause", - "dependencies": { - "tweetnacl": "^0.14.3" - } + "license": "MIT" }, - "node_modules/better-ajv-errors": { - "version": "1.2.0", + "node_modules/@tsconfig/node16": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.4.tgz", + "integrity": "sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==", "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@babel/code-frame": "^7.16.0", - "@humanwhocodes/momoa": "^2.0.2", - "chalk": "^4.1.2", - "jsonpointer": "^5.0.0", - "leven": "^3.1.0 < 4" - }, - "engines": { - "node": ">= 12.13.0" - }, - "peerDependencies": { - "ajv": "4.11.8 - 8" - } + "license": "MIT" }, - "node_modules/binary-extensions": { - "version": "2.3.0", + "node_modules/@tsconfig/node22": { + "version": "22.0.5", + "resolved": "https://registry.npmjs.org/@tsconfig/node22/-/node22-22.0.5.tgz", + "integrity": "sha512-hLf2ld+sYN/BtOJjHUWOk568dvjFQkHnLNa6zce25GIH+vxKfvTgm3qpaH6ToF5tu/NN0IH66s+Bb5wElHrLcw==", "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } + "license": "MIT" }, - "node_modules/bl": { - "version": "4.1.0", + "node_modules/@tybys/wasm-util": { + "version": "0.10.1", + "resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.10.1.tgz", + "integrity": "sha512-9tTaPJLSiejZKx+Bmog4uSubteqTvFrVrURwkmHixBo0G4seD0zUxp98E1DzUBJxLQ3NPwXrGKDiVjwx/DpPsg==", "dev": true, "license": "MIT", + "optional": true, "dependencies": { - "buffer": "^5.5.0", - "inherits": "^2.0.4", - "readable-stream": "^3.4.0" + "tslib": "^2.4.0" } }, - "node_modules/bowser": { - "version": "2.12.0", + "node_modules/@types/aws-lambda": { + "version": "8.10.159", + "resolved": "https://registry.npmjs.org/@types/aws-lambda/-/aws-lambda-8.10.159.tgz", + "integrity": "sha512-SAP22WSGNN12OQ8PlCzGzRCZ7QDCwI85dQZbmpz7+mAk+L7j+wI7qnvmdKh+o7A5LaOp6QnOZ2NJphAZQTTHQg==", + "dev": true, "license": "MIT" }, - "node_modules/boxen": { - "version": "7.0.0", + "node_modules/@types/babel__core": { + "version": "7.20.5", + "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz", + "integrity": "sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==", + "dev": true, "license": "MIT", "dependencies": { - "ansi-align": "^3.0.1", - "camelcase": "^7.0.0", - "chalk": "^5.0.1", - "cli-boxes": "^3.0.0", - "string-width": "^5.1.2", - "type-fest": "^2.13.0", - "widest-line": "^4.0.1", - "wrap-ansi": "^8.0.1" - }, - "engines": { - "node": ">=14.16" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "@babel/parser": "^7.20.7", + "@babel/types": "^7.20.7", + "@types/babel__generator": "*", + "@types/babel__template": "*", + "@types/babel__traverse": "*" } }, - "node_modules/boxen/node_modules/ansi-regex": { - "version": "6.1.0", + "node_modules/@types/babel__generator": { + "version": "7.27.0", + "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.27.0.tgz", + "integrity": "sha512-ufFd2Xi92OAVPYsy+P4n7/U7e68fex0+Ee8gSG9KX7eo084CWiQ4sdxktvdl0bOPupXtVJPY19zk6EwWqUQ8lg==", + "dev": true, "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/ansi-regex?sponsor=1" + "dependencies": { + "@babel/types": "^7.0.0" } }, - "node_modules/boxen/node_modules/ansi-styles": { - "version": "6.2.1", + "node_modules/@types/babel__template": { + "version": "7.4.4", + "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.4.tgz", + "integrity": "sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==", + "dev": true, "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" + "dependencies": { + "@babel/parser": "^7.1.0", + "@babel/types": "^7.0.0" } }, - "node_modules/boxen/node_modules/camelcase": { - "version": "7.0.1", + "node_modules/@types/babel__traverse": { + "version": "7.28.0", + "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.28.0.tgz", + "integrity": "sha512-8PvcXf70gTDZBgt9ptxJ8elBeBjcLOAcOtoO/mPJjtji1+CdGbHgm77om1GrsPxsiE+uXIpNSK64UYaIwQXd4Q==", + "dev": true, "license": "MIT", - "engines": { - "node": ">=14.16" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "dependencies": { + "@babel/types": "^7.28.2" } }, - "node_modules/boxen/node_modules/chalk": { - "version": "5.5.0", + "node_modules/@types/docker-modem": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/@types/docker-modem/-/docker-modem-3.0.6.tgz", + "integrity": "sha512-yKpAGEuKRSS8wwx0joknWxsmLha78wNMe9R2S3UNsVOkZded8UqOrV8KoeDXoXsjndxwyF3eIhyClGbO1SEhEg==", + "dev": true, "license": "MIT", - "engines": { - "node": "^12.17.0 || ^14.13 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" + "dependencies": { + "@types/node": "*", + "@types/ssh2": "*" } }, - "node_modules/boxen/node_modules/string-width": { - "version": "5.1.2", + "node_modules/@types/dockerode": { + "version": "3.3.47", + "resolved": "https://registry.npmjs.org/@types/dockerode/-/dockerode-3.3.47.tgz", + "integrity": "sha512-ShM1mz7rCjdssXt7Xz0u1/R2BJC7piWa3SJpUBiVjCf2A3XNn4cP6pUVaD8bLanpPVVn4IKzJuw3dOvkJ8IbYw==", + "dev": true, "license": "MIT", "dependencies": { - "eastasianwidth": "^0.2.0", - "emoji-regex": "^9.2.2", - "strip-ansi": "^7.0.1" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "@types/docker-modem": "*", + "@types/node": "*", + "@types/ssh2": "*" } }, - "node_modules/boxen/node_modules/strip-ansi": { - "version": "7.1.0", + "node_modules/@types/es-aggregate-error": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/@types/es-aggregate-error/-/es-aggregate-error-1.0.6.tgz", + "integrity": "sha512-qJ7LIFp06h1QE1aVxbVd+zJP2wdaugYXYfd6JxsyRMrYHaxb6itXPogW2tz+ylUJ1n1b+JF1PHyYCfYHm0dvUg==", + "dev": true, "license": "MIT", "dependencies": { - "ansi-regex": "^6.0.1" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/strip-ansi?sponsor=1" + "@types/node": "*" } }, - "node_modules/boxen/node_modules/type-fest": { - "version": "2.19.0", - "license": "(MIT OR CC0-1.0)", - "engines": { - "node": ">=12.20" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } + "node_modules/@types/estree": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz", + "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==", + "dev": true, + "license": "MIT" }, - "node_modules/boxen/node_modules/wrap-ansi": { - "version": "8.1.0", + "node_modules/@types/istanbul-lib-coverage": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.6.tgz", + "integrity": "sha512-2QF/t/auWm0lsy8XtKVPG19v3sSOQlJe/YHZgfjb/KBBHOGSV+J2q/S671rcq9uTBrLAXmZpqJiaQbMT+zNU1w==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/istanbul-lib-report": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.3.tgz", + "integrity": "sha512-NQn7AHQnk/RSLOxrBbGyJM/aVQ+pjj5HCgasFxc0K/KhoATfQ/47AyUl15I2yBUpihjmas+a+VJBOqecrFH+uA==", + "dev": true, "license": "MIT", "dependencies": { - "ansi-styles": "^6.1.0", - "string-width": "^5.0.1", - "strip-ansi": "^7.0.1" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + "@types/istanbul-lib-coverage": "*" } }, - "node_modules/brace-expansion": { - "version": "2.0.2", + "node_modules/@types/istanbul-reports": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.4.tgz", + "integrity": "sha512-pk2B1NWalF9toCRu6gjBzR69syFjP4Od8WRAX+0mmf9lAjCRicLOWc+ZrxZHx/0XRjotgkF9t6iaMJ+aXcOdZQ==", "dev": true, "license": "MIT", "dependencies": { - "balanced-match": "^1.0.0" + "@types/istanbul-lib-report": "*" } }, - "node_modules/braces": { - "version": "3.0.3", + "node_modules/@types/jest": { + "version": "30.0.0", + "resolved": "https://registry.npmjs.org/@types/jest/-/jest-30.0.0.tgz", + "integrity": "sha512-XTYugzhuwqWjws0CVz8QpM36+T+Dz5mTEBKhNs/esGLnCIlGdRy+Dq78NRjd7ls7r8BC8ZRMOrKlkO1hU0JOwA==", "dev": true, "license": "MIT", "dependencies": { - "fill-range": "^7.1.1" - }, - "engines": { - "node": ">=8" + "expect": "^30.0.0", + "pretty-format": "^30.0.0" } }, - "node_modules/browserslist": { - "version": "4.27.0", + "node_modules/@types/jsdom": { + "version": "21.1.7", + "resolved": "https://registry.npmjs.org/@types/jsdom/-/jsdom-21.1.7.tgz", + "integrity": "sha512-yOriVnggzrnQ3a9OKOCxaVuSug3w3/SbOj5i7VwXWZEyUNl3bLF9V3MfxGbZKuwqJOQyRfqXyROBB1CoZLFWzA==", "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/browserslist" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], "license": "MIT", "dependencies": { - "baseline-browser-mapping": "^2.8.19", - "caniuse-lite": "^1.0.30001751", - "electron-to-chromium": "^1.5.238", - "node-releases": "^2.0.26", - "update-browserslist-db": "^1.1.4" - }, - "bin": { - "browserslist": "cli.js" - }, - "engines": { - "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" + "@types/node": "*", + "@types/tough-cookie": "*", + "parse5": "^7.0.0" } }, - "node_modules/bs-logger": { - "version": "0.2.6", + "node_modules/@types/json-schema": { + "version": "7.0.15", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", + "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", + "license": "MIT" + }, + "node_modules/@types/json5": { + "version": "0.0.29", + "resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz", + "integrity": "sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/markdown-escape": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@types/markdown-escape/-/markdown-escape-1.1.3.tgz", + "integrity": "sha512-JIc1+s3y5ujKnt/+N+wq6s/QdL2qZ11fP79MijrVXsAAnzSxCbT2j/3prHRouJdZ2yFLN3vkP0HytfnoCczjOw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/node": { + "version": "25.0.3", + "resolved": "https://registry.npmjs.org/@types/node/-/node-25.0.3.tgz", + "integrity": "sha512-W609buLVRVmeW693xKfzHeIV6nJGGz98uCPfeXI1ELMLXVeKYZ9m15fAMSaUPBHYLGFsVRcMmSCksQOrZV9BYA==", "dev": true, "license": "MIT", "dependencies": { - "fast-json-stable-stringify": "2.x" - }, - "engines": { - "node": ">= 6" + "undici-types": "~7.16.0" } }, - "node_modules/bser": { - "version": "2.1.1", + "node_modules/@types/sarif": { + "version": "2.1.7", + "resolved": "https://registry.npmjs.org/@types/sarif/-/sarif-2.1.7.tgz", + "integrity": "sha512-kRz0VEkJqWLf1LLVN4pT1cg1Z9wAuvI6L97V3m2f5B76Tg8d413ddvLBPTEHAZJlnn4XSvu0FkZtViCQGVyrXQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/ssh2": { + "version": "1.15.5", + "resolved": "https://registry.npmjs.org/@types/ssh2/-/ssh2-1.15.5.tgz", + "integrity": "sha512-N1ASjp/nXH3ovBHddRJpli4ozpk6UdDYIX4RJWFa9L1YKnzdhTlVmiGHm4DZnj/jLbqZpes4aeR30EFGQtvhQQ==", "dev": true, - "license": "Apache-2.0", + "license": "MIT", "dependencies": { - "node-int64": "^0.4.0" + "@types/node": "^18.11.18" } }, - "node_modules/buffer": { - "version": "5.7.1", + "node_modules/@types/ssh2-streams": { + "version": "0.1.13", + "resolved": "https://registry.npmjs.org/@types/ssh2-streams/-/ssh2-streams-0.1.13.tgz", + "integrity": "sha512-faHyY3brO9oLEA0QlcO8N2wT7R0+1sHWZvQ+y3rMLwdY1ZyS1z0W3t65j9PqT4HmQ6ALzNe7RZlNuCNE0wBSWA==", "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], "license": "MIT", "dependencies": { - "base64-js": "^1.3.1", - "ieee754": "^1.1.13" + "@types/node": "*" } }, - "node_modules/buffer-crc32": { - "version": "1.0.0", + "node_modules/@types/ssh2/node_modules/@types/node": { + "version": "18.19.130", + "resolved": "https://registry.npmjs.org/@types/node/-/node-18.19.130.tgz", + "integrity": "sha512-GRaXQx6jGfL8sKfaIDD6OupbIHBr9jv7Jnaml9tB7l4v068PAOXqfcujMMo5PhbIs6ggR1XODELqahT2R8v0fg==", "dev": true, "license": "MIT", - "engines": { - "node": ">=8.0.0" + "dependencies": { + "undici-types": "~5.26.4" } }, - "node_modules/buffer-from": { - "version": "1.1.2", + "node_modules/@types/ssh2/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/builtin-modules": { - "version": "3.3.0", + "node_modules/@types/stack-utils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.3.tgz", + "integrity": "sha512-9aEbYZ3TbYMznPdcdr3SmIrLXwC/AKZXQeCf9Pgao5CKb8CyHuEX5jzWPTkvregvhRJHcpRO6BFoGW9ycaOkYw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/stylis": { + "version": "4.2.5", + "resolved": "https://registry.npmjs.org/@types/stylis/-/stylis-4.2.5.tgz", + "integrity": "sha512-1Xve+NMN7FWjY14vLoY5tL3BVEQ/n42YLwaqJIPYhotZ9uBHt87VceMwWQpzmdEt2TNXIorIFG+YeCUUW7RInw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/tough-cookie": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/@types/tough-cookie/-/tough-cookie-4.0.5.tgz", + "integrity": "sha512-/Ad8+nIOV7Rl++6f1BdKxFSMgmoqEoYbHRpPcx3JEfv8VRsQe9Z4mCXeJBzxs7mbHY/XOZZuXlRNfhpVPbs6ZA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/trusted-types": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/@types/trusted-types/-/trusted-types-2.0.7.tgz", + "integrity": "sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw==", "dev": true, "license": "MIT", - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } + "optional": true }, - "node_modules/bundle-name": { - "version": "4.1.0", + "node_modules/@types/urijs": { + "version": "1.19.26", + "resolved": "https://registry.npmjs.org/@types/urijs/-/urijs-1.19.26.tgz", + "integrity": "sha512-wkXrVzX5yoqLnndOwFsieJA7oKM8cNkOKJtf/3vVGSUFkWDKZvFHpIl9Pvqb/T9UsawBBFMTTD8xu7sK5MWuvg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/yargs": { + "version": "17.0.35", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.35.tgz", + "integrity": "sha512-qUHkeCyQFxMXg79wQfTtfndEC+N9ZZg76HJftDJp+qH2tV7Gj4OJi7l+PiWwJ+pWtW8GwSmqsDj/oymhrTWXjg==", "dev": true, "license": "MIT", "dependencies": { - "run-applescript": "^7.0.0" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "@types/yargs-parser": "*" } }, - "node_modules/byline": { - "version": "5.0.0", + "node_modules/@types/yargs-parser": { + "version": "21.0.3", + "resolved": "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-21.0.3.tgz", + "integrity": "sha512-I4q9QU9MQv4oEOz4tAHJtNz1cwuLxn2F3xcc2iV5WdqLPpUnj30aUuxt1mAxYTG+oe8CZMV/+6rU4S4gRDzqtQ==", "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } + "license": "MIT" }, - "node_modules/bytes": { - "version": "3.1.2", + "node_modules/@typescript-eslint/eslint-plugin": { + "version": "8.50.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.50.0.tgz", + "integrity": "sha512-O7QnmOXYKVtPrfYzMolrCTfkezCJS9+ljLdKW/+DCvRsc3UAz+sbH6Xcsv7p30+0OwUbeWfUDAQE0vpabZ3QLg==", + "dev": true, "license": "MIT", + "dependencies": { + "@eslint-community/regexpp": "^4.10.0", + "@typescript-eslint/scope-manager": "8.50.0", + "@typescript-eslint/type-utils": "8.50.0", + "@typescript-eslint/utils": "8.50.0", + "@typescript-eslint/visitor-keys": "8.50.0", + "ignore": "^7.0.0", + "natural-compare": "^1.4.0", + "ts-api-utils": "^2.1.0" + }, "engines": { - "node": ">= 0.8" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "@typescript-eslint/parser": "^8.50.0", + "eslint": "^8.57.0 || ^9.0.0", + "typescript": ">=4.8.4 <6.0.0" } }, - "node_modules/call-bind": { - "version": "1.0.8", + "node_modules/@typescript-eslint/parser": { + "version": "8.50.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.50.0.tgz", + "integrity": "sha512-6/cmF2piao+f6wSxUsJLZjck7OQsYyRtcOZS02k7XINSNlz93v6emM8WutDQSXnroG2xwYlEVHJI+cPA7CPM3Q==", "dev": true, "license": "MIT", "dependencies": { - "call-bind-apply-helpers": "^1.0.0", - "es-define-property": "^1.0.0", - "get-intrinsic": "^1.2.4", - "set-function-length": "^1.2.2" + "@typescript-eslint/scope-manager": "8.50.0", + "@typescript-eslint/types": "8.50.0", + "@typescript-eslint/typescript-estree": "8.50.0", + "@typescript-eslint/visitor-keys": "8.50.0", + "debug": "^4.3.4" }, "engines": { - "node": ">= 0.4" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { - "url": "https://github.com/sponsors/ljharb" + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0", + "typescript": ">=4.8.4 <6.0.0" } }, - "node_modules/call-bind-apply-helpers": { - "version": "1.0.2", + "node_modules/@typescript-eslint/project-service": { + "version": "8.50.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.50.0.tgz", + "integrity": "sha512-Cg/nQcL1BcoTijEWyx4mkVC56r8dj44bFDvBdygifuS20f3OZCHmFbjF34DPSi07kwlFvqfv/xOLnJ5DquxSGQ==", "dev": true, "license": "MIT", "dependencies": { - "es-errors": "^1.3.0", - "function-bind": "^1.1.2" + "@typescript-eslint/tsconfig-utils": "^8.50.0", + "@typescript-eslint/types": "^8.50.0", + "debug": "^4.3.4" }, "engines": { - "node": ">= 0.4" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <6.0.0" } }, - "node_modules/call-bound": { - "version": "1.0.4", + "node_modules/@typescript-eslint/scope-manager": { + "version": "8.50.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.50.0.tgz", + "integrity": "sha512-xCwfuCZjhIqy7+HKxBLrDVT5q/iq7XBVBXLn57RTIIpelLtEIZHXAF/Upa3+gaCpeV1NNS5Z9A+ID6jn50VD4A==", "dev": true, "license": "MIT", "dependencies": { - "call-bind-apply-helpers": "^1.0.2", - "get-intrinsic": "^1.3.0" + "@typescript-eslint/types": "8.50.0", + "@typescript-eslint/visitor-keys": "8.50.0" }, "engines": { - "node": ">= 0.4" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { - "url": "https://github.com/sponsors/ljharb" + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" } }, - "node_modules/call-me-maybe": { - "version": "1.0.2", - "dev": true, - "license": "MIT" - }, - "node_modules/callsites": { - "version": "3.1.0", + "node_modules/@typescript-eslint/tsconfig-utils": { + "version": "8.50.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.50.0.tgz", + "integrity": "sha512-vxd3G/ybKTSlm31MOA96gqvrRGv9RJ7LGtZCn2Vrc5htA0zCDvcMqUkifcjrWNNKXHUU3WCkYOzzVSFBd0wa2w==", "dev": true, "license": "MIT", "engines": { - "node": ">=6" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <6.0.0" } }, - "node_modules/camelcase": { - "version": "5.3.1", + "node_modules/@typescript-eslint/type-utils": { + "version": "8.50.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.50.0.tgz", + "integrity": "sha512-7OciHT2lKCewR0mFoBrvZJ4AXTMe/sYOe87289WAViOocEmDjjv8MvIOT2XESuKj9jp8u3SZYUSh89QA4S1kQw==", "dev": true, "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.50.0", + "@typescript-eslint/typescript-estree": "8.50.0", + "@typescript-eslint/utils": "8.50.0", + "debug": "^4.3.4", + "ts-api-utils": "^2.1.0" + }, "engines": { - "node": ">=6" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0", + "typescript": ">=4.8.4 <6.0.0" } }, - "node_modules/camelize": { - "version": "1.0.1", + "node_modules/@typescript-eslint/types": { + "version": "8.50.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.50.0.tgz", + "integrity": "sha512-iX1mgmGrXdANhhITbpp2QQM2fGehBse9LbTf0sidWK6yg/NE+uhV5dfU1g6EYPlcReYmkE9QLPq/2irKAmtS9w==", "dev": true, "license": "MIT", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, "funding": { - "url": "https://github.com/sponsors/ljharb" + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" } }, - "node_modules/caniuse-lite": { - "version": "1.0.30001751", + "node_modules/@typescript-eslint/typescript-estree": { + "version": "8.50.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.50.0.tgz", + "integrity": "sha512-W7SVAGBR/IX7zm1t70Yujpbk+zdPq/u4soeFSknWFdXIFuWsBGBOUu/Tn/I6KHSKvSh91OiMuaSnYp3mtPt5IQ==", "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/caniuse-lite" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "CC-BY-4.0" - }, - "node_modules/chalk": { - "version": "4.1.2", "license": "MIT", "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" + "@typescript-eslint/project-service": "8.50.0", + "@typescript-eslint/tsconfig-utils": "8.50.0", + "@typescript-eslint/types": "8.50.0", + "@typescript-eslint/visitor-keys": "8.50.0", + "debug": "^4.3.4", + "minimatch": "^9.0.4", + "semver": "^7.6.0", + "tinyglobby": "^0.2.15", + "ts-api-utils": "^2.1.0" }, "engines": { - "node": ">=10" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <6.0.0" } }, - "node_modules/chalk-template": { - "version": "0.4.0", + "node_modules/@typescript-eslint/utils": { + "version": "8.50.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.50.0.tgz", + "integrity": "sha512-87KgUXET09CRjGCi2Ejxy3PULXna63/bMYv72tCAlDJC3Yqwln0HiFJ3VJMst2+mEtNtZu5oFvX4qJGjKsnAgg==", + "dev": true, "license": "MIT", "dependencies": { - "chalk": "^4.1.2" + "@eslint-community/eslint-utils": "^4.7.0", + "@typescript-eslint/scope-manager": "8.50.0", + "@typescript-eslint/types": "8.50.0", + "@typescript-eslint/typescript-estree": "8.50.0" }, "engines": { - "node": ">=12" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { - "url": "https://github.com/chalk/chalk-template?sponsor=1" - } - }, - "node_modules/change-case": { - "version": "5.4.4", - "dev": true, - "license": "MIT" - }, - "node_modules/char-regex": { - "version": "1.0.2", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10" - } - }, - "node_modules/chardet": { - "version": "2.1.0", - "dev": true, - "license": "MIT" + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0", + "typescript": ">=4.8.4 <6.0.0" + } }, - "node_modules/chokidar": { - "version": "3.6.0", + "node_modules/@typescript-eslint/visitor-keys": { + "version": "8.50.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.50.0.tgz", + "integrity": "sha512-Xzmnb58+Db78gT/CCj/PVCvK+zxbnsw6F+O1oheYszJbBSdEjVhQi3C/Xttzxgi/GLmpvOggRs1RFpiJ8+c34Q==", "dev": true, "license": "MIT", "dependencies": { - "anymatch": "~3.1.2", - "braces": "~3.0.2", - "glob-parent": "~5.1.2", - "is-binary-path": "~2.1.0", - "is-glob": "~4.0.1", - "normalize-path": "~3.0.0", - "readdirp": "~3.6.0" + "@typescript-eslint/types": "8.50.0", + "eslint-visitor-keys": "^4.2.1" }, "engines": { - "node": ">= 8.10.0" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { - "url": "https://paulmillr.com/funding/" - }, - "optionalDependencies": { - "fsevents": "~2.3.2" + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" } }, - "node_modules/chownr": { - "version": "1.1.4", - "dev": true, + "node_modules/@ungap/structured-clone": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.3.0.tgz", + "integrity": "sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g==", "license": "ISC" }, - "node_modules/ci-info": { - "version": "4.3.1", + "node_modules/@unrs/resolver-binding-android-arm-eabi": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-android-arm-eabi/-/resolver-binding-android-arm-eabi-1.11.1.tgz", + "integrity": "sha512-ppLRUgHVaGRWUx0R0Ut06Mjo9gBaBkg3v/8AxusGLhsIotbBLuRk51rAzqLC8gq6NyyAojEXglNjzf6R948DNw==", + "cpu": [ + "arm" + ], "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/sibiraj-s" - } + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@unrs/resolver-binding-android-arm64": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-android-arm64/-/resolver-binding-android-arm64-1.11.1.tgz", + "integrity": "sha512-lCxkVtb4wp1v+EoN+HjIG9cIIzPkX5OtM03pQYkG+U5O/wL53LC4QbIeazgiKqluGeVEeBlZahHalCaBvU1a2g==", + "cpu": [ + "arm64" ], + "dev": true, "license": "MIT", - "engines": { - "node": ">=8" - } + "optional": true, + "os": [ + "android" + ] }, - "node_modules/cjs-module-lexer": { - "version": "2.1.0", + "node_modules/@unrs/resolver-binding-darwin-arm64": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-darwin-arm64/-/resolver-binding-darwin-arm64-1.11.1.tgz", + "integrity": "sha512-gPVA1UjRu1Y/IsB/dQEsp2V1pm44Of6+LWvbLc9SDk1c2KhhDRDBUkQCYVWe6f26uJb3fOK8saWMgtX8IrMk3g==", + "cpu": [ + "arm64" + ], "dev": true, - "license": "MIT" + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] }, - "node_modules/classnames": { - "version": "2.5.1", + "node_modules/@unrs/resolver-binding-darwin-x64": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-darwin-x64/-/resolver-binding-darwin-x64-1.11.1.tgz", + "integrity": "sha512-cFzP7rWKd3lZaCsDze07QX1SC24lO8mPty9vdP+YVa3MGdVgPmFc59317b2ioXtgCMKGiCLxJ4HQs62oz6GfRQ==", + "cpu": [ + "x64" + ], "dev": true, - "license": "MIT" + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] }, - "node_modules/clean-regexp": { - "version": "1.0.0", + "node_modules/@unrs/resolver-binding-freebsd-x64": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-freebsd-x64/-/resolver-binding-freebsd-x64-1.11.1.tgz", + "integrity": "sha512-fqtGgak3zX4DCB6PFpsH5+Kmt/8CIi4Bry4rb1ho6Av2QHTREM+47y282Uqiu3ZRF5IQioJQ5qWRV6jduA+iGw==", + "cpu": [ + "x64" + ], "dev": true, "license": "MIT", - "dependencies": { - "escape-string-regexp": "^1.0.5" - }, - "engines": { - "node": ">=4" - } + "optional": true, + "os": [ + "freebsd" + ] }, - "node_modules/clean-regexp/node_modules/escape-string-regexp": { - "version": "1.0.5", + "node_modules/@unrs/resolver-binding-linux-arm-gnueabihf": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-arm-gnueabihf/-/resolver-binding-linux-arm-gnueabihf-1.11.1.tgz", + "integrity": "sha512-u92mvlcYtp9MRKmP+ZvMmtPN34+/3lMHlyMj7wXJDeXxuM0Vgzz0+PPJNsro1m3IZPYChIkn944wW8TYgGKFHw==", + "cpu": [ + "arm" + ], "dev": true, "license": "MIT", - "engines": { - "node": ">=0.8.0" - } + "optional": true, + "os": [ + "linux" + ] }, - "node_modules/cli-boxes": { - "version": "3.0.0", + "node_modules/@unrs/resolver-binding-linux-arm-musleabihf": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-arm-musleabihf/-/resolver-binding-linux-arm-musleabihf-1.11.1.tgz", + "integrity": "sha512-cINaoY2z7LVCrfHkIcmvj7osTOtm6VVT16b5oQdS4beibX2SYBwgYLmqhBjA1t51CarSaBuX5YNsWLjsqfW5Cw==", + "cpu": [ + "arm" + ], + "dev": true, "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } + "optional": true, + "os": [ + "linux" + ] }, - "node_modules/cli-cursor": { - "version": "3.1.0", + "node_modules/@unrs/resolver-binding-linux-arm64-gnu": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-arm64-gnu/-/resolver-binding-linux-arm64-gnu-1.11.1.tgz", + "integrity": "sha512-34gw7PjDGB9JgePJEmhEqBhWvCiiWCuXsL9hYphDF7crW7UgI05gyBAi6MF58uGcMOiOqSJ2ybEeCvHcq0BCmQ==", + "cpu": [ + "arm64" + ], "dev": true, "license": "MIT", - "dependencies": { - "restore-cursor": "^3.1.0" - }, - "engines": { - "node": ">=8" - } + "optional": true, + "os": [ + "linux" + ] }, - "node_modules/cli-spinners": { - "version": "2.9.2", + "node_modules/@unrs/resolver-binding-linux-arm64-musl": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-arm64-musl/-/resolver-binding-linux-arm64-musl-1.11.1.tgz", + "integrity": "sha512-RyMIx6Uf53hhOtJDIamSbTskA99sPHS96wxVE/bJtePJJtpdKGXO1wY90oRdXuYOGOTuqjT8ACccMc4K6QmT3w==", + "cpu": [ + "arm64" + ], "dev": true, "license": "MIT", - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } + "optional": true, + "os": [ + "linux" + ] }, - "node_modules/cli-width": { - "version": "3.0.0", + "node_modules/@unrs/resolver-binding-linux-ppc64-gnu": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-ppc64-gnu/-/resolver-binding-linux-ppc64-gnu-1.11.1.tgz", + "integrity": "sha512-D8Vae74A4/a+mZH0FbOkFJL9DSK2R6TFPC9M+jCWYia/q2einCubX10pecpDiTmkJVUH+y8K3BZClycD8nCShA==", + "cpu": [ + "ppc64" + ], "dev": true, - "license": "ISC", - "engines": { - "node": ">= 10" - } + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] }, - "node_modules/clipboardy": { - "version": "3.0.0", + "node_modules/@unrs/resolver-binding-linux-riscv64-gnu": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-riscv64-gnu/-/resolver-binding-linux-riscv64-gnu-1.11.1.tgz", + "integrity": "sha512-frxL4OrzOWVVsOc96+V3aqTIQl1O2TjgExV4EKgRY09AJ9leZpEg8Ak9phadbuX0BA4k8U5qtvMSQQGGmaJqcQ==", + "cpu": [ + "riscv64" + ], + "dev": true, "license": "MIT", - "dependencies": { - "arch": "^2.2.0", - "execa": "^5.1.1", - "is-wsl": "^2.2.0" - }, - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } + "optional": true, + "os": [ + "linux" + ] }, - "node_modules/cliui": { - "version": "7.0.4", + "node_modules/@unrs/resolver-binding-linux-riscv64-musl": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-riscv64-musl/-/resolver-binding-linux-riscv64-musl-1.11.1.tgz", + "integrity": "sha512-mJ5vuDaIZ+l/acv01sHoXfpnyrNKOk/3aDoEdLO/Xtn9HuZlDD6jKxHlkN8ZhWyLJsRBxfv9GYM2utQ1SChKew==", + "cpu": [ + "riscv64" + ], "dev": true, - "license": "ISC", - "dependencies": { - "string-width": "^4.2.0", - "strip-ansi": "^6.0.0", - "wrap-ansi": "^7.0.0" - } + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] }, - "node_modules/cliui/node_modules/wrap-ansi": { - "version": "7.0.0", + "node_modules/@unrs/resolver-binding-linux-s390x-gnu": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-s390x-gnu/-/resolver-binding-linux-s390x-gnu-1.11.1.tgz", + "integrity": "sha512-kELo8ebBVtb9sA7rMe1Cph4QHreByhaZ2QEADd9NzIQsYNQpt9UkM9iqr2lhGr5afh885d/cB5QeTXSbZHTYPg==", + "cpu": [ + "s390x" + ], "dev": true, "license": "MIT", - "dependencies": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" - } + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@unrs/resolver-binding-linux-x64-gnu": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-x64-gnu/-/resolver-binding-linux-x64-gnu-1.11.1.tgz", + "integrity": "sha512-C3ZAHugKgovV5YvAMsxhq0gtXuwESUKc5MhEtjBpLoHPLYM+iuwSj3lflFwK3DPm68660rZ7G8BMcwSro7hD5w==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@unrs/resolver-binding-linux-x64-musl": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-x64-musl/-/resolver-binding-linux-x64-musl-1.11.1.tgz", + "integrity": "sha512-rV0YSoyhK2nZ4vEswT/QwqzqQXw5I6CjoaYMOX0TqBlWhojUf8P94mvI7nuJTeaCkkds3QE4+zS8Ko+GdXuZtA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] }, - "node_modules/clone": { - "version": "1.0.4", + "node_modules/@unrs/resolver-binding-wasm32-wasi": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-wasm32-wasi/-/resolver-binding-wasm32-wasi-1.11.1.tgz", + "integrity": "sha512-5u4RkfxJm+Ng7IWgkzi3qrFOvLvQYnPBmjmZQ8+szTK/b31fQCnleNl1GgEt7nIsZRIf5PLhPwT0WM+q45x/UQ==", + "cpu": [ + "wasm32" + ], "dev": true, "license": "MIT", + "optional": true, + "dependencies": { + "@napi-rs/wasm-runtime": "^0.2.11" + }, "engines": { - "node": ">=0.8" + "node": ">=14.0.0" } }, - "node_modules/clsx": { - "version": "2.1.1", + "node_modules/@unrs/resolver-binding-win32-arm64-msvc": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-win32-arm64-msvc/-/resolver-binding-win32-arm64-msvc-1.11.1.tgz", + "integrity": "sha512-nRcz5Il4ln0kMhfL8S3hLkxI85BXs3o8EYoattsJNdsX4YUU89iOkVn7g0VHSRxFuVMdM4Q1jEpIId1Ihim/Uw==", + "cpu": [ + "arm64" + ], "dev": true, "license": "MIT", - "engines": { - "node": ">=6" - } + "optional": true, + "os": [ + "win32" + ] }, - "node_modules/co": { - "version": "4.6.0", + "node_modules/@unrs/resolver-binding-win32-ia32-msvc": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-win32-ia32-msvc/-/resolver-binding-win32-ia32-msvc-1.11.1.tgz", + "integrity": "sha512-DCEI6t5i1NmAZp6pFonpD5m7i6aFrpofcp4LA2i8IIq60Jyo28hamKBxNrZcyOwVOZkgsRp9O2sXWBWP8MnvIQ==", + "cpu": [ + "ia32" + ], "dev": true, "license": "MIT", - "engines": { - "iojs": ">= 1.0.0", - "node": ">= 0.12.0" - } + "optional": true, + "os": [ + "win32" + ] }, - "node_modules/collect-v8-coverage": { - "version": "1.0.2", + "node_modules/@unrs/resolver-binding-win32-x64-msvc": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-win32-x64-msvc/-/resolver-binding-win32-x64-msvc-1.11.1.tgz", + "integrity": "sha512-lrW200hZdbfRtztbygyaq/6jP6AKE8qQN2KvPcJ+x7wiD038YtnYtZ82IMNJ69GJibV7bwL3y9FgK+5w/pYt6g==", + "cpu": [ + "x64" + ], "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@zeit/schemas": { + "version": "2.36.0", + "resolved": "https://registry.npmjs.org/@zeit/schemas/-/schemas-2.36.0.tgz", + "integrity": "sha512-7kjMwcChYEzMKjeex9ZFXkt1AyNov9R5HZtjBKVsmVpw7pa7ZtlCGvCBC2vnnXctaYN+aRI61HjIqeetZW5ROg==", "license": "MIT" }, - "node_modules/color-convert": { - "version": "2.0.1", + "node_modules/abort-controller": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz", + "integrity": "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==", + "dev": true, "license": "MIT", "dependencies": { - "color-name": "~1.1.4" + "event-target-shim": "^5.0.0" }, "engines": { - "node": ">=7.0.0" + "node": ">=6.5" } }, - "node_modules/color-name": { - "version": "1.1.4", - "license": "MIT" - }, - "node_modules/colorette": { - "version": "1.4.0", - "dev": true, - "license": "MIT" - }, - "node_modules/combined-stream": { - "version": "1.0.8", - "dev": true, + "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": { - "delayed-stream": "~1.0.0" + "mime-types": "^3.0.0", + "negotiator": "^1.0.0" }, "engines": { - "node": ">= 0.8" + "node": ">= 0.6" } }, - "node_modules/commander": { - "version": "8.3.0", + "node_modules/acorn": { + "version": "8.15.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz", + "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==", "dev": true, "license": "MIT", + "bin": { + "acorn": "bin/acorn" + }, "engines": { - "node": ">= 12" + "node": ">=0.4.0" } }, - "node_modules/comment-parser": { - "version": "1.4.1", + "node_modules/acorn-jsx": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", + "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", "dev": true, "license": "MIT", - "engines": { - "node": ">= 12.0.0" + "peerDependencies": { + "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" } }, - "node_modules/compare-versions": { - "version": "6.1.1", - "dev": true, - "license": "MIT" - }, - "node_modules/compress-commons": { - "version": "6.0.2", + "node_modules/acorn-walk": { + "version": "8.3.4", + "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.4.tgz", + "integrity": "sha512-ueEepnujpqee2o5aIYnvHU6C0A42MNdsIDeqy5BydrkuC5R1ZuUFnm27EeFJGoEHJQgn3uleRvmTXaJgfXbt4g==", "dev": true, "license": "MIT", "dependencies": { - "crc-32": "^1.2.0", - "crc32-stream": "^6.0.0", - "is-stream": "^2.0.1", - "normalize-path": "^3.0.0", - "readable-stream": "^4.0.0" + "acorn": "^8.11.0" }, "engines": { - "node": ">= 14" - } - }, - "node_modules/compress-commons/node_modules/buffer": { - "version": "6.0.3", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "MIT", - "dependencies": { - "base64-js": "^1.3.1", - "ieee754": "^1.2.1" + "node": ">=0.4.0" } }, - "node_modules/compress-commons/node_modules/readable-stream": { - "version": "4.7.0", - "dev": true, + "node_modules/agent-base": { + "version": "7.1.4", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.4.tgz", + "integrity": "sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==", "license": "MIT", - "dependencies": { - "abort-controller": "^3.0.0", - "buffer": "^6.0.3", - "events": "^3.3.0", - "process": "^0.11.10", - "string_decoder": "^1.3.0" - }, "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + "node": ">= 14" } }, - "node_modules/compressible": { - "version": "2.0.18", + "node_modules/ajv": { + "version": "8.17.1", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz", + "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", "license": "MIT", "dependencies": { - "mime-db": ">= 1.43.0 < 2" + "fast-deep-equal": "^3.1.3", + "fast-uri": "^3.0.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2" }, - "engines": { - "node": ">= 0.6" + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" } }, - "node_modules/compression": { - "version": "1.8.1", + "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", - "dependencies": { - "bytes": "3.1.2", - "compressible": "~2.0.18", - "debug": "2.6.9", - "negotiator": "~0.6.4", - "on-headers": "~1.1.0", - "safe-buffer": "5.2.1", - "vary": "~1.1.2" + "peerDependencies": { + "ajv": "^8.5.0" }, - "engines": { - "node": ">= 0.8.0" + "peerDependenciesMeta": { + "ajv": { + "optional": true + } } }, - "node_modules/compression/node_modules/debug": { - "version": "2.6.9", + "node_modules/ajv-errors": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/ajv-errors/-/ajv-errors-3.0.0.tgz", + "integrity": "sha512-V3wD15YHfHz6y0KdhYFjyy9vWtEVALT9UrxfN3zqlI6dMioHnJrqOYfyPKol3oqrnCM9uwkcdCwkJ0WUcbLMTQ==", + "dev": true, "license": "MIT", - "dependencies": { - "ms": "2.0.0" + "peerDependencies": { + "ajv": "^8.0.1" } }, - "node_modules/compression/node_modules/ms": { - "version": "2.0.0", - "license": "MIT" - }, - "node_modules/compression/node_modules/safe-buffer": { - "version": "5.2.1", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "MIT" - }, - "node_modules/concat-map": { - "version": "0.0.1", - "license": "MIT" - }, - "node_modules/concat-stream": { - "version": "2.0.0", + "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, - "engines": [ - "node >= 6.0" - ], "license": "MIT", "dependencies": { - "buffer-from": "^1.0.0", - "inherits": "^2.0.3", - "readable-stream": "^3.0.2", - "typedarray": "^0.0.6" + "ajv": "^8.0.0" + }, + "peerDependencies": { + "ajv": "^8.0.0" + }, + "peerDependenciesMeta": { + "ajv": { + "optional": true + } } }, - "node_modules/concurrently": { - "version": "9.2.1", + "node_modules/allure-commandline": { + "version": "2.36.0", + "resolved": "https://registry.npmjs.org/allure-commandline/-/allure-commandline-2.36.0.tgz", + "integrity": "sha512-ls/4fk2Psv2Tu2PbWFrQPmUnm3gmmO9MBan4MuPWwqdkJPEmln2KRwtvtWYr9Av+e5AnFK1fGXWVyxqJIPiPwA==", "dev": true, - "license": "MIT", + "license": "Apache-2.0", + "bin": { + "allure": "bin/allure" + } + }, + "node_modules/allure-js-commons": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/allure-js-commons/-/allure-js-commons-3.4.3.tgz", + "integrity": "sha512-2n2uUFVmI7OaKXfkUPq2AcD1ksPR/WXZ9IhmQlMylb8bNpPChn1QPA/W97+RDYawFgvfhnadzIrH9ZWTWKjiNg==", + "license": "Apache-2.0", "dependencies": { - "chalk": "4.1.2", - "rxjs": "7.8.2", - "shell-quote": "1.8.3", - "supports-color": "8.1.1", - "tree-kill": "1.2.2", - "yargs": "17.7.2" + "md5": "^2.3.0" }, - "bin": { - "conc": "dist/bin/concurrently.js", - "concurrently": "dist/bin/concurrently.js" + "peerDependencies": { + "allure-playwright": "3.4.3" }, - "engines": { - "node": ">=18" + "peerDependenciesMeta": { + "allure-playwright": { + "optional": true + } + } + }, + "node_modules/allure-playwright": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/allure-playwright/-/allure-playwright-3.4.3.tgz", + "integrity": "sha512-yrRwxloT5eSDXm6PDo1UCWpdPF7WRifhXwdBsZX1r8eXpQLhwHOZSzAvXP3Q3LcLdQpgn0cUjDIteBvuY2+75Q==", + "devOptional": true, + "license": "Apache-2.0", + "dependencies": { + "allure-js-commons": "3.4.3" }, - "funding": { - "url": "https://github.com/open-cli-tools/concurrently?sponsor=1" + "peerDependencies": { + "@playwright/test": ">=1.53.0" } }, - "node_modules/concurrently/node_modules/cliui": { - "version": "8.0.1", - "dev": true, + "node_modules/ansi-align": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/ansi-align/-/ansi-align-3.0.1.tgz", + "integrity": "sha512-IOfwwBF5iczOjp/WeY4YxyjqAFMQoZufdQWDd19SEExbVLNXqvpzSJ/M7Za4/sCPmQ0+GRquoA7bGcINcxew6w==", "license": "ISC", "dependencies": { - "string-width": "^4.2.0", - "strip-ansi": "^6.0.1", - "wrap-ansi": "^7.0.0" - }, + "string-width": "^4.1.0" + } + }, + "node_modules/ansi-colors": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.3.tgz", + "integrity": "sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==", + "dev": true, + "license": "MIT", "engines": { - "node": ">=12" + "node": ">=6" } }, - "node_modules/concurrently/node_modules/supports-color": { - "version": "8.1.1", + "node_modules/ansi-escapes": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", + "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==", "dev": true, "license": "MIT", "dependencies": { - "has-flag": "^4.0.0" + "type-fest": "^0.21.3" }, "engines": { - "node": ">=10" + "node": ">=8" }, "funding": { - "url": "https://github.com/chalk/supports-color?sponsor=1" + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/concurrently/node_modules/wrap-ansi": { - "version": "7.0.0", - "dev": true, + "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/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", "license": "MIT", "dependencies": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" + "color-convert": "^2.0.1" }, "engines": { - "node": ">=10" + "node": ">=8" }, "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "node_modules/concurrently/node_modules/yargs": { - "version": "17.7.2", + "node_modules/anymatch": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", + "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", "dev": true, - "license": "MIT", + "license": "ISC", "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" + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" }, "engines": { - "node": ">=12" + "node": ">= 8" } }, - "node_modules/confusing-browser-globals": { - "version": "1.0.11", - "dev": true, - "license": "MIT" - }, - "node_modules/consola": { - "version": "3.4.2", + "node_modules/anymatch/node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", "dev": true, "license": "MIT", "engines": { - "node": "^14.18.0 || >=16.10.0" + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" } }, - "node_modules/console.table": { - "version": "0.10.0", + "node_modules/arch": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/arch/-/arch-2.2.0.tgz", + "integrity": "sha512-Of/R0wqp83cgHozfIYLbBMnej79U/SVGOOyuB3VVFv1NRM/PSFMK12x9KVtiYzJqmnU5WR2qp0Z5rHb7sWGnFQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/archiver": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/archiver/-/archiver-7.0.1.tgz", + "integrity": "sha512-ZcbTaIqJOfCc03QwD468Unz/5Ir8ATtvAHsK+FdXbDIbGfihqh9mrvdcYunQzqn4HrvWWaFyaxJhGZagaJJpPQ==", "dev": true, "license": "MIT", "dependencies": { - "easy-table": "1.1.0" + "archiver-utils": "^5.0.2", + "async": "^3.2.4", + "buffer-crc32": "^1.0.0", + "readable-stream": "^4.0.0", + "readdir-glob": "^1.1.2", + "tar-stream": "^3.0.0", + "zip-stream": "^6.0.1" }, "engines": { - "node": "> 0.10" + "node": ">= 14" } }, - "node_modules/content-disposition": { - "version": "0.5.2", + "node_modules/archiver-utils": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/archiver-utils/-/archiver-utils-5.0.2.tgz", + "integrity": "sha512-wuLJMmIBQYCsGZgYLTy5FIB2pF6Lfb6cXMSF8Qywwk3t20zWnAi7zLcQFdKQmIB8wyZpY5ER38x08GbwtR2cLA==", + "dev": true, "license": "MIT", + "dependencies": { + "glob": "^10.0.0", + "graceful-fs": "^4.2.0", + "is-stream": "^2.0.1", + "lazystream": "^1.0.0", + "lodash": "^4.17.15", + "normalize-path": "^3.0.0", + "readable-stream": "^4.0.0" + }, "engines": { - "node": ">= 0.6" + "node": ">= 14" } }, - "node_modules/convert-source-map": { - "version": "2.0.0", - "dev": true, - "license": "MIT" - }, - "node_modules/cookie": { - "version": "0.7.2", + "node_modules/archiver-utils/node_modules/buffer": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz", + "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==", "dev": true, + "funding": [ + { + "type": "github", + "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": ">= 0.6" + "dependencies": { + "base64-js": "^1.3.1", + "ieee754": "^1.2.1" } }, - "node_modules/core-js": { - "version": "3.45.0", + "node_modules/archiver-utils/node_modules/events": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", + "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==", "dev": true, - "hasInstallScript": true, "license": "MIT", - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/core-js" + "engines": { + "node": ">=0.8.x" } }, - "node_modules/core-js-compat": { - "version": "3.46.0", + "node_modules/archiver-utils/node_modules/glob": { + "version": "10.5.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.5.0.tgz", + "integrity": "sha512-DfXN8DfhJ7NH3Oe7cFmu3NCu1wKbkReJ8TorzSAFbSKrlNaQSKfIzqYqVY8zlbs2NLBbWpRiU52GX2PbaBVNkg==", "dev": true, - "license": "MIT", + "license": "ISC", "dependencies": { - "browserslist": "^4.26.3" + "foreground-child": "^3.1.0", + "jackspeak": "^3.1.2", + "minimatch": "^9.0.4", + "minipass": "^7.1.2", + "package-json-from-dist": "^1.0.0", + "path-scurry": "^1.11.1" + }, + "bin": { + "glob": "dist/esm/bin.mjs" }, "funding": { - "type": "opencollective", - "url": "https://opencollective.com/core-js" + "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/core-util-is": { - "version": "1.0.3", + "node_modules/archiver-utils/node_modules/ieee754": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", "dev": true, - "license": "MIT" + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "BSD-3-Clause" }, - "node_modules/crc-32": { - "version": "1.2.2", + "node_modules/archiver-utils/node_modules/lru-cache": { + "version": "10.4.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", + "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", "dev": true, - "license": "Apache-2.0", - "bin": { - "crc32": "bin/crc32.njs" + "license": "ISC" + }, + "node_modules/archiver-utils/node_modules/path-scurry": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz", + "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "lru-cache": "^10.2.0", + "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" }, "engines": { - "node": ">=0.8" + "node": ">=16 || 14 >=14.18" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/crc32-stream": { - "version": "6.0.0", + "node_modules/archiver-utils/node_modules/readable-stream": { + "version": "4.7.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-4.7.0.tgz", + "integrity": "sha512-oIGGmcpTLwPga8Bn6/Z75SVaH1z5dUut2ibSyAMVhmUggWpmDn2dapB0n7f8nwaSiRtepAsfJyfXIO5DCVAODg==", "dev": true, "license": "MIT", "dependencies": { - "crc-32": "^1.2.0", - "readable-stream": "^4.0.0" + "abort-controller": "^3.0.0", + "buffer": "^6.0.3", + "events": "^3.3.0", + "process": "^0.11.10", + "string_decoder": "^1.3.0" }, "engines": { - "node": ">= 14" + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + } + }, + "node_modules/archiver/node_modules/buffer": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz", + "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { + "base64-js": "^1.3.1", + "ieee754": "^1.2.1" + } + }, + "node_modules/archiver/node_modules/events": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", + "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.8.x" } }, - "node_modules/crc32-stream/node_modules/buffer": { - "version": "6.0.3", + "node_modules/archiver/node_modules/ieee754": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", "dev": true, "funding": [ { @@ -11024,14 +7901,12 @@ "url": "https://feross.org/support" } ], - "license": "MIT", - "dependencies": { - "base64-js": "^1.3.1", - "ieee754": "^1.2.1" - } + "license": "BSD-3-Clause" }, - "node_modules/crc32-stream/node_modules/readable-stream": { + "node_modules/archiver/node_modules/readable-stream": { "version": "4.7.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-4.7.0.tgz", + "integrity": "sha512-oIGGmcpTLwPga8Bn6/Z75SVaH1z5dUut2ibSyAMVhmUggWpmDn2dapB0n7f8nwaSiRtepAsfJyfXIO5DCVAODg==", "dev": true, "license": "MIT", "dependencies": { @@ -11045,2729 +7920,2783 @@ "node": "^12.22.0 || ^14.17.0 || >=16.0.0" } }, - "node_modules/create-jest": { - "version": "29.7.0", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/types": "^29.6.3", - "chalk": "^4.0.0", - "exit": "^0.1.2", - "graceful-fs": "^4.2.9", - "jest-config": "^29.7.0", - "jest-util": "^29.7.0", - "prompts": "^2.0.1" - }, - "bin": { - "create-jest": "bin/create-jest.js" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/create-jest/node_modules/@jest/environment": { - "version": "29.7.0", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/fake-timers": "^29.7.0", - "@jest/types": "^29.6.3", - "@types/node": "*", - "jest-mock": "^29.7.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } + "node_modules/arg": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/arg/-/arg-5.0.2.tgz", + "integrity": "sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==", + "license": "MIT" }, - "node_modules/create-jest/node_modules/@jest/expect": { - "version": "29.7.0", - "dev": true, - "license": "MIT", - "dependencies": { - "expect": "^29.7.0", - "jest-snapshot": "^29.7.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } + "node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "license": "Python-2.0" }, - "node_modules/create-jest/node_modules/@jest/fake-timers": { - "version": "29.7.0", + "node_modules/aria-query": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.3.2.tgz", + "integrity": "sha512-COROpnaoap1E2F000S62r6A60uHZnmlvomhfyT2DlTcrY1OrBKn2UhH7qn5wTC9zMvD0AY7csdPSNwKP+7WiQw==", "dev": true, - "license": "MIT", - "dependencies": { - "@jest/types": "^29.6.3", - "@sinonjs/fake-timers": "^10.0.2", - "@types/node": "*", - "jest-message-util": "^29.7.0", - "jest-mock": "^29.7.0", - "jest-util": "^29.7.0" - }, + "license": "Apache-2.0", "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + "node": ">= 0.4" } }, - "node_modules/create-jest/node_modules/@jest/globals": { - "version": "29.7.0", + "node_modules/array-buffer-byte-length": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/array-buffer-byte-length/-/array-buffer-byte-length-1.0.2.tgz", + "integrity": "sha512-LHE+8BuR7RYGDKvnrmcuSq3tDcKv9OFEXQt/HpbZhY7V6h0zlUXutnAD82GiFx9rdieCMjkvtcsPqBwgUl1Iiw==", "dev": true, "license": "MIT", "dependencies": { - "@jest/environment": "^29.7.0", - "@jest/expect": "^29.7.0", - "@jest/types": "^29.6.3", - "jest-mock": "^29.7.0" + "call-bound": "^1.0.3", + "is-array-buffer": "^3.0.5" }, "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/create-jest/node_modules/@jest/source-map": { - "version": "29.6.3", - "dev": true, - "license": "MIT", - "dependencies": { - "@jridgewell/trace-mapping": "^0.3.18", - "callsites": "^3.0.0", - "graceful-fs": "^4.2.9" + "node": ">= 0.4" }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/create-jest/node_modules/@jest/test-sequencer": { - "version": "29.7.0", + "node_modules/array-includes": { + "version": "3.1.9", + "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.1.9.tgz", + "integrity": "sha512-FmeCCAenzH0KH381SPT5FZmiA/TmpndpcaShhfgEN9eCVjnFBqq3l1xrI42y8+PPLI6hypzou4GXw00WHmPBLQ==", "dev": true, "license": "MIT", "dependencies": { - "@jest/test-result": "^29.7.0", - "graceful-fs": "^4.2.9", - "jest-haste-map": "^29.7.0", - "slash": "^3.0.0" + "call-bind": "^1.0.8", + "call-bound": "^1.0.4", + "define-properties": "^1.2.1", + "es-abstract": "^1.24.0", + "es-object-atoms": "^1.1.1", + "get-intrinsic": "^1.3.0", + "is-string": "^1.1.1", + "math-intrinsics": "^1.1.0" }, "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/create-jest/node_modules/@jest/transform": { - "version": "29.7.0", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/core": "^7.11.6", - "@jest/types": "^29.6.3", - "@jridgewell/trace-mapping": "^0.3.18", - "babel-plugin-istanbul": "^6.1.1", - "chalk": "^4.0.0", - "convert-source-map": "^2.0.0", - "fast-json-stable-stringify": "^2.1.0", - "graceful-fs": "^4.2.9", - "jest-haste-map": "^29.7.0", - "jest-regex-util": "^29.6.3", - "jest-util": "^29.7.0", - "micromatch": "^4.0.4", - "pirates": "^4.0.4", - "slash": "^3.0.0", - "write-file-atomic": "^4.0.2" + "node": ">= 0.4" }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/create-jest/node_modules/@sinonjs/fake-timers": { - "version": "10.3.0", - "dev": true, - "license": "BSD-3-Clause", - "dependencies": { - "@sinonjs/commons": "^3.0.0" + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/create-jest/node_modules/babel-jest": { - "version": "29.7.0", + "node_modules/array.prototype.findlast": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/array.prototype.findlast/-/array.prototype.findlast-1.2.5.tgz", + "integrity": "sha512-CVvd6FHg1Z3POpBLxO6E6zr+rSKEQ9L6rZHAaY7lLfhKsWYUBBOuMs0e9o24oopj6H+geRCX0YJ+TJLBK2eHyQ==", "dev": true, "license": "MIT", "dependencies": { - "@jest/transform": "^29.7.0", - "@types/babel__core": "^7.1.14", - "babel-plugin-istanbul": "^6.1.1", - "babel-preset-jest": "^29.6.3", - "chalk": "^4.0.0", - "graceful-fs": "^4.2.9", - "slash": "^3.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - }, - "peerDependencies": { - "@babel/core": "^7.8.0" - } - }, - "node_modules/create-jest/node_modules/babel-plugin-istanbul": { - "version": "6.1.1", - "dev": true, - "license": "BSD-3-Clause", - "dependencies": { - "@babel/helper-plugin-utils": "^7.0.0", - "@istanbuljs/load-nyc-config": "^1.0.0", - "@istanbuljs/schema": "^0.1.2", - "istanbul-lib-instrument": "^5.0.4", - "test-exclude": "^6.0.0" + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.2", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.0.0", + "es-shim-unscopables": "^1.0.2" }, "engines": { - "node": ">=8" - } - }, - "node_modules/create-jest/node_modules/babel-plugin-jest-hoist": { - "version": "29.6.3", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/template": "^7.3.3", - "@babel/types": "^7.3.3", - "@types/babel__core": "^7.1.14", - "@types/babel__traverse": "^7.0.6" + "node": ">= 0.4" }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/create-jest/node_modules/babel-preset-jest": { - "version": "29.6.3", + "node_modules/array.prototype.findlastindex": { + "version": "1.2.6", + "resolved": "https://registry.npmjs.org/array.prototype.findlastindex/-/array.prototype.findlastindex-1.2.6.tgz", + "integrity": "sha512-F/TKATkzseUExPlfvmwQKGITM3DGTK+vkAsCZoDc5daVygbJBnjEUCbgkAvVFsgfXfX4YIqZ/27G3k3tdXrTxQ==", "dev": true, "license": "MIT", "dependencies": { - "babel-plugin-jest-hoist": "^29.6.3", - "babel-preset-current-node-syntax": "^1.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + "call-bind": "^1.0.8", + "call-bound": "^1.0.4", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.9", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.1.1", + "es-shim-unscopables": "^1.1.0" }, - "peerDependencies": { - "@babel/core": "^7.0.0" - } - }, - "node_modules/create-jest/node_modules/brace-expansion": { - "version": "1.1.12", - "dev": true, - "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "node_modules/create-jest/node_modules/camelcase": { - "version": "6.3.0", - "dev": true, - "license": "MIT", "engines": { - "node": ">=10" + "node": ">= 0.4" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/create-jest/node_modules/ci-info": { - "version": "3.9.0", + "node_modules/array.prototype.flat": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/array.prototype.flat/-/array.prototype.flat-1.3.3.tgz", + "integrity": "sha512-rwG/ja1neyLqCuGZ5YYrznA62D4mZXg0i1cIskIUKSiqF3Cje9/wXAls9B9s1Wa2fomMsIv8czB8jZcPmxCXFg==", "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/sibiraj-s" - } - ], "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/create-jest/node_modules/cjs-module-lexer": { - "version": "1.4.3", - "dev": true, - "license": "MIT" - }, - "node_modules/create-jest/node_modules/glob": { - "version": "7.2.3", - "dev": true, - "license": "ISC", "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.1.1", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" + "call-bind": "^1.0.8", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.5", + "es-shim-unscopables": "^1.0.2" }, "engines": { - "node": "*" + "node": ">= 0.4" }, "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/create-jest/node_modules/istanbul-lib-instrument": { - "version": "5.2.1", - "dev": true, - "license": "BSD-3-Clause", - "dependencies": { - "@babel/core": "^7.12.3", - "@babel/parser": "^7.14.7", - "@istanbuljs/schema": "^0.1.2", - "istanbul-lib-coverage": "^3.2.0", - "semver": "^6.3.0" - }, - "engines": { - "node": ">=8" + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/create-jest/node_modules/jest-circus": { - "version": "29.7.0", + "node_modules/array.prototype.flatmap": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/array.prototype.flatmap/-/array.prototype.flatmap-1.3.3.tgz", + "integrity": "sha512-Y7Wt51eKJSyi80hFrJCePGGNo5ktJCslFuboqJsbf57CCPcm5zztluPlc4/aD8sWsKvlwatezpV4U1efk8kpjg==", "dev": true, "license": "MIT", "dependencies": { - "@jest/environment": "^29.7.0", - "@jest/expect": "^29.7.0", - "@jest/test-result": "^29.7.0", - "@jest/types": "^29.6.3", - "@types/node": "*", - "chalk": "^4.0.0", - "co": "^4.6.0", - "dedent": "^1.0.0", - "is-generator-fn": "^2.0.0", - "jest-each": "^29.7.0", - "jest-matcher-utils": "^29.7.0", - "jest-message-util": "^29.7.0", - "jest-runtime": "^29.7.0", - "jest-snapshot": "^29.7.0", - "jest-util": "^29.7.0", - "p-limit": "^3.1.0", - "pretty-format": "^29.7.0", - "pure-rand": "^6.0.0", - "slash": "^3.0.0", - "stack-utils": "^2.0.3" + "call-bind": "^1.0.8", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.5", + "es-shim-unscopables": "^1.0.2" }, "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/create-jest/node_modules/jest-config": { - "version": "29.7.0", + "node_modules/array.prototype.tosorted": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/array.prototype.tosorted/-/array.prototype.tosorted-1.1.4.tgz", + "integrity": "sha512-p6Fx8B7b7ZhL/gmUsAy0D15WhvDccw3mnGNbZpi3pmeJdxtWsj2jEaI4Y6oo3XiHfzuSgPwKc04MYt6KgvC/wA==", "dev": true, "license": "MIT", "dependencies": { - "@babel/core": "^7.11.6", - "@jest/test-sequencer": "^29.7.0", - "@jest/types": "^29.6.3", - "babel-jest": "^29.7.0", - "chalk": "^4.0.0", - "ci-info": "^3.2.0", - "deepmerge": "^4.2.2", - "glob": "^7.1.3", - "graceful-fs": "^4.2.9", - "jest-circus": "^29.7.0", - "jest-environment-node": "^29.7.0", - "jest-get-type": "^29.6.3", - "jest-regex-util": "^29.6.3", - "jest-resolve": "^29.7.0", - "jest-runner": "^29.7.0", - "jest-util": "^29.7.0", - "jest-validate": "^29.7.0", - "micromatch": "^4.0.4", - "parse-json": "^5.2.0", - "pretty-format": "^29.7.0", - "slash": "^3.0.0", - "strip-json-comments": "^3.1.1" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - }, - "peerDependencies": { - "@types/node": "*", - "ts-node": ">=9.0.0" - }, - "peerDependenciesMeta": { - "@types/node": { - "optional": true - }, - "ts-node": { - "optional": true - } + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.3", + "es-errors": "^1.3.0", + "es-shim-unscopables": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" } }, - "node_modules/create-jest/node_modules/jest-docblock": { - "version": "29.7.0", + "node_modules/arraybuffer.prototype.slice": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/arraybuffer.prototype.slice/-/arraybuffer.prototype.slice-1.0.4.tgz", + "integrity": "sha512-BNoCY6SXXPQ7gF2opIP4GBE+Xw7U+pHMYKuzjgCN3GwiaIR09UUeKfheyIry77QtrCBlC0KK0q5/TER/tYh3PQ==", "dev": true, "license": "MIT", "dependencies": { - "detect-newline": "^3.0.0" + "array-buffer-byte-length": "^1.0.1", + "call-bind": "^1.0.8", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.5", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.6", + "is-array-buffer": "^3.0.4" }, "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/create-jest/node_modules/jest-each": { - "version": "29.7.0", + "node_modules/as-table": { + "version": "1.0.55", + "resolved": "https://registry.npmjs.org/as-table/-/as-table-1.0.55.tgz", + "integrity": "sha512-xvsWESUJn0JN421Xb9MQw6AsMHRCUknCe0Wjlxvjud80mU4E6hQf1A6NzQKcYNmYw62MfzEtXc+badstZP3JpQ==", "dev": true, "license": "MIT", "dependencies": { - "@jest/types": "^29.6.3", - "chalk": "^4.0.0", - "jest-get-type": "^29.6.3", - "jest-util": "^29.7.0", - "pretty-format": "^29.7.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + "printable-characters": "^1.0.42" } }, - "node_modules/create-jest/node_modules/jest-environment-node": { - "version": "29.7.0", + "node_modules/asn1": { + "version": "0.2.6", + "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.6.tgz", + "integrity": "sha512-ix/FxPn0MDjeyJ7i/yoHGFt/EX6LyNbxSEhPPXODPL+KB0VPk86UYfL0lMdy+KCnv+fmvIzySwaK5COwqVbWTQ==", "dev": true, "license": "MIT", "dependencies": { - "@jest/environment": "^29.7.0", - "@jest/fake-timers": "^29.7.0", - "@jest/types": "^29.6.3", - "@types/node": "*", - "jest-mock": "^29.7.0", - "jest-util": "^29.7.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + "safer-buffer": "~2.1.0" } }, - "node_modules/create-jest/node_modules/jest-haste-map": { - "version": "29.7.0", + "node_modules/ast-types": { + "version": "0.13.4", + "resolved": "https://registry.npmjs.org/ast-types/-/ast-types-0.13.4.tgz", + "integrity": "sha512-x1FCFnFifvYDDzTaLII71vG5uvDwgtmDTEVWAxrgeiR8VjMONcCXJx7E+USjDtHlwFmt9MysbqgF9b9Vjr6w+w==", "dev": true, "license": "MIT", "dependencies": { - "@jest/types": "^29.6.3", - "@types/graceful-fs": "^4.1.3", - "@types/node": "*", - "anymatch": "^3.0.3", - "fb-watchman": "^2.0.0", - "graceful-fs": "^4.2.9", - "jest-regex-util": "^29.6.3", - "jest-util": "^29.7.0", - "jest-worker": "^29.7.0", - "micromatch": "^4.0.4", - "walker": "^1.0.8" + "tslib": "^2.0.1" }, "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - }, - "optionalDependencies": { - "fsevents": "^2.3.2" + "node": ">=4" } }, - "node_modules/create-jest/node_modules/jest-leak-detector": { - "version": "29.7.0", + "node_modules/ast-types-flow": { + "version": "0.0.8", + "resolved": "https://registry.npmjs.org/ast-types-flow/-/ast-types-flow-0.0.8.tgz", + "integrity": "sha512-OH/2E5Fg20h2aPrbe+QL8JZQFko0YZaF+j4mnQ7BGhfavO7OpSLa8a0y9sBwomHdSbkhTS8TQNayBfnW5DwbvQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/astring": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/astring/-/astring-1.9.0.tgz", + "integrity": "sha512-LElXdjswlqjWrPpJFg1Fx4wpkOCxj1TDHlSV4PlaRxHGWko024xICaa97ZkMfs6DRKlCguiAI+rbXv5GWwXIkg==", "dev": true, "license": "MIT", - "dependencies": { - "jest-get-type": "^29.6.3", - "pretty-format": "^29.7.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + "bin": { + "astring": "bin/astring" } }, - "node_modules/create-jest/node_modules/jest-mock": { - "version": "29.7.0", + "node_modules/async": { + "version": "3.2.6", + "resolved": "https://registry.npmjs.org/async/-/async-3.2.6.tgz", + "integrity": "sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA==", + "dev": true, + "license": "MIT" + }, + "node_modules/async-function": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/async-function/-/async-function-1.0.0.tgz", + "integrity": "sha512-hsU18Ae8CDTR6Kgu9DYf0EbCr/a5iGL0rytQDobUcdpYOKokk8LEjVphnXkDkgpi0wYVsqrXuP0bZxJaTqdgoA==", "dev": true, "license": "MIT", - "dependencies": { - "@jest/types": "^29.6.3", - "@types/node": "*", - "jest-util": "^29.7.0" - }, "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + "node": ">= 0.4" } }, - "node_modules/create-jest/node_modules/jest-regex-util": { - "version": "29.6.3", + "node_modules/async-lock": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/async-lock/-/async-lock-1.4.1.tgz", + "integrity": "sha512-Az2ZTpuytrtqENulXwO3GGv1Bztugx6TT37NIo7imr/Qo0gsYiGtSdBa2B6fsXhTpVZDNfu1Qn3pk531e3q+nQ==", "dev": true, + "license": "MIT" + }, + "node_modules/asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", + "license": "MIT" + }, + "node_modules/atomic-sleep": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/atomic-sleep/-/atomic-sleep-1.0.0.tgz", + "integrity": "sha512-kNOjDqAh7px0XWNI+4QbzoiR/nTkHAWNud2uvnJquD1/x5a7EQZMJT0AczqK0Qn67oY/TTQ1LbUKajZpp3I9tQ==", "license": "MIT", "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + "node": ">=8.0.0" } }, - "node_modules/create-jest/node_modules/jest-resolve": { - "version": "29.7.0", - "dev": true, + "node_modules/available-typed-arrays": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz", + "integrity": "sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==", "license": "MIT", "dependencies": { - "chalk": "^4.0.0", - "graceful-fs": "^4.2.9", - "jest-haste-map": "^29.7.0", - "jest-pnp-resolver": "^1.2.2", - "jest-util": "^29.7.0", - "jest-validate": "^29.7.0", - "resolve": "^1.20.0", - "resolve.exports": "^2.0.0", - "slash": "^3.0.0" + "possible-typed-array-names": "^1.0.0" }, "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/create-jest/node_modules/jest-runner": { - "version": "29.7.0", - "dev": true, + "node_modules/aws-lambda": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/aws-lambda/-/aws-lambda-1.0.7.tgz", + "integrity": "sha512-9GNFMRrEMG5y3Jvv+V4azWvc+qNWdWLTjDdhf/zgMlz8haaaLWv0xeAIWxz9PuWUBawsVxy0zZotjCdR3Xq+2w==", "license": "MIT", "dependencies": { - "@jest/console": "^29.7.0", - "@jest/environment": "^29.7.0", - "@jest/test-result": "^29.7.0", - "@jest/transform": "^29.7.0", - "@jest/types": "^29.6.3", - "@types/node": "*", - "chalk": "^4.0.0", - "emittery": "^0.13.1", - "graceful-fs": "^4.2.9", - "jest-docblock": "^29.7.0", - "jest-environment-node": "^29.7.0", - "jest-haste-map": "^29.7.0", - "jest-leak-detector": "^29.7.0", - "jest-message-util": "^29.7.0", - "jest-resolve": "^29.7.0", - "jest-runtime": "^29.7.0", - "jest-util": "^29.7.0", - "jest-watcher": "^29.7.0", - "jest-worker": "^29.7.0", - "p-limit": "^3.1.0", - "source-map-support": "0.5.13" + "aws-sdk": "^2.814.0", + "commander": "^3.0.2", + "js-yaml": "^3.14.1", + "watchpack": "^2.0.0-beta.10" }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + "bin": { + "lambda": "bin/lambda" } }, - "node_modules/create-jest/node_modules/jest-runtime": { - "version": "29.7.0", - "dev": true, + "node_modules/aws-lambda/node_modules/argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", "license": "MIT", "dependencies": { - "@jest/environment": "^29.7.0", - "@jest/fake-timers": "^29.7.0", - "@jest/globals": "^29.7.0", - "@jest/source-map": "^29.6.3", - "@jest/test-result": "^29.7.0", - "@jest/transform": "^29.7.0", - "@jest/types": "^29.6.3", - "@types/node": "*", - "chalk": "^4.0.0", - "cjs-module-lexer": "^1.0.0", - "collect-v8-coverage": "^1.0.0", - "glob": "^7.1.3", - "graceful-fs": "^4.2.9", - "jest-haste-map": "^29.7.0", - "jest-message-util": "^29.7.0", - "jest-mock": "^29.7.0", - "jest-regex-util": "^29.6.3", - "jest-resolve": "^29.7.0", - "jest-snapshot": "^29.7.0", - "jest-util": "^29.7.0", - "slash": "^3.0.0", - "strip-bom": "^4.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + "sprintf-js": "~1.0.2" } }, - "node_modules/create-jest/node_modules/jest-snapshot": { - "version": "29.7.0", - "dev": true, + "node_modules/aws-lambda/node_modules/commander": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/commander/-/commander-3.0.2.tgz", + "integrity": "sha512-Gar0ASD4BDyKC4hl4DwHqDrmvjoxWKZigVnAbn5H1owvm4CxCPdb0HQDehwNYMJpla5+M2tPmPARzhtYuwpHow==", + "license": "MIT" + }, + "node_modules/aws-lambda/node_modules/js-yaml": { + "version": "3.14.2", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.2.tgz", + "integrity": "sha512-PMSmkqxr106Xa156c2M265Z+FTrPl+oxd/rgOQy2tijQeK5TxQ43psO1ZCwhVOSdnn+RzkzlRz/eY4BgJBYVpg==", "license": "MIT", "dependencies": { - "@babel/core": "^7.11.6", - "@babel/generator": "^7.7.2", - "@babel/plugin-syntax-jsx": "^7.7.2", - "@babel/plugin-syntax-typescript": "^7.7.2", - "@babel/types": "^7.3.3", - "@jest/expect-utils": "^29.7.0", - "@jest/transform": "^29.7.0", - "@jest/types": "^29.6.3", - "babel-preset-current-node-syntax": "^1.0.0", - "chalk": "^4.0.0", - "expect": "^29.7.0", - "graceful-fs": "^4.2.9", - "jest-diff": "^29.7.0", - "jest-get-type": "^29.6.3", - "jest-matcher-utils": "^29.7.0", - "jest-message-util": "^29.7.0", - "jest-util": "^29.7.0", - "natural-compare": "^1.4.0", - "pretty-format": "^29.7.0", - "semver": "^7.5.3" + "argparse": "^1.0.7", + "esprima": "^4.0.0" }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + "bin": { + "js-yaml": "bin/js-yaml.js" } }, - "node_modules/create-jest/node_modules/jest-snapshot/node_modules/semver": { - "version": "7.7.3", - "dev": true, - "license": "ISC", - "bin": { - "semver": "bin/semver.js" + "node_modules/aws-sdk": { + "version": "2.1693.0", + "resolved": "https://registry.npmjs.org/aws-sdk/-/aws-sdk-2.1693.0.tgz", + "integrity": "sha512-cJmb8xEnVLT+R6fBS5sn/EFJiX7tUnDaPtOPZ1vFbOJtd0fnZn/Ky2XGgsvvoeliWeH7mL3TWSX5zXXGSQV6gQ==", + "hasInstallScript": true, + "license": "Apache-2.0", + "dependencies": { + "buffer": "4.9.2", + "events": "1.1.1", + "ieee754": "1.1.13", + "jmespath": "0.16.0", + "querystring": "0.2.0", + "sax": "1.2.1", + "url": "0.10.3", + "util": "^0.12.4", + "uuid": "8.0.0", + "xml2js": "0.6.2" }, "engines": { - "node": ">=10" + "node": ">= 10.0.0" } }, - "node_modules/create-jest/node_modules/jest-validate": { - "version": "29.7.0", + "node_modules/axe-core": { + "version": "4.11.0", + "resolved": "https://registry.npmjs.org/axe-core/-/axe-core-4.11.0.tgz", + "integrity": "sha512-ilYanEU8vxxBexpJd8cWM4ElSQq4QctCLKih0TSfjIfCQTeyH/6zVrmIJfLPrKTKJRbiG+cfnZbQIjAlJmF1jQ==", "dev": true, + "license": "MPL-2.0", + "engines": { + "node": ">=4" + } + }, + "node_modules/axios": { + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.13.2.tgz", + "integrity": "sha512-VPk9ebNqPcy5lRGuSlKx752IlDatOjT9paPlm8A7yOuW2Fbvp4X3JznJtT4f0GzGLLiWE9W8onz51SqLYwzGaA==", "license": "MIT", "dependencies": { - "@jest/types": "^29.6.3", - "camelcase": "^6.2.0", - "chalk": "^4.0.0", - "jest-get-type": "^29.6.3", - "leven": "^3.1.0", - "pretty-format": "^29.7.0" - }, + "follow-redirects": "^1.15.6", + "form-data": "^4.0.4", + "proxy-from-env": "^1.1.0" + } + }, + "node_modules/axobject-query": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-4.1.0.tgz", + "integrity": "sha512-qIj0G9wZbMGNLjLmg1PT6v2mE9AH2zlnADJD/2tC6E00hgmhUOfEB6greHPAfLRSufHqROIUTkw6E+M3lH0PTQ==", + "dev": true, + "license": "Apache-2.0", "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + "node": ">= 0.4" } }, - "node_modules/create-jest/node_modules/jest-watcher": { - "version": "29.7.0", + "node_modules/b4a": { + "version": "1.7.3", + "resolved": "https://registry.npmjs.org/b4a/-/b4a-1.7.3.tgz", + "integrity": "sha512-5Q2mfq2WfGuFp3uS//0s6baOJLMoVduPYVeNmDYxu5OUA1/cBfvr2RIS7vi62LdNj/urk1hfmj867I3qt6uZ7Q==", "dev": true, - "license": "MIT", - "dependencies": { - "@jest/test-result": "^29.7.0", - "@jest/types": "^29.6.3", - "@types/node": "*", - "ansi-escapes": "^4.2.1", - "chalk": "^4.0.0", - "emittery": "^0.13.1", - "jest-util": "^29.7.0", - "string-length": "^4.0.1" + "license": "Apache-2.0", + "peerDependencies": { + "react-native-b4a": "*" }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + "peerDependenciesMeta": { + "react-native-b4a": { + "optional": true + } } }, - "node_modules/create-jest/node_modules/jest-worker": { - "version": "29.7.0", + "node_modules/babel-jest": { + "version": "30.2.0", + "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-30.2.0.tgz", + "integrity": "sha512-0YiBEOxWqKkSQWL9nNGGEgndoeL0ZpWrbLMNL5u/Kaxrli3Eaxlt3ZtIDktEvXt4L/R9r3ODr2zKwGM/2BjxVw==", "dev": true, "license": "MIT", "dependencies": { - "@types/node": "*", - "jest-util": "^29.7.0", - "merge-stream": "^2.0.0", - "supports-color": "^8.0.0" + "@jest/transform": "30.2.0", + "@types/babel__core": "^7.20.5", + "babel-plugin-istanbul": "^7.0.1", + "babel-preset-jest": "30.2.0", + "chalk": "^4.1.2", + "graceful-fs": "^4.2.11", + "slash": "^3.0.0" }, "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + }, + "peerDependencies": { + "@babel/core": "^7.11.0 || ^8.0.0-0" } }, - "node_modules/create-jest/node_modules/minimatch": { - "version": "3.1.2", + "node_modules/babel-plugin-istanbul": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/babel-plugin-istanbul/-/babel-plugin-istanbul-7.0.1.tgz", + "integrity": "sha512-D8Z6Qm8jCvVXtIRkBnqNHX0zJ37rQcFJ9u8WOS6tkYOsRdHBzypCstaxWiu5ZIlqQtviRYbgnRLSoCEvjqcqbA==", "dev": true, - "license": "ISC", + "license": "BSD-3-Clause", + "workspaces": [ + "test/babel-8" + ], "dependencies": { - "brace-expansion": "^1.1.7" + "@babel/helper-plugin-utils": "^7.0.0", + "@istanbuljs/load-nyc-config": "^1.0.0", + "@istanbuljs/schema": "^0.1.3", + "istanbul-lib-instrument": "^6.0.2", + "test-exclude": "^6.0.0" }, "engines": { - "node": "*" + "node": ">=12" } }, - "node_modules/create-jest/node_modules/pure-rand": { - "version": "6.1.0", - "dev": true, - "funding": [ - { - "type": "individual", - "url": "https://github.com/sponsors/dubzzz" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/fast-check" - } - ], - "license": "MIT" - }, - "node_modules/create-jest/node_modules/semver": { - "version": "6.3.1", + "node_modules/babel-plugin-jest-hoist": { + "version": "30.2.0", + "resolved": "https://registry.npmjs.org/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-30.2.0.tgz", + "integrity": "sha512-ftzhzSGMUnOzcCXd6WHdBGMyuwy15Wnn0iyyWGKgBDLxf9/s5ABuraCSpBX2uG0jUg4rqJnxsLc5+oYBqoxVaA==", "dev": true, - "license": "ISC", - "bin": { - "semver": "bin/semver.js" + "license": "MIT", + "dependencies": { + "@types/babel__core": "^7.20.5" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" } }, - "node_modules/create-jest/node_modules/signal-exit": { - "version": "3.0.7", + "node_modules/babel-preset-current-node-syntax": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/babel-preset-current-node-syntax/-/babel-preset-current-node-syntax-1.2.0.tgz", + "integrity": "sha512-E/VlAEzRrsLEb2+dv8yp3bo4scof3l9nR4lrld+Iy5NyVqgVYUJnDAmunkhPMisRI32Qc4iRiz425d8vM++2fg==", "dev": true, - "license": "ISC" + "license": "MIT", + "dependencies": { + "@babel/plugin-syntax-async-generators": "^7.8.4", + "@babel/plugin-syntax-bigint": "^7.8.3", + "@babel/plugin-syntax-class-properties": "^7.12.13", + "@babel/plugin-syntax-class-static-block": "^7.14.5", + "@babel/plugin-syntax-import-attributes": "^7.24.7", + "@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", + "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3", + "@babel/plugin-syntax-numeric-separator": "^7.10.4", + "@babel/plugin-syntax-object-rest-spread": "^7.8.3", + "@babel/plugin-syntax-optional-catch-binding": "^7.8.3", + "@babel/plugin-syntax-optional-chaining": "^7.8.3", + "@babel/plugin-syntax-private-property-in-object": "^7.14.5", + "@babel/plugin-syntax-top-level-await": "^7.14.5" + }, + "peerDependencies": { + "@babel/core": "^7.0.0 || ^8.0.0-0" + } }, - "node_modules/create-jest/node_modules/supports-color": { - "version": "8.1.1", + "node_modules/babel-preset-jest": { + "version": "30.2.0", + "resolved": "https://registry.npmjs.org/babel-preset-jest/-/babel-preset-jest-30.2.0.tgz", + "integrity": "sha512-US4Z3NOieAQumwFnYdUWKvUKh8+YSnS/gB3t6YBiz0bskpu7Pine8pPCheNxlPEW4wnUkma2a94YuW2q3guvCQ==", "dev": true, "license": "MIT", "dependencies": { - "has-flag": "^4.0.0" + "babel-plugin-jest-hoist": "30.2.0", + "babel-preset-current-node-syntax": "^1.2.0" }, "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/supports-color?sponsor=1" + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + }, + "peerDependencies": { + "@babel/core": "^7.11.0 || ^8.0.0-beta.1" } }, - "node_modules/create-jest/node_modules/write-file-atomic": { - "version": "4.0.2", + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "license": "MIT" + }, + "node_modules/bare-events": { + "version": "2.8.2", + "resolved": "https://registry.npmjs.org/bare-events/-/bare-events-2.8.2.tgz", + "integrity": "sha512-riJjyv1/mHLIPX4RwiK+oW9/4c3TEUeORHKefKAKnZ5kyslbN+HXowtbaVEqt4IMUB7OXlfixcs6gsFeo/jhiQ==", "dev": true, - "license": "ISC", - "dependencies": { - "imurmurhash": "^0.1.4", - "signal-exit": "^3.0.7" + "license": "Apache-2.0", + "peerDependencies": { + "bare-abort-controller": "*" }, - "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + "peerDependenciesMeta": { + "bare-abort-controller": { + "optional": true + } } }, - "node_modules/create-require": { - "version": "1.1.1", + "node_modules/bare-fs": { + "version": "4.5.2", + "resolved": "https://registry.npmjs.org/bare-fs/-/bare-fs-4.5.2.tgz", + "integrity": "sha512-veTnRzkb6aPHOvSKIOy60KzURfBdUflr5VReI+NSaPL6xf+XLdONQgZgpYvUuZLVQ8dCqxpBAudaOM1+KpAUxw==", "dev": true, - "license": "MIT" - }, - "node_modules/cross-spawn": { - "version": "7.0.6", - "license": "MIT", + "license": "Apache-2.0", + "optional": true, "dependencies": { - "path-key": "^3.1.0", - "shebang-command": "^2.0.0", - "which": "^2.0.1" + "bare-events": "^2.5.4", + "bare-path": "^3.0.0", + "bare-stream": "^2.6.4", + "bare-url": "^2.2.2", + "fast-fifo": "^1.3.2" }, "engines": { - "node": ">= 8" + "bare": ">=1.16.0" + }, + "peerDependencies": { + "bare-buffer": "*" + }, + "peerDependenciesMeta": { + "bare-buffer": { + "optional": true + } } }, - "node_modules/css-color-keywords": { - "version": "1.0.0", + "node_modules/bare-os": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/bare-os/-/bare-os-3.6.2.tgz", + "integrity": "sha512-T+V1+1srU2qYNBmJCXZkUY5vQ0B4FSlL3QDROnKQYOqeiQR8UbjNHlPa+TIbM4cuidiN9GaTaOZgSEgsvPbh5A==", "dev": true, - "license": "ISC", + "license": "Apache-2.0", + "optional": true, "engines": { - "node": ">=4" + "bare": ">=1.14.0" } }, - "node_modules/css-to-react-native": { - "version": "3.2.0", + "node_modules/bare-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/bare-path/-/bare-path-3.0.0.tgz", + "integrity": "sha512-tyfW2cQcB5NN8Saijrhqn0Zh7AnFNsnczRcuWODH0eYAXBsJ5gVxAUuNr7tsHSC6IZ77cA0SitzT+s47kot8Mw==", "dev": true, - "license": "MIT", + "license": "Apache-2.0", + "optional": true, "dependencies": { - "camelize": "^1.0.0", - "css-color-keywords": "^1.0.0", - "postcss-value-parser": "^4.0.2" + "bare-os": "^3.0.1" } }, - "node_modules/cssstyle": { - "version": "4.6.0", + "node_modules/bare-stream": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/bare-stream/-/bare-stream-2.7.0.tgz", + "integrity": "sha512-oyXQNicV1y8nc2aKffH+BUHFRXmx6VrPzlnaEvMhram0nPBrKcEdcyBg5r08D0i8VxngHFAiVyn1QKXpSG0B8A==", "dev": true, - "license": "MIT", + "license": "Apache-2.0", + "optional": true, "dependencies": { - "@asamuzakjp/css-color": "^3.2.0", - "rrweb-cssom": "^0.8.0" + "streamx": "^2.21.0" }, - "engines": { - "node": ">=18" + "peerDependencies": { + "bare-buffer": "*", + "bare-events": "*" + }, + "peerDependenciesMeta": { + "bare-buffer": { + "optional": true + }, + "bare-events": { + "optional": true + } } }, - "node_modules/csstype": { - "version": "3.1.3", + "node_modules/bare-url": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/bare-url/-/bare-url-2.3.2.tgz", + "integrity": "sha512-ZMq4gd9ngV5aTMa5p9+UfY0b3skwhHELaDkhEHetMdX0LRkW9kzaym4oo/Eh+Ghm0CCDuMTsRIGM/ytUc1ZYmw==", "dev": true, + "license": "Apache-2.0", + "optional": true, + "dependencies": { + "bare-path": "^3.0.0" + } + }, + "node_modules/base64-js": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], "license": "MIT" }, - "node_modules/damerau-levenshtein": { - "version": "1.0.8", + "node_modules/baseline-browser-mapping": { + "version": "2.9.10", + "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.9.10.tgz", + "integrity": "sha512-2VIKvDx8Z1a9rTB2eCkdPE5nSe28XnA+qivGnWHoB40hMMt/h1hSz0960Zqsn6ZyxWXUie0EBdElKv8may20AA==", "dev": true, - "license": "BSD-2-Clause" + "license": "Apache-2.0", + "bin": { + "baseline-browser-mapping": "dist/cli.js" + } }, - "node_modules/data-uri-to-buffer": { - "version": "6.0.2", + "node_modules/basic-ftp": { + "version": "5.0.5", + "resolved": "https://registry.npmjs.org/basic-ftp/-/basic-ftp-5.0.5.tgz", + "integrity": "sha512-4Bcg1P8xhUuqcii/S0Z9wiHIrQVPMermM1any+MX5GeGD7faD3/msQUDGLol9wOcz4/jbg/WJnGqoJF6LiBdtg==", "dev": true, "license": "MIT", "engines": { - "node": ">= 14" + "node": ">=10.0.0" } }, - "node_modules/data-urls": { - "version": "5.0.0", + "node_modules/bcrypt-pbkdf": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz", + "integrity": "sha512-qeFIXtP4MSoi6NLqO12WfqARWWuCKi2Rn/9hJLEmtB5yTNr9DqFWkJRCf2qShWzPeAMRnOgCrq0sg/KLv5ES9w==", "dev": true, - "license": "MIT", + "license": "BSD-3-Clause", "dependencies": { - "whatwg-mimetype": "^4.0.0", - "whatwg-url": "^14.0.0" - }, - "engines": { - "node": ">=18" + "tweetnacl": "^0.14.3" } }, - "node_modules/data-view-buffer": { - "version": "1.0.2", + "node_modules/better-ajv-errors": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/better-ajv-errors/-/better-ajv-errors-1.2.0.tgz", + "integrity": "sha512-UW+IsFycygIo7bclP9h5ugkNH8EjCSgqyFB/yQ4Hqqa1OEYDtb0uFIkYE0b6+CjkgJYVM5UKI/pJPxjYe9EZlA==", "dev": true, - "license": "MIT", + "license": "Apache-2.0", "dependencies": { - "call-bound": "^1.0.3", - "es-errors": "^1.3.0", - "is-data-view": "^1.0.2" + "@babel/code-frame": "^7.16.0", + "@humanwhocodes/momoa": "^2.0.2", + "chalk": "^4.1.2", + "jsonpointer": "^5.0.0", + "leven": "^3.1.0 < 4" }, "engines": { - "node": ">= 0.4" + "node": ">= 12.13.0" }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "peerDependencies": { + "ajv": "4.11.8 - 8" } }, - "node_modules/data-view-byte-length": { - "version": "1.0.2", + "node_modules/binary-extensions": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz", + "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==", "dev": true, "license": "MIT", - "dependencies": { - "call-bound": "^1.0.3", - "es-errors": "^1.3.0", - "is-data-view": "^1.0.2" - }, "engines": { - "node": ">= 0.4" + "node": ">=8" }, "funding": { - "url": "https://github.com/sponsors/inspect-js" + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/data-view-byte-offset": { - "version": "1.0.1", + "node_modules/bl": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz", + "integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==", "dev": true, "license": "MIT", "dependencies": { - "call-bound": "^1.0.2", - "es-errors": "^1.3.0", - "is-data-view": "^1.0.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "buffer": "^5.5.0", + "inherits": "^2.0.4", + "readable-stream": "^3.4.0" } }, - "node_modules/dateformat": { - "version": "3.0.2", + "node_modules/bl/node_modules/buffer": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", + "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], "license": "MIT", - "engines": { - "node": "*" + "dependencies": { + "base64-js": "^1.3.1", + "ieee754": "^1.1.13" } }, - "node_modules/debug": { - "version": "4.4.1", - "dev": true, + "node_modules/body-parser": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-2.2.1.tgz", + "integrity": "sha512-nfDwkulwiZYQIGwxdy0RUmowMhKcFVcYXUU7m4QlKYim1rUtg83xm2yjZ40QjDuc291AJjjeSc9b++AWHSgSHw==", "license": "MIT", "dependencies": { - "ms": "^2.1.3" + "bytes": "^3.1.2", + "content-type": "^1.0.5", + "debug": "^4.4.3", + "http-errors": "^2.0.0", + "iconv-lite": "^0.7.0", + "on-finished": "^2.4.1", + "qs": "^6.14.0", + "raw-body": "^3.0.1", + "type-is": "^2.0.1" }, "engines": { - "node": ">=6.0" + "node": ">=18" }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" } }, - "node_modules/decimal.js": { - "version": "10.6.0", - "dev": true, + "node_modules/bowser": { + "version": "2.13.1", + "resolved": "https://registry.npmjs.org/bowser/-/bowser-2.13.1.tgz", + "integrity": "sha512-OHawaAbjwx6rqICCKgSG0SAnT05bzd7ppyKLVUITZpANBaaMFBAsaNkto3LoQ31tyFP5kNujE8Cdx85G9VzOkw==", "license": "MIT" }, - "node_modules/decko": { - "version": "1.2.0", - "dev": true - }, - "node_modules/dedent": { - "version": "1.7.0", - "dev": true, + "node_modules/boxen": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/boxen/-/boxen-7.0.0.tgz", + "integrity": "sha512-j//dBVuyacJbvW+tvZ9HuH03fZ46QcaKvvhZickZqtB271DxJ7SNRSNxrV/dZX0085m7hISRZWbzWlJvx/rHSg==", "license": "MIT", - "peerDependencies": { - "babel-plugin-macros": "^3.1.0" + "dependencies": { + "ansi-align": "^3.0.1", + "camelcase": "^7.0.0", + "chalk": "^5.0.1", + "cli-boxes": "^3.0.0", + "string-width": "^5.1.2", + "type-fest": "^2.13.0", + "widest-line": "^4.0.1", + "wrap-ansi": "^8.0.1" }, - "peerDependenciesMeta": { - "babel-plugin-macros": { - "optional": true - } + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } - }, - "node_modules/deep-extend": { - "version": "0.6.0", + }, + "node_modules/boxen/node_modules/ansi-regex": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.2.tgz", + "integrity": "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==", "license": "MIT", "engines": { - "node": ">=4.0.0" + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" } }, - "node_modules/deep-is": { - "version": "0.1.4", - "dev": true, - "license": "MIT" - }, - "node_modules/deepmerge": { - "version": "4.3.1", - "dev": true, + "node_modules/boxen/node_modules/ansi-styles": { + "version": "6.2.3", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.3.tgz", + "integrity": "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==", "license": "MIT", "engines": { - "node": ">=0.10.0" + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "node_modules/default-browser": { - "version": "5.2.1", - "dev": true, + "node_modules/boxen/node_modules/camelcase": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-7.0.1.tgz", + "integrity": "sha512-xlx1yCK2Oc1APsPXDL2LdlNP6+uu8OCDdhOBSVT279M/S+y75O30C2VuD8T2ogdePBBl7PfPF4504tnLgX3zfw==", "license": "MIT", - "dependencies": { - "bundle-name": "^4.1.0", - "default-browser-id": "^5.0.0" - }, "engines": { - "node": ">=18" + "node": ">=14.16" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/default-browser-id": { - "version": "5.0.0", - "dev": true, + "node_modules/boxen/node_modules/chalk": { + "version": "5.6.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.6.2.tgz", + "integrity": "sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA==", "license": "MIT", "engines": { - "node": ">=18" + "node": "^12.17.0 || ^14.13 || >=16.0.0" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "url": "https://github.com/chalk/chalk?sponsor=1" } }, - "node_modules/defaults": { - "version": "1.0.4", - "dev": true, + "node_modules/boxen/node_modules/string-width": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", + "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", "license": "MIT", "dependencies": { - "clone": "^1.0.2" + "eastasianwidth": "^0.2.0", + "emoji-regex": "^9.2.2", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/define-data-property": { - "version": "1.1.4", - "dev": true, + "node_modules/boxen/node_modules/strip-ansi": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.2.tgz", + "integrity": "sha512-gmBGslpoQJtgnMAvOVqGZpEz9dyoKTCzy2nfz/n8aIFhN/jCE/rCmcxabB6jOOHV+0WNnylOxaxBQPSvcWklhA==", "license": "MIT", "dependencies": { - "es-define-property": "^1.0.0", - "es-errors": "^1.3.0", - "gopd": "^1.0.1" + "ansi-regex": "^6.0.1" }, "engines": { - "node": ">= 0.4" + "node": ">=12" }, "funding": { - "url": "https://github.com/sponsors/ljharb" + "url": "https://github.com/chalk/strip-ansi?sponsor=1" } }, - "node_modules/define-lazy-prop": { - "version": "3.0.0", - "dev": true, - "license": "MIT", + "node_modules/boxen/node_modules/type-fest": { + "version": "2.19.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-2.19.0.tgz", + "integrity": "sha512-RAH822pAdBgcNMAfWnCBU3CFZcfZ/i1eZjwFU/dsLKumyuuP3niueg2UAukXYF0E2AAoc82ZSSf9J0WQBinzHA==", + "license": "(MIT OR CC0-1.0)", "engines": { - "node": ">=12" + "node": ">=12.20" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/define-properties": { - "version": "1.2.1", - "dev": true, + "node_modules/boxen/node_modules/wrap-ansi": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", + "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", "license": "MIT", "dependencies": { - "define-data-property": "^1.0.1", - "has-property-descriptors": "^1.0.0", - "object-keys": "^1.1.1" + "ansi-styles": "^6.1.0", + "string-width": "^5.0.1", + "strip-ansi": "^7.0.1" }, "engines": { - "node": ">= 0.4" + "node": ">=12" }, "funding": { - "url": "https://github.com/sponsors/ljharb" + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" } }, - "node_modules/degenerator": { - "version": "5.0.1", + "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": { - "ast-types": "^0.13.4", - "escodegen": "^2.1.0", - "esprima": "^4.0.1" - }, - "engines": { - "node": ">= 14" + "balanced-match": "^1.0.0" } }, - "node_modules/delayed-stream": { - "version": "1.0.0", + "node_modules/braces": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", "dev": true, "license": "MIT", + "dependencies": { + "fill-range": "^7.1.1" + }, "engines": { - "node": ">=0.4.0" + "node": ">=8" } }, - "node_modules/detect-newline": { - "version": "3.1.0", + "node_modules/browserslist": { + "version": "4.28.1", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.28.1.tgz", + "integrity": "sha512-ZC5Bd0LgJXgwGqUknZY/vkUQ04r8NXnJZ3yYi4vDmSiZmC/pdSN0NbNRPxZpbtO4uAfDUAFffO8IZoM3Gj8IkA==", "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], "license": "MIT", + "dependencies": { + "baseline-browser-mapping": "^2.9.0", + "caniuse-lite": "^1.0.30001759", + "electron-to-chromium": "^1.5.263", + "node-releases": "^2.0.27", + "update-browserslist-db": "^1.2.0" + }, + "bin": { + "browserslist": "cli.js" + }, "engines": { - "node": ">=8" + "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" } }, - "node_modules/diff": { - "version": "4.0.2", + "node_modules/bs-logger": { + "version": "0.2.6", + "resolved": "https://registry.npmjs.org/bs-logger/-/bs-logger-0.2.6.tgz", + "integrity": "sha512-pd8DCoxmbgc7hyPKOvxtqNcjYoOsABPQdcCUjGp3d42VR2CX1ORhk2A87oqqu5R1kk+76nsxZupkmyd+MVtCog==", "dev": true, - "license": "BSD-3-Clause", + "license": "MIT", + "dependencies": { + "fast-json-stable-stringify": "2.x" + }, "engines": { - "node": ">=0.3.1" + "node": ">= 6" } }, - "node_modules/diff-sequences": { - "version": "29.6.3", + "node_modules/bser": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/bser/-/bser-2.1.1.tgz", + "integrity": "sha512-gQxTNE/GAfIIrmHLUE3oJyp5FO6HRBfhjnw4/wMmA63ZGDJnWBmgY/lyQBpnDUkGmAhbSe39tx2d/iTOAfglwQ==", "dev": true, + "license": "Apache-2.0", + "dependencies": { + "node-int64": "^0.4.0" + } + }, + "node_modules/buffer": { + "version": "4.9.2", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-4.9.2.tgz", + "integrity": "sha512-xq+q3SRMOxGivLhBNaUdC64hDTQwejJ+H0T/NB1XMtTVEwNTrfFF3gAxiyW0Bu/xWEGhjVKgUcMhCrUy2+uCWg==", "license": "MIT", - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + "dependencies": { + "base64-js": "^1.0.2", + "ieee754": "^1.1.4", + "isarray": "^1.0.0" } }, - "node_modules/docker-compose": { - "version": "1.2.0", + "node_modules/buffer-crc32": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-1.0.0.tgz", + "integrity": "sha512-Db1SbgBS/fg/392AblrMJk97KggmvYhr4pB5ZIMTWtaivCPMWLkmb7m21cJvpvgK+J3nsU2CmmixNBZx4vFj/w==", "dev": true, "license": "MIT", - "dependencies": { - "yaml": "^2.2.2" - }, "engines": { - "node": ">= 6.0.0" + "node": ">=8.0.0" } }, - "node_modules/docker-compose/node_modules/yaml": { - "version": "2.8.1", + "node_modules/buffer-from": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", + "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", "dev": true, - "license": "ISC", - "bin": { - "yaml": "bin.mjs" - }, + "license": "MIT" + }, + "node_modules/buildcheck": { + "version": "0.0.7", + "resolved": "https://registry.npmjs.org/buildcheck/-/buildcheck-0.0.7.tgz", + "integrity": "sha512-lHblz4ahamxpTmnsk+MNTRWsjYKv965MwOrSJyeD588rR3Jcu7swE+0wN5F+PbL5cjgu/9ObkhfzEPuofEMwLA==", + "dev": true, + "optional": true, "engines": { - "node": ">= 14.6" + "node": ">=10.0.0" } }, - "node_modules/docker-modem": { - "version": "5.0.6", + "node_modules/builtin-modules": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-3.3.0.tgz", + "integrity": "sha512-zhaCDicdLuWN5UbN5IMnFqNMhNfo919sH85y2/ea+5Yg9TsTkeZxpL+JLbp6cgYFS4sRLp3YV4S6yDuqVWHYOw==", "dev": true, - "license": "Apache-2.0", - "dependencies": { - "debug": "^4.1.1", - "readable-stream": "^3.5.0", - "split-ca": "^1.0.1", - "ssh2": "^1.15.0" - }, + "license": "MIT", "engines": { - "node": ">= 8.0" + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/dockerode": { - "version": "4.0.7", + "node_modules/builtins": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/builtins/-/builtins-1.0.3.tgz", + "integrity": "sha512-uYBjakWipfaO/bXI7E8rq6kpwHRZK5cNYrUv2OzZSI/FvmdMyXJ2tG9dKcjEC5YHmHpUAwsargWIZNWdxb/bnQ==", "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@balena/dockerignore": "^1.0.2", - "@grpc/grpc-js": "^1.11.1", - "@grpc/proto-loader": "^0.7.13", - "docker-modem": "^5.0.6", - "protobufjs": "^7.3.2", - "tar-fs": "~2.1.2", - "uuid": "^10.0.0" - }, + "license": "MIT" + }, + "node_modules/byline": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/byline/-/byline-5.0.0.tgz", + "integrity": "sha512-s6webAy+R4SR8XVuJWt2V2rGvhnrhxN+9S15GNuTK3wKPOXFF6RNc+8ug2XhH+2s4f+uudG4kUVYmYOQWL2g0Q==", + "dev": true, + "license": "MIT", "engines": { - "node": ">= 8.0" + "node": ">=0.10.0" } }, - "node_modules/dockerode/node_modules/tar-fs": { - "version": "2.1.4", - "dev": true, + "node_modules/bytes": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", + "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", "license": "MIT", - "dependencies": { - "chownr": "^1.1.1", - "mkdirp-classic": "^0.5.2", - "pump": "^3.0.0", - "tar-stream": "^2.1.4" + "engines": { + "node": ">= 0.8" } }, - "node_modules/dockerode/node_modules/tar-stream": { - "version": "2.2.0", - "dev": true, + "node_modules/call-bind": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.8.tgz", + "integrity": "sha512-oKlSFMcMwpUg2ednkhQ454wfWiU/ul3CkJe/PEHcTKuiX6RpbehUiFMXu13HalGZxfUwCQzZG747YXBn1im9ww==", "license": "MIT", "dependencies": { - "bl": "^4.0.3", - "end-of-stream": "^1.4.1", - "fs-constants": "^1.0.0", - "inherits": "^2.0.3", - "readable-stream": "^3.1.1" + "call-bind-apply-helpers": "^1.0.0", + "es-define-property": "^1.0.0", + "get-intrinsic": "^1.2.4", + "set-function-length": "^1.2.2" }, "engines": { - "node": ">=6" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/dockerode/node_modules/uuid": { - "version": "10.0.0", - "dev": true, - "funding": [ - "https://github.com/sponsors/broofa", - "https://github.com/sponsors/ctavan" - ], + "node_modules/call-bind-apply-helpers": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", + "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", "license": "MIT", - "bin": { - "uuid": "dist/bin/uuid" - } - }, - "node_modules/doctrine": { - "version": "2.1.0", - "dev": true, - "license": "Apache-2.0", "dependencies": { - "esutils": "^2.0.2" + "es-errors": "^1.3.0", + "function-bind": "^1.1.2" }, "engines": { - "node": ">=0.10.0" + "node": ">= 0.4" } }, - "node_modules/dom-serializer": { - "version": "2.0.0", - "dev": true, + "node_modules/call-bound": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz", + "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==", "license": "MIT", "dependencies": { - "domelementtype": "^2.3.0", - "domhandler": "^5.0.2", - "entities": "^4.2.0" + "call-bind-apply-helpers": "^1.0.2", + "get-intrinsic": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" }, "funding": { - "url": "https://github.com/cheeriojs/dom-serializer?sponsor=1" + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/dom-serializer/node_modules/entities": { - "version": "4.5.0", + "node_modules/call-me-maybe": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-me-maybe/-/call-me-maybe-1.0.2.tgz", + "integrity": "sha512-HpX65o1Hnr9HH25ojC1YGs7HCQLq0GCOibSaWER0eNpgJ/Z1MZv2mTc7+xh6WOPxbRVcmgbv4hGU+uSQ/2xFZQ==", "dev": true, - "license": "BSD-2-Clause", + "license": "MIT" + }, + "node_modules/callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "dev": true, + "license": "MIT", "engines": { - "node": ">=0.12" - }, + "node": ">=6" + } + }, + "node_modules/camelcase": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", + "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/camelize": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/camelize/-/camelize-1.0.1.tgz", + "integrity": "sha512-dU+Tx2fsypxTgtLoE36npi3UqcjSSMNYfkqgmoEhtZrraP5VWq0K7FkWVTYa8eMPtnU/G2txVsfdCJTn9uzpuQ==", + "dev": true, + "license": "MIT", "funding": { - "url": "https://github.com/fb55/entities?sponsor=1" + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/domelementtype": { - "version": "2.3.0", + "node_modules/caniuse-lite": { + "version": "1.0.30001761", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001761.tgz", + "integrity": "sha512-JF9ptu1vP2coz98+5051jZ4PwQgd2ni8A+gYSN7EA7dPKIMf0pDlSUxhdmVOaV3/fYK5uWBkgSXJaRLr4+3A6g==", "dev": true, "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/caniuse-lite" + }, { "type": "github", - "url": "https://github.com/sponsors/fb55" + "url": "https://github.com/sponsors/ai" } ], - "license": "BSD-2-Clause" + "license": "CC-BY-4.0" }, - "node_modules/domhandler": { - "version": "5.0.3", - "dev": true, - "license": "BSD-2-Clause", + "node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "license": "MIT", "dependencies": { - "domelementtype": "^2.3.0" + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" }, "engines": { - "node": ">= 4" + "node": ">=10" }, "funding": { - "url": "https://github.com/fb55/domhandler?sponsor=1" - } - }, - "node_modules/dompurify": { - "version": "3.2.6", - "dev": true, - "license": "(MPL-2.0 OR Apache-2.0)", - "optionalDependencies": { - "@types/trusted-types": "^2.0.7" + "url": "https://github.com/chalk/chalk?sponsor=1" } }, - "node_modules/domutils": { - "version": "3.2.2", - "dev": true, - "license": "BSD-2-Clause", + "node_modules/chalk-template": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/chalk-template/-/chalk-template-0.4.0.tgz", + "integrity": "sha512-/ghrgmhfY8RaSdeo43hNXxpoHAtxdbskUHjPpfqUWGttFgycUhYPGx3YZBCnUCvOa7Doivn1IZec3DEGFoMgLg==", + "license": "MIT", "dependencies": { - "dom-serializer": "^2.0.0", - "domelementtype": "^2.3.0", - "domhandler": "^5.0.3" + "chalk": "^4.1.2" }, - "funding": { - "url": "https://github.com/fb55/domutils?sponsor=1" - } - }, - "node_modules/dotenv": { - "version": "16.4.7", - "dev": true, - "license": "BSD-2-Clause", "engines": { "node": ">=12" }, "funding": { - "url": "https://dotenvx.com" + "url": "https://github.com/chalk/chalk-template?sponsor=1" } }, - "node_modules/dunder-proto": { - "version": "1.0.1", + "node_modules/change-case": { + "version": "5.4.4", + "resolved": "https://registry.npmjs.org/change-case/-/change-case-5.4.4.tgz", + "integrity": "sha512-HRQyTk2/YPEkt9TnUPbOpr64Uw3KOicFWPVBb+xiHvd6eBx/qPr9xqfBFDT8P2vWsvvz4jbEkfDe71W3VyNu2w==", "dev": true, - "license": "MIT", - "dependencies": { - "call-bind-apply-helpers": "^1.0.1", - "es-errors": "^1.3.0", - "gopd": "^1.2.0" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/eastasianwidth": { - "version": "0.2.0", "license": "MIT" }, - "node_modules/easy-table": { - "version": "1.1.0", + "node_modules/char-regex": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/char-regex/-/char-regex-1.0.2.tgz", + "integrity": "sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw==", "dev": true, "license": "MIT", - "optionalDependencies": { - "wcwidth": ">=1.0.1" + "engines": { + "node": ">=10" } }, - "node_modules/electron-to-chromium": { - "version": "1.5.240", + "node_modules/chardet": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/chardet/-/chardet-2.1.1.tgz", + "integrity": "sha512-PsezH1rqdV9VvyNhxxOW32/d75r01NY7TQCmOqomRo15ZSOKbpTFVsfjghxo6JloQUCGnH4k1LGu0R4yCLlWQQ==", "dev": true, - "license": "ISC" + "license": "MIT" }, - "node_modules/emittery": { - "version": "0.13.1", - "dev": true, - "license": "MIT", + "node_modules/charenc": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/charenc/-/charenc-0.0.2.tgz", + "integrity": "sha512-yrLQ/yVUFXkzg7EDQsPieE/53+0RlaWTs+wBrvW36cyilJ2SaDWfl4Yj7MtLTXleV9uEKefbAGUPv2/iWSooRA==", + "license": "BSD-3-Clause", "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sindresorhus/emittery?sponsor=1" + "node": "*" } }, - "node_modules/emoji-regex": { - "version": "9.2.2", + "node_modules/check-types": { + "version": "11.2.3", + "resolved": "https://registry.npmjs.org/check-types/-/check-types-11.2.3.tgz", + "integrity": "sha512-+67P1GkJRaxQD6PKK0Et9DhwQB+vGg3PM5+aavopCpZT1lj9jeqfvpgTLAWErNj8qApkkmXlu/Ug74kmhagkXg==", "license": "MIT" }, - "node_modules/end-of-stream": { - "version": "1.4.5", + "node_modules/chokidar": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", + "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", "dev": true, "license": "MIT", "dependencies": { - "once": "^1.4.0" - } - }, - "node_modules/entities": { - "version": "6.0.1", - "dev": true, - "license": "BSD-2-Clause", + "anymatch": "~3.1.2", + "braces": "~3.0.2", + "glob-parent": "~5.1.2", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.6.0" + }, "engines": { - "node": ">=0.12" + "node": ">= 8.10.0" }, "funding": { - "url": "https://github.com/fb55/entities?sponsor=1" + "url": "https://paulmillr.com/funding/" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" } }, - "node_modules/error-ex": { - "version": "1.3.4", + "node_modules/chownr": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz", + "integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==", + "dev": true, + "license": "ISC" + }, + "node_modules/ci-info": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-4.3.1.tgz", + "integrity": "sha512-Wdy2Igu8OcBpI2pZePZ5oWjPC38tmDVx5WKUXKwlLYkA0ozo85sLsLvkBbBn/sZaSCMFOGZJ14fvW9t5/d7kdA==", "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/sibiraj-s" + } + ], "license": "MIT", - "dependencies": { - "is-arrayish": "^0.2.1" + "engines": { + "node": ">=8" } }, - "node_modules/es-abstract": { - "version": "1.24.0", + "node_modules/cjs-module-lexer": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-2.1.1.tgz", + "integrity": "sha512-+CmxIZ/L2vNcEfvNtLdU0ZQ6mbq3FZnwAP2PPTiKP+1QOoKwlKlPgb8UKV0Dds7QVaMnHm+FwSft2VB0s/SLjQ==", "dev": true, - "license": "MIT", - "dependencies": { - "array-buffer-byte-length": "^1.0.2", - "arraybuffer.prototype.slice": "^1.0.4", - "available-typed-arrays": "^1.0.7", - "call-bind": "^1.0.8", - "call-bound": "^1.0.4", - "data-view-buffer": "^1.0.2", - "data-view-byte-length": "^1.0.2", - "data-view-byte-offset": "^1.0.1", - "es-define-property": "^1.0.1", - "es-errors": "^1.3.0", - "es-object-atoms": "^1.1.1", - "es-set-tostringtag": "^2.1.0", - "es-to-primitive": "^1.3.0", - "function.prototype.name": "^1.1.8", - "get-intrinsic": "^1.3.0", - "get-proto": "^1.0.1", - "get-symbol-description": "^1.1.0", - "globalthis": "^1.0.4", - "gopd": "^1.2.0", - "has-property-descriptors": "^1.0.2", - "has-proto": "^1.2.0", - "has-symbols": "^1.1.0", - "hasown": "^2.0.2", - "internal-slot": "^1.1.0", - "is-array-buffer": "^3.0.5", - "is-callable": "^1.2.7", - "is-data-view": "^1.0.2", - "is-negative-zero": "^2.0.3", - "is-regex": "^1.2.1", - "is-set": "^2.0.3", - "is-shared-array-buffer": "^1.0.4", - "is-string": "^1.1.1", - "is-typed-array": "^1.1.15", - "is-weakref": "^1.1.1", - "math-intrinsics": "^1.1.0", - "object-inspect": "^1.13.4", - "object-keys": "^1.1.1", - "object.assign": "^4.1.7", - "own-keys": "^1.0.1", - "regexp.prototype.flags": "^1.5.4", - "safe-array-concat": "^1.1.3", - "safe-push-apply": "^1.0.0", - "safe-regex-test": "^1.1.0", - "set-proto": "^1.0.0", - "stop-iteration-iterator": "^1.1.0", - "string.prototype.trim": "^1.2.10", - "string.prototype.trimend": "^1.0.9", - "string.prototype.trimstart": "^1.0.8", - "typed-array-buffer": "^1.0.3", - "typed-array-byte-length": "^1.0.3", - "typed-array-byte-offset": "^1.0.4", - "typed-array-length": "^1.0.7", - "unbox-primitive": "^1.1.0", - "which-typed-array": "^1.1.19" + "license": "MIT" + }, + "node_modules/classnames": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/classnames/-/classnames-2.5.1.tgz", + "integrity": "sha512-saHYOzhIQs6wy2sVxTM6bUDsQO4F50V9RQ22qBpEdCW+I+/Wmke2HOl6lS6dTpdxVhb88/I6+Hs+438c3lfUow==", + "dev": true, + "license": "MIT" + }, + "node_modules/clean-regexp": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/clean-regexp/-/clean-regexp-1.0.0.tgz", + "integrity": "sha512-GfisEZEJvzKrmGWkvfhgzcz/BllN1USeqD2V6tg14OAOgaCD2Z/PUEuxnAZ/nPvmaHRG7a8y77p1T/IRQ4D1Hw==", + "dev": true, + "license": "MIT", + "dependencies": { + "escape-string-regexp": "^1.0.5" }, "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": ">=4" } }, - "node_modules/es-define-property": { - "version": "1.0.1", + "node_modules/clean-regexp/node_modules/escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", "dev": true, "license": "MIT", "engines": { - "node": ">= 0.4" + "node": ">=0.8.0" } }, - "node_modules/es-errors": { - "version": "1.3.0", - "dev": true, + "node_modules/cli-boxes": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/cli-boxes/-/cli-boxes-3.0.0.tgz", + "integrity": "sha512-/lzGpEWL/8PfI0BmBOPRwp0c/wFNX1RdUML3jK/RcSBA9T8mZDdQpqYBKtCFTOfQbwPqWEOpjqW+Fnayc0969g==", "license": "MIT", "engines": { - "node": ">= 0.4" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/es-iterator-helpers": { - "version": "1.2.1", + "node_modules/cli-cursor": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz", + "integrity": "sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==", "dev": true, "license": "MIT", "dependencies": { - "call-bind": "^1.0.8", - "call-bound": "^1.0.3", - "define-properties": "^1.2.1", - "es-abstract": "^1.23.6", - "es-errors": "^1.3.0", - "es-set-tostringtag": "^2.0.3", - "function-bind": "^1.1.2", - "get-intrinsic": "^1.2.6", - "globalthis": "^1.0.4", - "gopd": "^1.2.0", - "has-property-descriptors": "^1.0.2", - "has-proto": "^1.2.0", - "has-symbols": "^1.1.0", - "internal-slot": "^1.1.0", - "iterator.prototype": "^1.1.4", - "safe-array-concat": "^1.1.3" + "restore-cursor": "^3.1.0" }, "engines": { - "node": ">= 0.4" + "node": ">=8" } }, - "node_modules/es-object-atoms": { - "version": "1.1.1", + "node_modules/cli-spinners": { + "version": "2.9.2", + "resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-2.9.2.tgz", + "integrity": "sha512-ywqV+5MmyL4E7ybXgKys4DugZbX0FC6LnwrhjuykIjnK9k8OQacQ7axGKnjDXWNhns0xot3bZI5h55H8yo9cJg==", "dev": true, "license": "MIT", - "dependencies": { - "es-errors": "^1.3.0" - }, "engines": { - "node": ">= 0.4" + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/es-set-tostringtag": { - "version": "2.1.0", + "node_modules/cli-width": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-3.0.0.tgz", + "integrity": "sha512-FxqpkPPwu1HjuN93Omfm4h8uIanXofW0RxVEW3k5RKx+mJJYSthzNhp32Kzxxy3YAEZ/Dc/EWN1vZRY0+kOhbw==", "dev": true, + "license": "ISC", + "engines": { + "node": ">= 10" + } + }, + "node_modules/clipboardy": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/clipboardy/-/clipboardy-3.0.0.tgz", + "integrity": "sha512-Su+uU5sr1jkUy1sGRpLKjKrvEOVXgSgiSInwa/qeID6aJ07yh+5NWc3h2QfjHjBnfX4LhtFcuAWKUsJ3r+fjbg==", "license": "MIT", "dependencies": { - "es-errors": "^1.3.0", - "get-intrinsic": "^1.2.6", - "has-tostringtag": "^1.0.2", - "hasown": "^2.0.2" + "arch": "^2.2.0", + "execa": "^5.1.1", + "is-wsl": "^2.2.0" }, "engines": { - "node": ">= 0.4" + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/es-shim-unscopables": { - "version": "1.1.0", - "dev": true, - "license": "MIT", + "node_modules/cliui": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", + "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", + "license": "ISC", "dependencies": { - "hasown": "^2.0.2" + "string-width": "^4.2.0", + "strip-ansi": "^6.0.1", + "wrap-ansi": "^7.0.0" }, "engines": { - "node": ">= 0.4" + "node": ">=12" } }, - "node_modules/es-to-primitive": { - "version": "1.3.0", - "dev": true, + "node_modules/cliui/node_modules/wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", "license": "MIT", "dependencies": { - "is-callable": "^1.2.7", - "is-date-object": "^1.0.5", - "is-symbol": "^1.0.4" + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" }, "engines": { - "node": ">= 0.4" + "node": ">=10" }, "funding": { - "url": "https://github.com/sponsors/ljharb" + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" } }, - "node_modules/es6-promise": { - "version": "3.3.1", + "node_modules/clone": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/clone/-/clone-1.0.4.tgz", + "integrity": "sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg==", "dev": true, - "license": "MIT" - }, - "node_modules/esbuild": { - "version": "0.25.11", - "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.11.tgz", - "integrity": "sha512-KohQwyzrKTQmhXDW1PjCv3Tyspn9n5GcY2RTDqeORIdIJY8yKIF7sTSopFmn/wpMPW4rdPXI0UE5LJLuq3bx0Q==", - "hasInstallScript": true, "license": "MIT", - "bin": { - "esbuild": "bin/esbuild" - }, "engines": { - "node": ">=18" - }, - "optionalDependencies": { - "@esbuild/aix-ppc64": "0.25.11", - "@esbuild/android-arm": "0.25.11", - "@esbuild/android-arm64": "0.25.11", - "@esbuild/android-x64": "0.25.11", - "@esbuild/darwin-arm64": "0.25.11", - "@esbuild/darwin-x64": "0.25.11", - "@esbuild/freebsd-arm64": "0.25.11", - "@esbuild/freebsd-x64": "0.25.11", - "@esbuild/linux-arm": "0.25.11", - "@esbuild/linux-arm64": "0.25.11", - "@esbuild/linux-ia32": "0.25.11", - "@esbuild/linux-loong64": "0.25.11", - "@esbuild/linux-mips64el": "0.25.11", - "@esbuild/linux-ppc64": "0.25.11", - "@esbuild/linux-riscv64": "0.25.11", - "@esbuild/linux-s390x": "0.25.11", - "@esbuild/linux-x64": "0.25.11", - "@esbuild/netbsd-arm64": "0.25.11", - "@esbuild/netbsd-x64": "0.25.11", - "@esbuild/openbsd-arm64": "0.25.11", - "@esbuild/openbsd-x64": "0.25.11", - "@esbuild/openharmony-arm64": "0.25.11", - "@esbuild/sunos-x64": "0.25.11", - "@esbuild/win32-arm64": "0.25.11", - "@esbuild/win32-ia32": "0.25.11", - "@esbuild/win32-x64": "0.25.11" + "node": ">=0.8" } }, - "node_modules/escalade": { - "version": "3.2.0", + "node_modules/clsx": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz", + "integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==", + "dev": true, "license": "MIT", "engines": { "node": ">=6" } }, - "node_modules/escape-string-regexp": { - "version": "4.0.0", + "node_modules/co": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", + "integrity": "sha512-QVb0dM5HvG+uaxitm8wONl7jltx8dqhfU33DcqtOZcLSVIKSDDLDi7+0LbAKiyI8hD9u42m2YxXSkMGWThaecQ==", "dev": true, "license": "MIT", "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "iojs": ">= 1.0.0", + "node": ">= 0.12.0" } }, - "node_modules/escodegen": { - "version": "2.1.0", + "node_modules/collect-v8-coverage": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/collect-v8-coverage/-/collect-v8-coverage-1.0.3.tgz", + "integrity": "sha512-1L5aqIkwPfiodaMgQunkF1zRhNqifHBmtbbbxcr6yVxxBnliw4TDOW6NxpO8DJLgJ16OT+Y4ztZqP6p/FtXnAw==", "dev": true, - "license": "BSD-2-Clause", + "license": "MIT" + }, + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "license": "MIT", "dependencies": { - "esprima": "^4.0.1", - "estraverse": "^5.2.0", - "esutils": "^2.0.2" - }, - "bin": { - "escodegen": "bin/escodegen.js", - "esgenerate": "bin/esgenerate.js" + "color-name": "~1.1.4" }, "engines": { - "node": ">=6.0" - }, - "optionalDependencies": { - "source-map": "~0.6.1" + "node": ">=7.0.0" } }, - "node_modules/eslint": { - "version": "9.38.0", + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "license": "MIT" + }, + "node_modules/colorette": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/colorette/-/colorette-1.4.0.tgz", + "integrity": "sha512-Y2oEozpomLn7Q3HFP7dpww7AtMJplbM9lGZP6RDfHqmbeRjiwRg4n6VM6j4KLmRke85uWEI7JqF17f3pqdRA0g==", "dev": true, + "license": "MIT" + }, + "node_modules/combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", "license": "MIT", "dependencies": { - "@eslint-community/eslint-utils": "^4.8.0", - "@eslint-community/regexpp": "^4.12.1", - "@eslint/config-array": "^0.21.1", - "@eslint/config-helpers": "^0.4.1", - "@eslint/core": "^0.16.0", - "@eslint/eslintrc": "^3.3.1", - "@eslint/js": "9.38.0", - "@eslint/plugin-kit": "^0.4.0", - "@humanfs/node": "^0.16.6", - "@humanwhocodes/module-importer": "^1.0.1", - "@humanwhocodes/retry": "^0.4.2", - "@types/estree": "^1.0.6", - "ajv": "^6.12.4", - "chalk": "^4.0.0", - "cross-spawn": "^7.0.6", - "debug": "^4.3.2", - "escape-string-regexp": "^4.0.0", - "eslint-scope": "^8.4.0", - "eslint-visitor-keys": "^4.2.1", - "espree": "^10.4.0", - "esquery": "^1.5.0", - "esutils": "^2.0.2", - "fast-deep-equal": "^3.1.3", - "file-entry-cache": "^8.0.0", - "find-up": "^5.0.0", - "glob-parent": "^6.0.2", - "ignore": "^5.2.0", - "imurmurhash": "^0.1.4", - "is-glob": "^4.0.0", - "json-stable-stringify-without-jsonify": "^1.0.1", - "lodash.merge": "^4.6.2", - "minimatch": "^3.1.2", - "natural-compare": "^1.4.0", - "optionator": "^0.9.3" - }, - "bin": { - "eslint": "bin/eslint.js" + "delayed-stream": "~1.0.0" }, "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "url": "https://eslint.org/donate" - }, - "peerDependencies": { - "jiti": "*" - }, - "peerDependenciesMeta": { - "jiti": { - "optional": true - } + "node": ">= 0.8" } }, - "node_modules/eslint-config-airbnb-extended": { - "version": "1.0.11", + "node_modules/commander": { + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-8.3.0.tgz", + "integrity": "sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 12" + } + }, + "node_modules/comment-parser": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/comment-parser/-/comment-parser-1.4.1.tgz", + "integrity": "sha512-buhp5kePrmda3vhc5B9t7pUQXAb2Tnd0qgpkIhPhkHXxJpiPJ11H0ZEU0oBpJ2QztSbzG/ZxMj/CHsYJqRHmyg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 12.0.0" + } + }, + "node_modules/commondir": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz", + "integrity": "sha512-W9pAhw0ja1Edb5GVdIF1mjZw/ASI0AlShXM83UUGe2DVr5TdAPEA1OA8m/g8zWp9x6On7gqufY+FatDbC3MDQg==", + "dev": true, + "license": "MIT" + }, + "node_modules/compare-versions": { + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/compare-versions/-/compare-versions-6.1.1.tgz", + "integrity": "sha512-4hm4VPpIecmlg59CHXnRDnqGplJFrbLG4aFEl5vl6cK1u76ws3LLvX7ikFnTDl5vo39sjWD6AaDPYodJp/NNHg==", + "dev": true, + "license": "MIT" + }, + "node_modules/compress-commons": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/compress-commons/-/compress-commons-6.0.2.tgz", + "integrity": "sha512-6FqVXeETqWPoGcfzrXb37E50NP0LXT8kAMu5ooZayhWWdgEY4lBEEcbQNXtkuKQsGduxiIcI4gOTsxTmuq/bSg==", "dev": true, "license": "MIT", "dependencies": { - "confusing-browser-globals": "^1.0.11", - "globals": "^16.0.0" - }, - "peerDependencies": { - "@next/eslint-plugin-next": "15.x", - "@stylistic/eslint-plugin": "3.x", - "@types/eslint-plugin-jsx-a11y": "6.x", - "eslint": "9.x", - "eslint-import-resolver-typescript": "4.x", - "eslint-plugin-import-x": "4.x", - "eslint-plugin-jsx-a11y": "6.x", - "eslint-plugin-n": "17.x", - "eslint-plugin-react": "7.x", - "eslint-plugin-react-hooks": "5.x", - "typescript-eslint": "8.x" + "crc-32": "^1.2.0", + "crc32-stream": "^6.0.0", + "is-stream": "^2.0.1", + "normalize-path": "^3.0.0", + "readable-stream": "^4.0.0" }, - "peerDependenciesMeta": { - "@next/eslint-plugin-next": { - "optional": true - }, - "@stylistic/eslint-plugin": { - "optional": true - }, - "@types/eslint-plugin-jsx-a11y": { - "optional": true - }, - "eslint": { - "optional": false - }, - "eslint-import-resolver-typescript": { - "optional": false - }, - "eslint-plugin-jsx-a11y": { - "optional": false - }, - "eslint-plugin-n": { - "optional": true - }, - "eslint-plugin-react": { - "optional": true + "engines": { + "node": ">= 14" + } + }, + "node_modules/compress-commons/node_modules/buffer": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz", + "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" }, - "eslint-plugin-react-hooks": { - "optional": true + { + "type": "patreon", + "url": "https://www.patreon.com/feross" }, - "typescript-eslint": { - "optional": true + { + "type": "consulting", + "url": "https://feross.org/support" } + ], + "license": "MIT", + "dependencies": { + "base64-js": "^1.3.1", + "ieee754": "^1.2.1" } }, - "node_modules/eslint-config-airbnb-extended/node_modules/globals": { - "version": "16.3.0", + "node_modules/compress-commons/node_modules/events": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", + "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==", "dev": true, "license": "MIT", "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">=0.8.x" } }, - "node_modules/eslint-config-next": { - "version": "15.4.6", + "node_modules/compress-commons/node_modules/ieee754": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", "dev": true, - "license": "MIT", - "dependencies": { - "@next/eslint-plugin-next": "15.4.6", - "@rushstack/eslint-patch": "^1.10.3", - "@typescript-eslint/eslint-plugin": "^5.4.2 || ^6.0.0 || ^7.0.0 || ^8.0.0", - "@typescript-eslint/parser": "^5.4.2 || ^6.0.0 || ^7.0.0 || ^8.0.0", - "eslint-import-resolver-node": "^0.3.6", - "eslint-import-resolver-typescript": "^3.5.2", - "eslint-plugin-import": "^2.31.0", - "eslint-plugin-jsx-a11y": "^6.10.0", - "eslint-plugin-react": "^7.37.0", - "eslint-plugin-react-hooks": "^5.0.0" - }, - "peerDependencies": { - "eslint": "^7.23.0 || ^8.0.0 || ^9.0.0", - "typescript": ">=3.3.1" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" } - } + ], + "license": "BSD-3-Clause" }, - "node_modules/eslint-config-next/node_modules/eslint-import-resolver-typescript": { - "version": "3.10.1", + "node_modules/compress-commons/node_modules/readable-stream": { + "version": "4.7.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-4.7.0.tgz", + "integrity": "sha512-oIGGmcpTLwPga8Bn6/Z75SVaH1z5dUut2ibSyAMVhmUggWpmDn2dapB0n7f8nwaSiRtepAsfJyfXIO5DCVAODg==", "dev": true, - "license": "ISC", + "license": "MIT", "dependencies": { - "@nolyfill/is-core-module": "1.0.39", - "debug": "^4.4.0", - "get-tsconfig": "^4.10.0", - "is-bun-module": "^2.0.0", - "stable-hash": "^0.0.5", - "tinyglobby": "^0.2.13", - "unrs-resolver": "^1.6.2" + "abort-controller": "^3.0.0", + "buffer": "^6.0.3", + "events": "^3.3.0", + "process": "^0.11.10", + "string_decoder": "^1.3.0" }, "engines": { - "node": "^14.18.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/eslint-import-resolver-typescript" - }, - "peerDependencies": { - "eslint": "*", - "eslint-plugin-import": "*", - "eslint-plugin-import-x": "*" - }, - "peerDependenciesMeta": { - "eslint-plugin-import": { - "optional": true - }, - "eslint-plugin-import-x": { - "optional": true - } + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" } }, - "node_modules/eslint-config-prettier": { - "version": "10.1.8", - "dev": true, + "node_modules/compressible": { + "version": "2.0.18", + "resolved": "https://registry.npmjs.org/compressible/-/compressible-2.0.18.tgz", + "integrity": "sha512-AF3r7P5dWxL8MxyITRMlORQNaOA2IkAFaTr4k7BUumjPtRpGDTZpl0Pb1XCO6JeDCBdp126Cgs9sMxqSjgYyRg==", "license": "MIT", - "bin": { - "eslint-config-prettier": "bin/cli.js" - }, - "funding": { - "url": "https://opencollective.com/eslint-config-prettier" + "dependencies": { + "mime-db": ">= 1.43.0 < 2" }, - "peerDependencies": { - "eslint": ">=7.0.0" + "engines": { + "node": ">= 0.6" } }, - "node_modules/eslint-import-context": { - "version": "0.1.9", - "dev": true, + "node_modules/compression": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/compression/-/compression-1.8.1.tgz", + "integrity": "sha512-9mAqGPHLakhCLeNyxPkK4xVo746zQ/czLH1Ky+vkitMnWfWZps8r0qXuwhwizagCRttsL4lfG4pIOvaWLpAP0w==", "license": "MIT", "dependencies": { - "get-tsconfig": "^4.10.1", - "stable-hash-x": "^0.2.0" + "bytes": "3.1.2", + "compressible": "~2.0.18", + "debug": "2.6.9", + "negotiator": "~0.6.4", + "on-headers": "~1.1.0", + "safe-buffer": "5.2.1", + "vary": "~1.1.2" }, "engines": { - "node": "^12.20.0 || ^14.18.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/eslint-import-context" - }, - "peerDependencies": { - "unrs-resolver": "^1.0.0" - }, - "peerDependenciesMeta": { - "unrs-resolver": { - "optional": true - } + "node": ">= 0.8.0" } }, - "node_modules/eslint-import-resolver-node": { - "version": "0.3.9", - "dev": true, + "node_modules/compression/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", "license": "MIT", "dependencies": { - "debug": "^3.2.7", - "is-core-module": "^2.13.0", - "resolve": "^1.22.4" + "ms": "2.0.0" } }, - "node_modules/eslint-import-resolver-node/node_modules/debug": { - "version": "3.2.7", - "dev": true, + "node_modules/compression/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "license": "MIT" + }, + "node_modules/compression/node_modules/negotiator": { + "version": "0.6.4", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.4.tgz", + "integrity": "sha512-myRT3DiWPHqho5PrJaIRyaMv2kgYf0mUVgBNOYMuCH5Ki1yEiQaf/ZJuQ62nvpc44wL5WDbTX7yGJi1Neevw8w==", "license": "MIT", - "dependencies": { - "ms": "^2.1.1" + "engines": { + "node": ">= 0.6" } }, - "node_modules/eslint-import-resolver-typescript": { - "version": "4.4.4", + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", + "license": "MIT" + }, + "node_modules/concurrently": { + "version": "9.2.1", + "resolved": "https://registry.npmjs.org/concurrently/-/concurrently-9.2.1.tgz", + "integrity": "sha512-fsfrO0MxV64Znoy8/l1vVIjjHa29SZyyqPgQBwhiDcaW8wJc2W3XWVOGx4M3oJBnv/zdUZIIp1gDeS98GzP8Ng==", "dev": true, - "license": "ISC", + "license": "MIT", "dependencies": { - "debug": "^4.4.1", - "eslint-import-context": "^0.1.8", - "get-tsconfig": "^4.10.1", - "is-bun-module": "^2.0.0", - "stable-hash-x": "^0.2.0", - "tinyglobby": "^0.2.14", - "unrs-resolver": "^1.7.11" + "chalk": "4.1.2", + "rxjs": "7.8.2", + "shell-quote": "1.8.3", + "supports-color": "8.1.1", + "tree-kill": "1.2.2", + "yargs": "17.7.2" + }, + "bin": { + "conc": "dist/bin/concurrently.js", + "concurrently": "dist/bin/concurrently.js" }, "engines": { - "node": "^16.17.0 || >=18.6.0" + "node": ">=18" }, "funding": { - "url": "https://opencollective.com/eslint-import-resolver-typescript" - }, - "peerDependencies": { - "eslint": "*", - "eslint-plugin-import": "*", - "eslint-plugin-import-x": "*" - }, - "peerDependenciesMeta": { - "eslint-plugin-import": { - "optional": true - }, - "eslint-plugin-import-x": { - "optional": true - } + "url": "https://github.com/open-cli-tools/concurrently?sponsor=1" } }, - "node_modules/eslint-module-utils": { - "version": "2.12.1", + "node_modules/concurrently/node_modules/supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", "dev": true, "license": "MIT", "dependencies": { - "debug": "^3.2.7" + "has-flag": "^4.0.0" }, "engines": { - "node": ">=4" + "node": ">=10" }, - "peerDependenciesMeta": { - "eslint": { - "optional": true - } + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" } }, - "node_modules/eslint-module-utils/node_modules/debug": { - "version": "3.2.7", + "node_modules/confusing-browser-globals": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/confusing-browser-globals/-/confusing-browser-globals-1.0.11.tgz", + "integrity": "sha512-JsPKdmh8ZkmnHxDk55FZ1TqVLvEQTvoByJZRN9jzI0UjxK/QgAmsphz7PGtqgPieQZ/CQcHWXCR7ATDNhGe+YA==", "dev": true, - "license": "MIT", - "dependencies": { - "ms": "^2.1.1" - } + "license": "MIT" }, - "node_modules/eslint-plugin-html": { - "version": "8.1.3", + "node_modules/consola": { + "version": "3.4.2", + "resolved": "https://registry.npmjs.org/consola/-/consola-3.4.2.tgz", + "integrity": "sha512-5IKcdX0nnYavi6G7TtOhwkYzyjfJlatbjMjuLSfE2kYT5pMDOilZ4OvMhi637CcDICTmz3wARPoyhqyX1Y+XvA==", "dev": true, - "license": "ISC", - "dependencies": { - "htmlparser2": "^10.0.0" - }, + "license": "MIT", "engines": { - "node": ">=16.0.0" + "node": "^14.18.0 || >=16.10.0" } }, - "node_modules/eslint-plugin-import": { - "version": "2.32.0", + "node_modules/console.table": { + "version": "0.10.0", + "resolved": "https://registry.npmjs.org/console.table/-/console.table-0.10.0.tgz", + "integrity": "sha512-dPyZofqggxuvSf7WXvNjuRfnsOk1YazkVP8FdxH4tcH2c37wc79/Yl6Bhr7Lsu00KMgy2ql/qCMuNu8xctZM8g==", "dev": true, "license": "MIT", "dependencies": { - "@rtsao/scc": "^1.1.0", - "array-includes": "^3.1.9", - "array.prototype.findlastindex": "^1.2.6", - "array.prototype.flat": "^1.3.3", - "array.prototype.flatmap": "^1.3.3", - "debug": "^3.2.7", - "doctrine": "^2.1.0", - "eslint-import-resolver-node": "^0.3.9", - "eslint-module-utils": "^2.12.1", - "hasown": "^2.0.2", - "is-core-module": "^2.16.1", - "is-glob": "^4.0.3", - "minimatch": "^3.1.2", - "object.fromentries": "^2.0.8", - "object.groupby": "^1.0.3", - "object.values": "^1.2.1", - "semver": "^6.3.1", - "string.prototype.trimend": "^1.0.9", - "tsconfig-paths": "^3.15.0" + "easy-table": "1.1.0" }, "engines": { - "node": ">=4" - }, - "peerDependencies": { - "eslint": "^2 || ^3 || ^4 || ^5 || ^6 || ^7.2.0 || ^8 || ^9" + "node": "> 0.10" } }, - "node_modules/eslint-plugin-import-x": { - "version": "4.16.1", - "dev": true, + "node_modules/content-disposition": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-1.0.1.tgz", + "integrity": "sha512-oIXISMynqSqm241k6kcQ5UwttDILMK4BiurCfGEREw6+X9jkkpEe5T9FZaApyLGGOnFuyMWZpdolTXMtvEJ08Q==", "license": "MIT", - "dependencies": { - "@typescript-eslint/types": "^8.35.0", - "comment-parser": "^1.4.1", - "debug": "^4.4.1", - "eslint-import-context": "^0.1.9", - "is-glob": "^4.0.3", - "minimatch": "^9.0.3 || ^10.0.1", - "semver": "^7.7.2", - "stable-hash-x": "^0.2.0", - "unrs-resolver": "^1.9.2" - }, "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + "node": ">=18" }, "funding": { - "url": "https://opencollective.com/eslint-plugin-import-x" - }, - "peerDependencies": { - "@typescript-eslint/utils": "^8.0.0", - "eslint": "^8.57.0 || ^9.0.0", - "eslint-import-resolver-node": "*" - }, - "peerDependenciesMeta": { - "@typescript-eslint/utils": { - "optional": true - }, - "eslint-import-resolver-node": { - "optional": true - } + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/content-type": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz", + "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==", + "license": "MIT", + "engines": { + "node": ">= 0.6" } }, - "node_modules/eslint-plugin-import-x/node_modules/minimatch": { - "version": "10.0.3", + "node_modules/convert-source-map": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", + "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", "dev": true, - "license": "ISC", - "dependencies": { - "@isaacs/brace-expansion": "^5.0.0" - }, + "license": "MIT" + }, + "node_modules/cookie": { + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.2.tgz", + "integrity": "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==", + "license": "MIT", "engines": { - "node": "20 || >=22" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" + "node": ">= 0.6" } }, - "node_modules/eslint-plugin-import/node_modules/brace-expansion": { - "version": "1.1.12", + "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/core-js": { + "version": "3.47.0", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.47.0.tgz", + "integrity": "sha512-c3Q2VVkGAUyupsjRnaNX6u8Dq2vAdzm9iuPj5FW0fRxzlxgq9Q39MDq10IvmQSpLgHQNyQzQmOo6bgGHmH3NNg==", "dev": true, + "hasInstallScript": true, "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" + "peer": true, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/core-js" } }, - "node_modules/eslint-plugin-import/node_modules/debug": { - "version": "3.2.7", + "node_modules/core-js-compat": { + "version": "3.47.0", + "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.47.0.tgz", + "integrity": "sha512-IGfuznZ/n7Kp9+nypamBhvwdwLsW6KC8IOaURw2doAK5e98AG3acVLdh0woOnEqCfUtS+Vu882JE4k/DAm3ItQ==", "dev": true, "license": "MIT", "dependencies": { - "ms": "^2.1.1" + "browserslist": "^4.28.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/core-js" } }, - "node_modules/eslint-plugin-import/node_modules/minimatch": { - "version": "3.1.2", + "node_modules/core-util-is": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", + "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==", "dev": true, - "license": "ISC", + "license": "MIT" + }, + "node_modules/cpu-features": { + "version": "0.0.10", + "resolved": "https://registry.npmjs.org/cpu-features/-/cpu-features-0.0.10.tgz", + "integrity": "sha512-9IkYqtX3YHPCzoVg1Py+o9057a3i0fp7S530UWokCSaFVTc7CwXPRiOjRjBQQ18ZCNafx78YfnG+HALxtVmOGA==", + "dev": true, + "hasInstallScript": true, + "optional": true, "dependencies": { - "brace-expansion": "^1.1.7" + "buildcheck": "~0.0.6", + "nan": "^2.19.0" }, "engines": { - "node": "*" + "node": ">=10.0.0" } }, - "node_modules/eslint-plugin-import/node_modules/semver": { - "version": "6.3.1", + "node_modules/crc-32": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/crc-32/-/crc-32-1.2.2.tgz", + "integrity": "sha512-ROmzCKrTnOwybPcJApAA6WBWij23HVfGVNKqqrZpuyZOHqK2CwHSvpGuyt/UNNvaIjEd8X5IFGp4Mh+Ie1IHJQ==", "dev": true, - "license": "ISC", + "license": "Apache-2.0", "bin": { - "semver": "bin/semver.js" + "crc32": "bin/crc32.njs" + }, + "engines": { + "node": ">=0.8" } }, - "node_modules/eslint-plugin-jest": { - "version": "29.0.1", + "node_modules/crc32-stream": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/crc32-stream/-/crc32-stream-6.0.0.tgz", + "integrity": "sha512-piICUB6ei4IlTv1+653yq5+KoqfBYmj9bw6LqXoOneTMDXk5nM1qt12mFW1caG3LlJXEKW1Bp0WggEmIfQB34g==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/utils": "^8.0.0" + "crc-32": "^1.2.0", + "readable-stream": "^4.0.0" }, "engines": { - "node": "^20.12.0 || ^22.0.0 || >=24.0.0" - }, - "peerDependencies": { - "@typescript-eslint/eslint-plugin": "^8.0.0", - "eslint": "^8.57.0 || ^9.0.0", - "jest": "*" - }, - "peerDependenciesMeta": { - "@typescript-eslint/eslint-plugin": { - "optional": true - }, - "jest": { - "optional": true - } + "node": ">= 14" } }, - "node_modules/eslint-plugin-json": { - "version": "4.0.1", + "node_modules/crc32-stream/node_modules/buffer": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz", + "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==", "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], "license": "MIT", "dependencies": { - "lodash": "^4.17.21", - "vscode-json-languageservice": "^4.1.6" - }, - "engines": { - "node": ">=18.0" + "base64-js": "^1.3.1", + "ieee754": "^1.2.1" } }, - "node_modules/eslint-plugin-jsx-a11y": { - "version": "6.10.2", + "node_modules/crc32-stream/node_modules/events": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", + "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==", "dev": true, "license": "MIT", - "dependencies": { - "aria-query": "^5.3.2", - "array-includes": "^3.1.8", - "array.prototype.flatmap": "^1.3.2", - "ast-types-flow": "^0.0.8", - "axe-core": "^4.10.0", - "axobject-query": "^4.1.0", - "damerau-levenshtein": "^1.0.8", - "emoji-regex": "^9.2.2", - "hasown": "^2.0.2", - "jsx-ast-utils": "^3.3.5", - "language-tags": "^1.0.9", - "minimatch": "^3.1.2", - "object.fromentries": "^2.0.8", - "safe-regex-test": "^1.0.3", - "string.prototype.includes": "^2.0.1" - }, "engines": { - "node": ">=4.0" - }, - "peerDependencies": { - "eslint": "^3 || ^4 || ^5 || ^6 || ^7 || ^8 || ^9" + "node": ">=0.8.x" } }, - "node_modules/eslint-plugin-jsx-a11y/node_modules/brace-expansion": { - "version": "1.1.12", + "node_modules/crc32-stream/node_modules/ieee754": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "BSD-3-Clause" + }, + "node_modules/crc32-stream/node_modules/readable-stream": { + "version": "4.7.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-4.7.0.tgz", + "integrity": "sha512-oIGGmcpTLwPga8Bn6/Z75SVaH1z5dUut2ibSyAMVhmUggWpmDn2dapB0n7f8nwaSiRtepAsfJyfXIO5DCVAODg==", "dev": true, "license": "MIT", "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" + "abort-controller": "^3.0.0", + "buffer": "^6.0.3", + "events": "^3.3.0", + "process": "^0.11.10", + "string_decoder": "^1.3.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" } }, - "node_modules/eslint-plugin-jsx-a11y/node_modules/minimatch": { - "version": "3.1.2", + "node_modules/create-require": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", + "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==", "dev": true, - "license": "ISC", + "license": "MIT" + }, + "node_modules/cross-spawn": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", + "license": "MIT", "dependencies": { - "brace-expansion": "^1.1.7" + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/crypt": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/crypt/-/crypt-0.0.2.tgz", + "integrity": "sha512-mCxBlsHFYh9C+HVpiEacem8FEBnMXgU9gy4zmNC+SXAZNB/1idgp/aulFJ4FgCi7GPEVbfyng092GqL2k2rmow==", + "license": "BSD-3-Clause", "engines": { "node": "*" } }, - "node_modules/eslint-plugin-no-relative-import-paths": { - "version": "v1.6.1", + "node_modules/css-color-keywords": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/css-color-keywords/-/css-color-keywords-1.0.0.tgz", + "integrity": "sha512-FyyrDHZKEjXDpNJYvVsV960FiqQyXc/LlYmsxl2BcdMb2WPx0OGRVgTg55rPSyLSNMqP52R9r8geSp7apN3Ofg==", "dev": true, - "license": "ISC" + "license": "ISC", + "engines": { + "node": ">=4" + } }, - "node_modules/eslint-plugin-prettier": { - "version": "5.5.4", + "node_modules/css-to-react-native": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/css-to-react-native/-/css-to-react-native-3.2.0.tgz", + "integrity": "sha512-e8RKaLXMOFii+02mOlqwjbD00KSEKqblnpO9e++1aXS1fPQOpS1YoqdVHBqPjHNoxeF2mimzVqawm2KCbEdtHQ==", "dev": true, "license": "MIT", "dependencies": { - "prettier-linter-helpers": "^1.0.0", - "synckit": "^0.11.7" - }, - "engines": { - "node": "^14.18.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/eslint-plugin-prettier" - }, - "peerDependencies": { - "@types/eslint": ">=8.0.0", - "eslint": ">=8.0.0", - "eslint-config-prettier": ">= 7.0.0 <10.0.0 || >=10.1.0", - "prettier": ">=3.0.0" - }, - "peerDependenciesMeta": { - "@types/eslint": { - "optional": true - }, - "eslint-config-prettier": { - "optional": true - } + "camelize": "^1.0.0", + "css-color-keywords": "^1.0.0", + "postcss-value-parser": "^4.0.2" } }, - "node_modules/eslint-plugin-react": { - "version": "7.37.5", + "node_modules/cssstyle": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/cssstyle/-/cssstyle-4.6.0.tgz", + "integrity": "sha512-2z+rWdzbbSZv6/rhtvzvqeZQHrBaqgogqt85sqFNbabZOuFbCVFb8kPeEtZjiKkbrm395irpNKiYeFeLiQnFPg==", "dev": true, "license": "MIT", "dependencies": { - "array-includes": "^3.1.8", - "array.prototype.findlast": "^1.2.5", - "array.prototype.flatmap": "^1.3.3", - "array.prototype.tosorted": "^1.1.4", - "doctrine": "^2.1.0", - "es-iterator-helpers": "^1.2.1", - "estraverse": "^5.3.0", - "hasown": "^2.0.2", - "jsx-ast-utils": "^2.4.1 || ^3.0.0", - "minimatch": "^3.1.2", - "object.entries": "^1.1.9", - "object.fromentries": "^2.0.8", - "object.values": "^1.2.1", - "prop-types": "^15.8.1", - "resolve": "^2.0.0-next.5", - "semver": "^6.3.1", - "string.prototype.matchall": "^4.0.12", - "string.prototype.repeat": "^1.0.0" + "@asamuzakjp/css-color": "^3.2.0", + "rrweb-cssom": "^0.8.0" }, "engines": { - "node": ">=4" - }, - "peerDependencies": { - "eslint": "^3 || ^4 || ^5 || ^6 || ^7 || ^8 || ^9.7" + "node": ">=18" } }, - "node_modules/eslint-plugin-react-hooks": { - "version": "5.2.0", + "node_modules/csstype": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz", + "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==", "dev": true, - "license": "MIT", - "engines": { - "node": ">=10" - }, - "peerDependencies": { - "eslint": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0 || ^9.0.0" - } + "license": "MIT" }, - "node_modules/eslint-plugin-react/node_modules/brace-expansion": { - "version": "1.1.12", + "node_modules/damerau-levenshtein": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/damerau-levenshtein/-/damerau-levenshtein-1.0.8.tgz", + "integrity": "sha512-sdQSFB7+llfUcQHUQO3+B8ERRj0Oa4w9POWMI/puGtuf7gFywGmkaLCElnudfTiKZV+NvHqL0ifzdrI8Ro7ESA==", + "dev": true, + "license": "BSD-2-Clause" + }, + "node_modules/data-uri-to-buffer": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-6.0.2.tgz", + "integrity": "sha512-7hvf7/GW8e86rW0ptuwS3OcBGDjIi6SZva7hCyWC0yYry2cOPmLIjXAUHI6DK2HsnwJd9ifmt57i8eV2n4YNpw==", "dev": true, "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" + "engines": { + "node": ">= 14" } }, - "node_modules/eslint-plugin-react/node_modules/minimatch": { - "version": "3.1.2", + "node_modules/data-urls": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/data-urls/-/data-urls-5.0.0.tgz", + "integrity": "sha512-ZYP5VBHshaDAiVZxjbRVcFJpc+4xGgT0bK3vzy1HLN8jTO975HEbuYzZJcHoQEY5K1a0z8YayJkyVETa08eNTg==", "dev": true, - "license": "ISC", + "license": "MIT", "dependencies": { - "brace-expansion": "^1.1.7" + "whatwg-mimetype": "^4.0.0", + "whatwg-url": "^14.0.0" }, "engines": { - "node": "*" + "node": ">=18" } }, - "node_modules/eslint-plugin-react/node_modules/resolve": { - "version": "2.0.0-next.5", + "node_modules/data-view-buffer": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/data-view-buffer/-/data-view-buffer-1.0.2.tgz", + "integrity": "sha512-EmKO5V3OLXh1rtK2wgXRansaK1/mtVdTUEiEI0W8RkvgT05kfxaH29PliLnpLP73yYO6142Q72QNa8Wx/A5CqQ==", "dev": true, "license": "MIT", "dependencies": { - "is-core-module": "^2.13.0", - "path-parse": "^1.0.7", - "supports-preserve-symlinks-flag": "^1.0.0" + "call-bound": "^1.0.3", + "es-errors": "^1.3.0", + "is-data-view": "^1.0.2" }, - "bin": { - "resolve": "bin/resolve" + "engines": { + "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/eslint-plugin-react/node_modules/semver": { - "version": "6.3.1", - "dev": true, - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - } - }, - "node_modules/eslint-plugin-security": { - "version": "3.0.1", + "node_modules/data-view-byte-length": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/data-view-byte-length/-/data-view-byte-length-1.0.2.tgz", + "integrity": "sha512-tuhGbE6CfTM9+5ANGf+oQb72Ky/0+s3xKUpHvShfiz2RxMFgFPjsXuRLBVMtvMs15awe45SRb83D6wH4ew6wlQ==", "dev": true, - "license": "Apache-2.0", + "license": "MIT", "dependencies": { - "safe-regex": "^2.1.1" + "call-bound": "^1.0.3", + "es-errors": "^1.3.0", + "is-data-view": "^1.0.2" }, "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + "node": ">= 0.4" }, "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/eslint-plugin-sonarjs": { - "version": "3.0.4", - "dev": true, - "license": "LGPL-3.0-only", - "dependencies": { - "@eslint-community/regexpp": "4.12.1", - "builtin-modules": "3.3.0", - "bytes": "3.1.2", - "functional-red-black-tree": "1.0.1", - "jsx-ast-utils": "3.3.5", - "lodash.merge": "4.6.2", - "minimatch": "9.0.5", - "scslre": "0.3.0", - "semver": "7.7.2", - "typescript": ">=5" - }, - "peerDependencies": { - "eslint": "^8.0.0 || ^9.0.0" + "url": "https://github.com/sponsors/inspect-js" } }, - "node_modules/eslint-plugin-sonarjs/node_modules/minimatch": { - "version": "9.0.5", + "node_modules/data-view-byte-offset": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/data-view-byte-offset/-/data-view-byte-offset-1.0.1.tgz", + "integrity": "sha512-BS8PfmtDGnrgYdOonGZQdLZslWIeCGFP9tpan0hi1Co2Zr2NKADsvGYA8XxuG/4UWgJ6Cjtv+YJnB6MM69QGlQ==", "dev": true, - "license": "ISC", + "license": "MIT", "dependencies": { - "brace-expansion": "^2.0.1" + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "is-data-view": "^1.0.1" }, "engines": { - "node": ">=16 || 14 >=14.17" + "node": ">= 0.4" }, "funding": { - "url": "https://github.com/sponsors/isaacs" + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/eslint-plugin-sort-destructure-keys": { - "version": "2.0.0", + "node_modules/dateformat": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/dateformat/-/dateformat-3.0.2.tgz", + "integrity": "sha512-EelsCzH0gMC2YmXuMeaZ3c6md1sUJQxyb1XXc4xaisi/K6qKukqZhKPrEQyRkdNIncgYyLoDTReq0nNyuKerTg==", "dev": true, - "license": "ISC", + "license": "MIT", + "engines": { + "node": "*" + } + }, + "node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "license": "MIT", "dependencies": { - "natural-compare-lite": "^1.4.0" + "ms": "^2.1.3" }, "engines": { - "node": ">=12" + "node": ">=6.0" }, - "peerDependencies": { - "eslint": "5 - 9" + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } } }, - "node_modules/eslint-plugin-unicorn": { - "version": "62.0.0", + "node_modules/decimal.js": { + "version": "10.6.0", + "resolved": "https://registry.npmjs.org/decimal.js/-/decimal.js-10.6.0.tgz", + "integrity": "sha512-YpgQiITW3JXGntzdUmyUR1V812Hn8T1YVXhCu+wO3OpS4eU9l4YdD3qjyiKdV6mvV29zapkMeD390UVEf2lkUg==", "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-validator-identifier": "^7.28.5", - "@eslint-community/eslint-utils": "^4.9.0", - "@eslint/plugin-kit": "^0.4.0", - "change-case": "^5.4.4", - "ci-info": "^4.3.1", - "clean-regexp": "^1.0.0", - "core-js-compat": "^3.46.0", - "esquery": "^1.6.0", - "find-up-simple": "^1.0.1", - "globals": "^16.4.0", - "indent-string": "^5.0.0", - "is-builtin-module": "^5.0.0", - "jsesc": "^3.1.0", - "pluralize": "^8.0.0", - "regexp-tree": "^0.1.27", - "regjsparser": "^0.13.0", - "semver": "^7.7.3", - "strip-indent": "^4.1.1" + "license": "MIT" + }, + "node_modules/decko": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/decko/-/decko-1.2.0.tgz", + "integrity": "sha512-m8FnyHXV1QX+S1cl+KPFDIl6NMkxtKsy6+U/aYyjrOqWMuwAwYWu7ePqrsUHtDR5Y8Yk2pi/KIDSgF+vT4cPOQ==", + "dev": true + }, + "node_modules/dedent": { + "version": "1.7.1", + "resolved": "https://registry.npmjs.org/dedent/-/dedent-1.7.1.tgz", + "integrity": "sha512-9JmrhGZpOlEgOLdQgSm0zxFaYoQon408V1v49aqTWuXENVlnCuY9JBZcXZiCsZQWDjTm5Qf/nIvAy77mXDAjEg==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "babel-plugin-macros": "^3.1.0" }, + "peerDependenciesMeta": { + "babel-plugin-macros": { + "optional": true + } + } + }, + "node_modules/deep-extend": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", + "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==", + "license": "MIT", "engines": { - "node": "^20.10.0 || >=21.0.0" - }, - "funding": { - "url": "https://github.com/sindresorhus/eslint-plugin-unicorn?sponsor=1" - }, - "peerDependencies": { - "eslint": ">=9.38.0" + "node": ">=4.0.0" } }, - "node_modules/eslint-plugin-unicorn/node_modules/globals": { - "version": "16.4.0", + "node_modules/deep-is": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", + "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", + "dev": true, + "license": "MIT" + }, + "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==", "dev": true, "license": "MIT", "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">=0.10.0" } }, - "node_modules/eslint-plugin-unicorn/node_modules/semver": { - "version": "7.7.3", + "node_modules/defaults": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/defaults/-/defaults-1.0.4.tgz", + "integrity": "sha512-eFuaLoy/Rxalv2kr+lqMlUnrDWV+3j4pljOIJgLIhI058IQfWJ7vXhyEIHu+HtC738klGALYxOKDO0bQP3tg8A==", "dev": true, - "license": "ISC", - "bin": { - "semver": "bin/semver.js" + "license": "MIT", + "dependencies": { + "clone": "^1.0.2" }, - "engines": { - "node": ">=10" + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/eslint-scope": { - "version": "8.4.0", - "dev": true, - "license": "BSD-2-Clause", + "node_modules/define-data-property": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz", + "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==", + "license": "MIT", "dependencies": { - "esrecurse": "^4.3.0", - "estraverse": "^5.2.0" + "es-define-property": "^1.0.0", + "es-errors": "^1.3.0", + "gopd": "^1.0.1" }, "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + "node": ">= 0.4" }, "funding": { - "url": "https://opencollective.com/eslint" + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/eslint-visitor-keys": { - "version": "3.4.3", + "node_modules/define-properties": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.2.1.tgz", + "integrity": "sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==", "dev": true, - "license": "Apache-2.0", + "license": "MIT", + "dependencies": { + "define-data-property": "^1.0.1", + "has-property-descriptors": "^1.0.0", + "object-keys": "^1.1.1" + }, "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + "node": ">= 0.4" }, "funding": { - "url": "https://opencollective.com/eslint" + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/eslint/node_modules/ajv": { - "version": "6.12.6", + "node_modules/degenerator": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/degenerator/-/degenerator-5.0.1.tgz", + "integrity": "sha512-TllpMR/t0M5sqCXfj85i4XaAzxmS5tVA16dqvdkMwGmzI+dXLXnw3J+3Vdv7VKw+ThlTMboK6i9rnZ6Nntj5CQ==", "dev": true, "license": "MIT", "dependencies": { - "fast-deep-equal": "^3.1.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", - "uri-js": "^4.2.2" + "ast-types": "^0.13.4", + "escodegen": "^2.1.0", + "esprima": "^4.0.1" }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" + "engines": { + "node": ">= 14" } }, - "node_modules/eslint/node_modules/brace-expansion": { - "version": "1.1.12", - "dev": true, + "node_modules/delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" + "engines": { + "node": ">=0.4.0" } }, - "node_modules/eslint/node_modules/eslint-visitor-keys": { - "version": "4.2.1", - "dev": true, - "license": "Apache-2.0", + "node_modules/depd": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", + "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", + "license": "MIT", "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" + "node": ">= 0.8" } }, - "node_modules/eslint/node_modules/glob-parent": { - "version": "6.0.2", + "node_modules/dependency-graph": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/dependency-graph/-/dependency-graph-0.11.0.tgz", + "integrity": "sha512-JeMq7fEshyepOWDfcfHK06N3MhyPhz++vtqWhMT5O9A3K42rdsEDpfdVqjaqaAhsw6a+ZqeDvQVtD0hFHQWrzg==", "dev": true, - "license": "ISC", - "dependencies": { - "is-glob": "^4.0.3" - }, + "license": "MIT", "engines": { - "node": ">=10.13.0" + "node": ">= 0.6.0" } }, - "node_modules/eslint/node_modules/ignore": { - "version": "5.3.2", + "node_modules/detect-libc": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz", + "integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==", + "license": "Apache-2.0", + "engines": { + "node": ">=8" + } + }, + "node_modules/detect-newline": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/detect-newline/-/detect-newline-3.1.0.tgz", + "integrity": "sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA==", "dev": true, "license": "MIT", "engines": { - "node": ">= 4" + "node": ">=8" } }, - "node_modules/eslint/node_modules/json-schema-traverse": { - "version": "0.4.1", + "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": "MIT" + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.3.1" + } }, - "node_modules/eslint/node_modules/minimatch": { - "version": "3.1.2", + "node_modules/docker-compose": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/docker-compose/-/docker-compose-1.3.0.tgz", + "integrity": "sha512-7Gevk/5eGD50+eMD+XDnFnOrruFkL0kSd7jEG4cjmqweDSUhB7i0g8is/nBdVpl+Bx338SqIB2GLKm32M+Vs6g==", "dev": true, - "license": "ISC", + "license": "MIT", "dependencies": { - "brace-expansion": "^1.1.7" + "yaml": "^2.2.2" }, "engines": { - "node": "*" + "node": ">= 6.0.0" } }, - "node_modules/espree": { - "version": "10.4.0", + "node_modules/docker-modem": { + "version": "5.0.6", + "resolved": "https://registry.npmjs.org/docker-modem/-/docker-modem-5.0.6.tgz", + "integrity": "sha512-ens7BiayssQz/uAxGzH8zGXCtiV24rRWXdjNha5V4zSOcxmAZsfGVm/PPFbwQdqEkDnhG+SyR9E3zSHUbOKXBQ==", "dev": true, - "license": "BSD-2-Clause", + "license": "Apache-2.0", "dependencies": { - "acorn": "^8.15.0", - "acorn-jsx": "^5.3.2", - "eslint-visitor-keys": "^4.2.1" + "debug": "^4.1.1", + "readable-stream": "^3.5.0", + "split-ca": "^1.0.1", + "ssh2": "^1.15.0" }, "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" + "node": ">= 8.0" } }, - "node_modules/espree/node_modules/eslint-visitor-keys": { - "version": "4.2.1", + "node_modules/dockerode": { + "version": "4.0.9", + "resolved": "https://registry.npmjs.org/dockerode/-/dockerode-4.0.9.tgz", + "integrity": "sha512-iND4mcOWhPaCNh54WmK/KoSb35AFqPAUWFMffTQcp52uQt36b5uNwEJTSXntJZBbeGad72Crbi/hvDIv6us/6Q==", "dev": true, "license": "Apache-2.0", - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + "dependencies": { + "@balena/dockerignore": "^1.0.2", + "@grpc/grpc-js": "^1.11.1", + "@grpc/proto-loader": "^0.7.13", + "docker-modem": "^5.0.6", + "protobufjs": "^7.3.2", + "tar-fs": "^2.1.4", + "uuid": "^10.0.0" }, - "funding": { - "url": "https://opencollective.com/eslint" + "engines": { + "node": ">= 8.0" } }, - "node_modules/esprima": { - "version": "4.0.1", + "node_modules/dockerode/node_modules/tar-fs": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-2.1.4.tgz", + "integrity": "sha512-mDAjwmZdh7LTT6pNleZ05Yt65HC3E+NiQzl672vQG38jIrehtJk/J3mNwIg+vShQPcLF/LV7CMnDW6vjj6sfYQ==", "dev": true, - "license": "BSD-2-Clause", - "bin": { - "esparse": "bin/esparse.js", - "esvalidate": "bin/esvalidate.js" - }, - "engines": { - "node": ">=4" + "license": "MIT", + "dependencies": { + "chownr": "^1.1.1", + "mkdirp-classic": "^0.5.2", + "pump": "^3.0.0", + "tar-stream": "^2.1.4" } }, - "node_modules/esquery": { - "version": "1.6.0", + "node_modules/dockerode/node_modules/tar-stream": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-2.2.0.tgz", + "integrity": "sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==", "dev": true, - "license": "BSD-3-Clause", + "license": "MIT", "dependencies": { - "estraverse": "^5.1.0" + "bl": "^4.0.3", + "end-of-stream": "^1.4.1", + "fs-constants": "^1.0.0", + "inherits": "^2.0.3", + "readable-stream": "^3.1.1" }, "engines": { - "node": ">=0.10" + "node": ">=6" } }, - "node_modules/esrecurse": { - "version": "4.3.0", + "node_modules/dockerode/node_modules/uuid": { + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-10.0.0.tgz", + "integrity": "sha512-8XkAphELsDnEGrDxUOHB3RGvXz6TeuYSGEZBOjtTtPm2lwhGBjLgOzLHB63IUWfBpNucQjND6d3AOudO+H3RWQ==", "dev": true, - "license": "BSD-2-Clause", + "funding": [ + "https://github.com/sponsors/broofa", + "https://github.com/sponsors/ctavan" + ], + "license": "MIT", + "bin": { + "uuid": "dist/bin/uuid" + } + }, + "node_modules/doctrine": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz", + "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==", + "dev": true, + "license": "Apache-2.0", "dependencies": { - "estraverse": "^5.2.0" + "esutils": "^2.0.2" }, "engines": { - "node": ">=4.0" + "node": ">=0.10.0" } }, - "node_modules/estraverse": { - "version": "5.3.0", + "node_modules/dom-serializer": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-2.0.0.tgz", + "integrity": "sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==", "dev": true, - "license": "BSD-2-Clause", - "engines": { - "node": ">=4.0" + "license": "MIT", + "dependencies": { + "domelementtype": "^2.3.0", + "domhandler": "^5.0.2", + "entities": "^4.2.0" + }, + "funding": { + "url": "https://github.com/cheeriojs/dom-serializer?sponsor=1" } }, - "node_modules/esutils": { - "version": "2.0.3", + "node_modules/dom-serializer/node_modules/entities": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", + "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==", "dev": true, "license": "BSD-2-Clause", "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/event-target-shim": { - "version": "5.0.1", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" } }, - "node_modules/eventemitter3": { - "version": "5.0.1", + "node_modules/domelementtype": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.3.0.tgz", + "integrity": "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==", "dev": true, - "license": "MIT" + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fb55" + } + ], + "license": "BSD-2-Clause" }, - "node_modules/events": { - "version": "3.3.0", + "node_modules/domhandler": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-5.0.3.tgz", + "integrity": "sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==", "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.8.x" - } - }, - "node_modules/execa": { - "version": "5.1.1", - "license": "MIT", + "license": "BSD-2-Clause", "dependencies": { - "cross-spawn": "^7.0.3", - "get-stream": "^6.0.0", - "human-signals": "^2.1.0", - "is-stream": "^2.0.0", - "merge-stream": "^2.0.0", - "npm-run-path": "^4.0.1", - "onetime": "^5.1.2", - "signal-exit": "^3.0.3", - "strip-final-newline": "^2.0.0" + "domelementtype": "^2.3.0" }, "engines": { - "node": ">=10" + "node": ">= 4" }, "funding": { - "url": "https://github.com/sindresorhus/execa?sponsor=1" + "url": "https://github.com/fb55/domhandler?sponsor=1" } }, - "node_modules/execa/node_modules/signal-exit": { - "version": "3.0.7", - "license": "ISC" + "node_modules/dompurify": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/dompurify/-/dompurify-3.3.1.tgz", + "integrity": "sha512-qkdCKzLNtrgPFP1Vo+98FRzJnBRGe4ffyCea9IwHB1fyxPOeNTHpLKYGd4Uk9xvNoH0ZoOjwZxNptyMwqrId1Q==", + "dev": true, + "license": "(MPL-2.0 OR Apache-2.0)", + "optionalDependencies": { + "@types/trusted-types": "^2.0.7" + } }, - "node_modules/exit": { - "version": "0.1.2", + "node_modules/domutils": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/domutils/-/domutils-3.2.2.tgz", + "integrity": "sha512-6kZKyUajlDuqlHKVX1w7gyslj9MPIXzIFiz/rGu35uC1wMi+kMhQwGhl4lt9unC9Vb9INnY9Z3/ZA3+FhASLaw==", "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "dom-serializer": "^2.0.0", + "domelementtype": "^2.3.0", + "domhandler": "^5.0.3" + }, + "funding": { + "url": "https://github.com/fb55/domutils?sponsor=1" + } + }, + "node_modules/dotenv": { + "version": "17.2.3", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-17.2.3.tgz", + "integrity": "sha512-JVUnt+DUIzu87TABbhPmNfVdBDt18BLOWjMUFJMSi/Qqg7NTYtabbvSNJGOJ7afbRuv9D/lngizHtP7QyLQ+9w==", + "license": "BSD-2-Clause", "engines": { - "node": ">= 0.8.0" + "node": ">=12" + }, + "funding": { + "url": "https://dotenvx.com" } }, - "node_modules/exit-x": { - "version": "0.2.2", - "dev": true, + "node_modules/drange": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/drange/-/drange-1.1.1.tgz", + "integrity": "sha512-pYxfDYpued//QpnLIm4Avk7rsNtAtQkUES2cwAYSvD/wd2pKD71gN2Ebj3e7klzXwjocvE8c5vx/1fxwpqmSxA==", "license": "MIT", "engines": { - "node": ">= 0.8.0" + "node": ">=4" } }, - "node_modules/expect": { - "version": "29.7.0", - "dev": true, + "node_modules/dunder-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", + "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", "license": "MIT", "dependencies": { - "@jest/expect-utils": "^29.7.0", - "jest-get-type": "^29.6.3", - "jest-matcher-utils": "^29.7.0", - "jest-message-util": "^29.7.0", - "jest-util": "^29.7.0" + "call-bind-apply-helpers": "^1.0.1", + "es-errors": "^1.3.0", + "gopd": "^1.2.0" }, "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + "node": ">= 0.4" } }, - "node_modules/fast-deep-equal": { - "version": "3.1.3", - "license": "MIT" - }, - "node_modules/fast-diff": { - "version": "1.3.0", - "dev": true, - "license": "Apache-2.0" - }, - "node_modules/fast-fifo": { - "version": "1.3.2", - "dev": true, + "node_modules/eastasianwidth": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", + "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", "license": "MIT" }, - "node_modules/fast-glob": { - "version": "3.3.3", + "node_modules/easy-table": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/easy-table/-/easy-table-1.1.0.tgz", + "integrity": "sha512-oq33hWOSSnl2Hoh00tZWaIPi1ievrD9aFG82/IgjlycAnW9hHx5PkJiXpxPsgEE+H7BsbVQXFVFST8TEXS6/pA==", "dev": true, "license": "MIT", - "dependencies": { - "@nodelib/fs.stat": "^2.0.2", - "@nodelib/fs.walk": "^1.2.3", - "glob-parent": "^5.1.2", - "merge2": "^1.3.0", - "micromatch": "^4.0.8" - }, - "engines": { - "node": ">=8.6.0" + "optionalDependencies": { + "wcwidth": ">=1.0.1" } }, - "node_modules/fast-json-stable-stringify": { - "version": "2.1.0", - "dev": true, + "node_modules/ee-first": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", + "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==", "license": "MIT" }, - "node_modules/fast-levenshtein": { - "version": "2.0.6", + "node_modules/electron-to-chromium": { + "version": "1.5.267", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.267.tgz", + "integrity": "sha512-0Drusm6MVRXSOJpGbaSVgcQsuB4hEkMpHXaVstcPmhu5LIedxs1xNK/nIxmQIU/RPC0+1/o0AVZfBTkTNJOdUw==", "dev": true, - "license": "MIT" + "license": "ISC" }, - "node_modules/fast-redact": { - "version": "3.5.0", + "node_modules/emittery": { + "version": "0.13.1", + "resolved": "https://registry.npmjs.org/emittery/-/emittery-0.13.1.tgz", + "integrity": "sha512-DeWwawk6r5yR9jFgnDKYt4sLS0LmHJJi3ZOnb5/JdbYwj3nW+FxQnHIjhBKz8YLC7oRNPVM9NQ47I3CVx34eqQ==", + "dev": true, "license": "MIT", "engines": { - "node": ">=6" + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sindresorhus/emittery?sponsor=1" } }, - "node_modules/fast-safe-stringify": { - "version": "2.1.1", - "dev": true, + "node_modules/emoji-regex": { + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", + "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", "license": "MIT" }, - "node_modules/fast-uri": { - "version": "3.0.6", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/fastify" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/fastify" - } - ], - "license": "BSD-3-Clause" + "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/fast-xml-parser": { - "version": "4.5.3", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/NaturalIntelligence" - } - ], + "node_modules/end-of-stream": { + "version": "1.4.5", + "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.5.tgz", + "integrity": "sha512-ooEGc6HP26xXq/N+GCGOT0JKCLDGrq2bQUZrQ7gyrJiZANJ/8YDTxTpQBXGMn+WbIQXNVpyWymm7KYVICQnyOg==", "license": "MIT", "dependencies": { - "strnum": "^1.1.1" + "once": "^1.4.0" + } + }, + "node_modules/entities": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/entities/-/entities-6.0.1.tgz", + "integrity": "sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.12" }, - "bin": { - "fxparser": "src/cli/cli.js" + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" } }, - "node_modules/fastq": { - "version": "1.19.1", + "node_modules/error-ex": { + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.4.tgz", + "integrity": "sha512-sqQamAnR14VgCr1A618A3sGrygcpK+HEbenA/HiEAkkUwcZIIB/tgWqHFxWgOyDh4nB4JCRimh79dR5Ywc9MDQ==", "dev": true, - "license": "ISC", + "license": "MIT", "dependencies": { - "reusify": "^1.0.4" + "is-arrayish": "^0.2.1" } }, - "node_modules/fb-watchman": { - "version": "2.0.2", + "node_modules/es-abstract": { + "version": "1.24.1", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.24.1.tgz", + "integrity": "sha512-zHXBLhP+QehSSbsS9Pt23Gg964240DPd6QCf8WpkqEXxQ7fhdZzYsocOr5u7apWonsS5EjZDmTF+/slGMyasvw==", "dev": true, - "license": "Apache-2.0", + "license": "MIT", "dependencies": { - "bser": "2.1.1" + "array-buffer-byte-length": "^1.0.2", + "arraybuffer.prototype.slice": "^1.0.4", + "available-typed-arrays": "^1.0.7", + "call-bind": "^1.0.8", + "call-bound": "^1.0.4", + "data-view-buffer": "^1.0.2", + "data-view-byte-length": "^1.0.2", + "data-view-byte-offset": "^1.0.1", + "es-define-property": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.1.1", + "es-set-tostringtag": "^2.1.0", + "es-to-primitive": "^1.3.0", + "function.prototype.name": "^1.1.8", + "get-intrinsic": "^1.3.0", + "get-proto": "^1.0.1", + "get-symbol-description": "^1.1.0", + "globalthis": "^1.0.4", + "gopd": "^1.2.0", + "has-property-descriptors": "^1.0.2", + "has-proto": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2", + "internal-slot": "^1.1.0", + "is-array-buffer": "^3.0.5", + "is-callable": "^1.2.7", + "is-data-view": "^1.0.2", + "is-negative-zero": "^2.0.3", + "is-regex": "^1.2.1", + "is-set": "^2.0.3", + "is-shared-array-buffer": "^1.0.4", + "is-string": "^1.1.1", + "is-typed-array": "^1.1.15", + "is-weakref": "^1.1.1", + "math-intrinsics": "^1.1.0", + "object-inspect": "^1.13.4", + "object-keys": "^1.1.1", + "object.assign": "^4.1.7", + "own-keys": "^1.0.1", + "regexp.prototype.flags": "^1.5.4", + "safe-array-concat": "^1.1.3", + "safe-push-apply": "^1.0.0", + "safe-regex-test": "^1.1.0", + "set-proto": "^1.0.0", + "stop-iteration-iterator": "^1.1.0", + "string.prototype.trim": "^1.2.10", + "string.prototype.trimend": "^1.0.9", + "string.prototype.trimstart": "^1.0.8", + "typed-array-buffer": "^1.0.3", + "typed-array-byte-length": "^1.0.3", + "typed-array-byte-offset": "^1.0.4", + "typed-array-length": "^1.0.7", + "unbox-primitive": "^1.1.0", + "which-typed-array": "^1.1.19" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/fflate": { - "version": "0.8.2", - "dev": true, - "license": "MIT" - }, - "node_modules/figures": { - "version": "3.2.0", + "node_modules/es-aggregate-error": { + "version": "1.0.14", + "resolved": "https://registry.npmjs.org/es-aggregate-error/-/es-aggregate-error-1.0.14.tgz", + "integrity": "sha512-3YxX6rVb07B5TV11AV5wsL7nQCHXNwoHPsQC8S4AmBiqYhyNCJ5BRKXkXyDJvs8QzXN20NgRtxe3dEEQD9NLHA==", "dev": true, "license": "MIT", "dependencies": { - "escape-string-regexp": "^1.0.5" + "define-data-property": "^1.1.4", + "define-properties": "^1.2.1", + "es-abstract": "^1.24.0", + "es-errors": "^1.3.0", + "function-bind": "^1.1.2", + "globalthis": "^1.0.4", + "has-property-descriptors": "^1.0.2", + "set-function-name": "^2.0.2" }, "engines": { - "node": ">=8" + "node": ">= 0.4" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/figures/node_modules/escape-string-regexp": { - "version": "1.0.5", - "dev": true, + "node_modules/es-define-property": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", + "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", "license": "MIT", "engines": { - "node": ">=0.8.0" + "node": ">= 0.4" } }, - "node_modules/file-entry-cache": { - "version": "8.0.0", - "dev": true, + "node_modules/es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", "license": "MIT", - "dependencies": { - "flat-cache": "^4.0.0" - }, "engines": { - "node": ">=16.0.0" + "node": ">= 0.4" } }, - "node_modules/file-type": { - "version": "21.0.0", + "node_modules/es-iterator-helpers": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/es-iterator-helpers/-/es-iterator-helpers-1.2.2.tgz", + "integrity": "sha512-BrUQ0cPTB/IwXj23HtwHjS9n7O4h9FX94b4xc5zlTHxeLgTAdzYUDyy6KdExAl9lbN5rtfe44xpjpmj9grxs5w==", "dev": true, "license": "MIT", "dependencies": { - "@tokenizer/inflate": "^0.2.7", - "strtok3": "^10.2.2", - "token-types": "^6.0.0", - "uint8array-extras": "^1.4.0" + "call-bind": "^1.0.8", + "call-bound": "^1.0.4", + "define-properties": "^1.2.1", + "es-abstract": "^1.24.1", + "es-errors": "^1.3.0", + "es-set-tostringtag": "^2.1.0", + "function-bind": "^1.1.2", + "get-intrinsic": "^1.3.0", + "globalthis": "^1.0.4", + "gopd": "^1.2.0", + "has-property-descriptors": "^1.0.2", + "has-proto": "^1.2.0", + "has-symbols": "^1.1.0", + "internal-slot": "^1.1.0", + "iterator.prototype": "^1.1.5", + "safe-array-concat": "^1.1.3" }, "engines": { - "node": ">=20" - }, - "funding": { - "url": "https://github.com/sindresorhus/file-type?sponsor=1" + "node": ">= 0.4" } }, - "node_modules/fill-range": { - "version": "7.1.1", - "dev": true, + "node_modules/es-object-atoms": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", + "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", "license": "MIT", "dependencies": { - "to-regex-range": "^5.0.1" + "es-errors": "^1.3.0" }, "engines": { - "node": ">=8" + "node": ">= 0.4" } }, - "node_modules/find-up": { - "version": "5.0.0", - "dev": true, + "node_modules/es-set-tostringtag": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz", + "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==", "license": "MIT", "dependencies": { - "locate-path": "^6.0.0", - "path-exists": "^4.0.0" - }, - "engines": { - "node": ">=10" + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.6", + "has-tostringtag": "^1.0.2", + "hasown": "^2.0.2" }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/find-up-simple": { - "version": "1.0.1", - "dev": true, - "license": "MIT", "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">= 0.4" } }, - "node_modules/flat-cache": { - "version": "4.0.1", + "node_modules/es-shim-unscopables": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/es-shim-unscopables/-/es-shim-unscopables-1.1.0.tgz", + "integrity": "sha512-d9T8ucsEhh8Bi1woXCf+TIKDIROLG5WCkxg8geBCbvk22kzwC5G2OnXVMO6FUsvQlgUUXQ2itephWDLqDzbeCw==", "dev": true, "license": "MIT", "dependencies": { - "flatted": "^3.2.9", - "keyv": "^4.5.4" + "hasown": "^2.0.2" }, "engines": { - "node": ">=16" - } - }, - "node_modules/flatted": { - "version": "3.3.3", - "dev": true, - "license": "ISC" - }, - "node_modules/follow-redirects": { - "version": "1.15.11", - "dev": true, - "funding": [ - { - "type": "individual", - "url": "https://github.com/sponsors/RubenVerborgh" - } - ], - "license": "MIT", - "engines": { - "node": ">=4.0" - }, - "peerDependenciesMeta": { - "debug": { - "optional": true - } + "node": ">= 0.4" } }, - "node_modules/for-each": { - "version": "0.3.5", + "node_modules/es-to-primitive": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.3.0.tgz", + "integrity": "sha512-w+5mJ3GuFL+NjVtJlvydShqE1eN3h3PbI7/5LAsYJP/2qtuMXjfL2LpHSRqo4b4eSF5K/DH1JXKUAHSB2UW50g==", "dev": true, "license": "MIT", "dependencies": { - "is-callable": "^1.2.7" + "is-callable": "^1.2.7", + "is-date-object": "^1.0.5", + "is-symbol": "^1.0.4" }, "engines": { "node": ">= 0.4" @@ -13776,3258 +10705,3788 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/foreach": { - "version": "2.0.6", + "node_modules/es6-promise": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-3.3.1.tgz", + "integrity": "sha512-SOp9Phqvqn7jtEUxPWdWfWoLmyt2VaJ6MpvP9Comy1MceMXqE6bxvaTu4iaxpYYPzhny28Lc+M87/c2cPK6lDg==", "dev": true, "license": "MIT" }, - "node_modules/foreground-child": { - "version": "3.3.1", - "dev": true, - "license": "ISC", - "dependencies": { - "cross-spawn": "^7.0.6", - "signal-exit": "^4.0.1" + "node_modules/esbuild": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.12.tgz", + "integrity": "sha512-bbPBYYrtZbkt6Os6FiTLCTFxvq4tt3JKall1vRwshA3fdVztsLAatFaZobhkBC8/BrPetoa0oksYoKXoG4ryJg==", + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" }, "engines": { - "node": ">=14" + "node": ">=18" }, - "funding": { - "url": "https://github.com/sponsors/isaacs" + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.25.12", + "@esbuild/android-arm": "0.25.12", + "@esbuild/android-arm64": "0.25.12", + "@esbuild/android-x64": "0.25.12", + "@esbuild/darwin-arm64": "0.25.12", + "@esbuild/darwin-x64": "0.25.12", + "@esbuild/freebsd-arm64": "0.25.12", + "@esbuild/freebsd-x64": "0.25.12", + "@esbuild/linux-arm": "0.25.12", + "@esbuild/linux-arm64": "0.25.12", + "@esbuild/linux-ia32": "0.25.12", + "@esbuild/linux-loong64": "0.25.12", + "@esbuild/linux-mips64el": "0.25.12", + "@esbuild/linux-ppc64": "0.25.12", + "@esbuild/linux-riscv64": "0.25.12", + "@esbuild/linux-s390x": "0.25.12", + "@esbuild/linux-x64": "0.25.12", + "@esbuild/netbsd-arm64": "0.25.12", + "@esbuild/netbsd-x64": "0.25.12", + "@esbuild/openbsd-arm64": "0.25.12", + "@esbuild/openbsd-x64": "0.25.12", + "@esbuild/openharmony-arm64": "0.25.12", + "@esbuild/sunos-x64": "0.25.12", + "@esbuild/win32-arm64": "0.25.12", + "@esbuild/win32-ia32": "0.25.12", + "@esbuild/win32-x64": "0.25.12" + } + }, + "node_modules/esbuild/node_modules/@esbuild/aix-ppc64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.12.tgz", + "integrity": "sha512-Hhmwd6CInZ3dwpuGTF8fJG6yoWmsToE+vYgD4nytZVxcu1ulHpUQRAB1UJ8+N1Am3Mz4+xOByoQoSZf4D+CpkA==", + "cpu": [ + "ppc64" + ], + "license": "MIT", + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=18" } }, - "node_modules/form-data": { - "version": "4.0.4", - "dev": true, + "node_modules/esbuild/node_modules/@esbuild/android-arm": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.12.tgz", + "integrity": "sha512-VJ+sKvNA/GE7Ccacc9Cha7bpS8nyzVv0jdVgwNDaR4gDMC/2TTRc33Ip8qrNYUcpkOHUT5OZ0bUcNNVZQ9RLlg==", + "cpu": [ + "arm" + ], "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" - }, + "optional": true, + "os": [ + "android" + ], "engines": { - "node": ">= 6" + "node": ">=18" } }, - "node_modules/fs-constants": { - "version": "1.0.0", - "dev": true, - "license": "MIT" + "node_modules/esbuild/node_modules/@esbuild/android-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.12.tgz", + "integrity": "sha512-6AAmLG7zwD1Z159jCKPvAxZd4y/VTO0VkprYy+3N2FtJ8+BQWFXU+OxARIwA46c5tdD9SsKGZ/1ocqBS/gAKHg==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } }, - "node_modules/fs-extra": { - "version": "11.3.2", - "dev": true, + "node_modules/esbuild/node_modules/@esbuild/android-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.12.tgz", + "integrity": "sha512-5jbb+2hhDHx5phYR2By8GTWEzn6I9UqR11Kwf22iKbNpYrsmRB18aX/9ivc5cabcUiAT/wM+YIZ6SG9QO6a8kg==", + "cpu": [ + "x64" + ], "license": "MIT", - "dependencies": { - "graceful-fs": "^4.2.0", - "jsonfile": "^6.0.1", - "universalify": "^2.0.0" - }, + "optional": true, + "os": [ + "android" + ], "engines": { - "node": ">=14.14" + "node": ">=18" } }, - "node_modules/fs.realpath": { - "version": "1.0.0", - "dev": true, - "license": "ISC" + "node_modules/esbuild/node_modules/@esbuild/darwin-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.12.tgz", + "integrity": "sha512-N3zl+lxHCifgIlcMUP5016ESkeQjLj/959RxxNYIthIg+CQHInujFuXeWbWMgnTo4cp5XVHqFPmpyu9J65C1Yg==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } }, - "node_modules/fsevents": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", - "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", - "dev": true, - "hasInstallScript": true, + "node_modules/esbuild/node_modules/@esbuild/darwin-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.12.tgz", + "integrity": "sha512-HQ9ka4Kx21qHXwtlTUVbKJOAnmG1ipXhdWTmNXiPzPfWKpXqASVcWdnf2bnL73wgjNrFXAa3yYvBSd9pzfEIpA==", + "cpu": [ + "x64" + ], "license": "MIT", "optional": true, "os": [ "darwin" ], "engines": { - "node": "^8.16.0 || ^10.6.0 || >=11.0.0" - } - }, - "node_modules/function-bind": { - "version": "1.1.2", - "dev": true, - "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": ">=18" } }, - "node_modules/function.prototype.name": { - "version": "1.1.8", - "dev": true, + "node_modules/esbuild/node_modules/@esbuild/freebsd-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.12.tgz", + "integrity": "sha512-gA0Bx759+7Jve03K1S0vkOu5Lg/85dou3EseOGUes8flVOGxbhDDh/iZaoek11Y8mtyKPGF3vP8XhnkDEAmzeg==", + "cpu": [ + "arm64" + ], "license": "MIT", - "dependencies": { - "call-bind": "^1.0.8", - "call-bound": "^1.0.3", - "define-properties": "^1.2.1", - "functions-have-names": "^1.2.3", - "hasown": "^2.0.2", - "is-callable": "^1.2.7" - }, + "optional": true, + "os": [ + "freebsd" + ], "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/functional-red-black-tree": { - "version": "1.0.1", - "dev": true, - "license": "MIT" - }, - "node_modules/functions-have-names": { - "version": "1.2.3", - "dev": true, - "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": ">=18" } }, - "node_modules/gensync": { - "version": "1.0.0-beta.2", - "dev": true, + "node_modules/esbuild/node_modules/@esbuild/freebsd-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.12.tgz", + "integrity": "sha512-TGbO26Yw2xsHzxtbVFGEXBFH0FRAP7gtcPE7P5yP7wGy7cXK2oO7RyOhL5NLiqTlBh47XhmIUXuGciXEqYFfBQ==", + "cpu": [ + "x64" + ], "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], "engines": { - "node": ">=6.9.0" + "node": ">=18" } }, - "node_modules/get-caller-file": { - "version": "2.0.5", - "license": "ISC", + "node_modules/esbuild/node_modules/@esbuild/linux-arm": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.12.tgz", + "integrity": "sha512-lPDGyC1JPDou8kGcywY0YILzWlhhnRjdof3UlcoqYmS9El818LLfJJc3PXXgZHrHCAKs/Z2SeZtDJr5MrkxtOw==", + "cpu": [ + "arm" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], "engines": { - "node": "6.* || 8.* || >= 10.*" + "node": ">=18" } }, - "node_modules/get-intrinsic": { - "version": "1.3.0", - "dev": true, + "node_modules/esbuild/node_modules/@esbuild/linux-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.12.tgz", + "integrity": "sha512-8bwX7a8FghIgrupcxb4aUmYDLp8pX06rGh5HqDT7bB+8Rdells6mHvrFHHW2JAOPZUbnjUpKTLg6ECyzvas2AQ==", + "cpu": [ + "arm64" + ], "license": "MIT", - "dependencies": { - "call-bind-apply-helpers": "^1.0.2", - "es-define-property": "^1.0.1", - "es-errors": "^1.3.0", - "es-object-atoms": "^1.1.1", - "function-bind": "^1.1.2", - "get-proto": "^1.0.1", - "gopd": "^1.2.0", - "has-symbols": "^1.1.0", - "hasown": "^2.0.2", - "math-intrinsics": "^1.1.0" - }, + "optional": true, + "os": [ + "linux" + ], "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": ">=18" } }, - "node_modules/get-package-type": { - "version": "0.1.0", - "dev": true, + "node_modules/esbuild/node_modules/@esbuild/linux-ia32": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.12.tgz", + "integrity": "sha512-0y9KrdVnbMM2/vG8KfU0byhUN+EFCny9+8g202gYqSSVMonbsCfLjUO+rCci7pM0WBEtz+oK/PIwHkzxkyharA==", + "cpu": [ + "ia32" + ], "license": "MIT", + "optional": true, + "os": [ + "linux" + ], "engines": { - "node": ">=8.0.0" + "node": ">=18" } }, - "node_modules/get-port": { - "version": "7.1.0", - "dev": true, + "node_modules/esbuild/node_modules/@esbuild/linux-loong64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.12.tgz", + "integrity": "sha512-h///Lr5a9rib/v1GGqXVGzjL4TMvVTv+s1DPoxQdz7l/AYv6LDSxdIwzxkrPW438oUXiDtwM10o9PmwS/6Z0Ng==", + "cpu": [ + "loong64" + ], "license": "MIT", + "optional": true, + "os": [ + "linux" + ], "engines": { - "node": ">=16" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">=18" } }, - "node_modules/get-port-please": { - "version": "3.2.0", - "dev": true, - "license": "MIT" - }, - "node_modules/get-proto": { - "version": "1.0.1", - "dev": true, + "node_modules/esbuild/node_modules/@esbuild/linux-mips64el": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.12.tgz", + "integrity": "sha512-iyRrM1Pzy9GFMDLsXn1iHUm18nhKnNMWscjmp4+hpafcZjrr2WbT//d20xaGljXDBYHqRcl8HnxbX6uaA/eGVw==", + "cpu": [ + "mips64el" + ], "license": "MIT", - "dependencies": { - "dunder-proto": "^1.0.1", - "es-object-atoms": "^1.0.0" - }, + "optional": true, + "os": [ + "linux" + ], "engines": { - "node": ">= 0.4" + "node": ">=18" } }, - "node_modules/get-stream": { - "version": "6.0.1", + "node_modules/esbuild/node_modules/@esbuild/linux-ppc64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.12.tgz", + "integrity": "sha512-9meM/lRXxMi5PSUqEXRCtVjEZBGwB7P/D4yT8UG/mwIdze2aV4Vo6U5gD3+RsoHXKkHCfSxZKzmDssVlRj1QQA==", + "cpu": [ + "ppc64" + ], "license": "MIT", + "optional": true, + "os": [ + "linux" + ], "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">=18" } }, - "node_modules/get-symbol-description": { - "version": "1.1.0", - "dev": true, + "node_modules/esbuild/node_modules/@esbuild/linux-riscv64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.12.tgz", + "integrity": "sha512-Zr7KR4hgKUpWAwb1f3o5ygT04MzqVrGEGXGLnj15YQDJErYu/BGg+wmFlIDOdJp0PmB0lLvxFIOXZgFRrdjR0w==", + "cpu": [ + "riscv64" + ], "license": "MIT", - "dependencies": { - "call-bound": "^1.0.3", - "es-errors": "^1.3.0", - "get-intrinsic": "^1.2.6" - }, + "optional": true, + "os": [ + "linux" + ], "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": ">=18" } }, - "node_modules/get-tsconfig": { - "version": "4.10.1", - "dev": true, + "node_modules/esbuild/node_modules/@esbuild/linux-s390x": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.12.tgz", + "integrity": "sha512-MsKncOcgTNvdtiISc/jZs/Zf8d0cl/t3gYWX8J9ubBnVOwlk65UIEEvgBORTiljloIWnBzLs4qhzPkJcitIzIg==", + "cpu": [ + "s390x" + ], "license": "MIT", - "dependencies": { - "resolve-pkg-maps": "^1.0.0" - }, - "funding": { - "url": "https://github.com/privatenumber/get-tsconfig?sponsor=1" + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" } }, - "node_modules/get-uri": { - "version": "6.0.5", - "dev": true, + "node_modules/esbuild/node_modules/@esbuild/linux-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.12.tgz", + "integrity": "sha512-uqZMTLr/zR/ed4jIGnwSLkaHmPjOjJvnm6TVVitAa08SLS9Z0VM8wIRx7gWbJB5/J54YuIMInDquWyYvQLZkgw==", + "cpu": [ + "x64" + ], "license": "MIT", - "dependencies": { - "basic-ftp": "^5.0.2", - "data-uri-to-buffer": "^6.0.2", - "debug": "^4.3.4" - }, + "optional": true, + "os": [ + "linux" + ], "engines": { - "node": ">= 14" + "node": ">=18" } }, - "node_modules/glob": { - "version": "11.0.3", - "dev": true, - "license": "ISC", - "dependencies": { - "foreground-child": "^3.3.1", - "jackspeak": "^4.1.1", - "minimatch": "^10.0.3", - "minipass": "^7.1.2", - "package-json-from-dist": "^1.0.0", - "path-scurry": "^2.0.0" - }, - "bin": { - "glob": "dist/esm/bin.mjs" - }, + "node_modules/esbuild/node_modules/@esbuild/netbsd-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.12.tgz", + "integrity": "sha512-xXwcTq4GhRM7J9A8Gv5boanHhRa/Q9KLVmcyXHCTaM4wKfIpWkdXiMog/KsnxzJ0A1+nD+zoecuzqPmCRyBGjg==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], "engines": { - "node": "20 || >=22" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" + "node": ">=18" } }, - "node_modules/glob-parent": { - "version": "5.1.2", - "dev": true, - "license": "ISC", - "dependencies": { - "is-glob": "^4.0.1" - }, + "node_modules/esbuild/node_modules/@esbuild/netbsd-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.12.tgz", + "integrity": "sha512-Ld5pTlzPy3YwGec4OuHh1aCVCRvOXdH8DgRjfDy/oumVovmuSzWfnSJg+VtakB9Cm0gxNO9BzWkj6mtO1FMXkQ==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], "engines": { - "node": ">= 6" + "node": ">=18" } }, - "node_modules/glob/node_modules/minimatch": { - "version": "10.0.3", - "dev": true, - "license": "ISC", - "dependencies": { - "@isaacs/brace-expansion": "^5.0.0" - }, + "node_modules/esbuild/node_modules/@esbuild/openbsd-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.12.tgz", + "integrity": "sha512-fF96T6KsBo/pkQI950FARU9apGNTSlZGsv1jZBAlcLL1MLjLNIWPBkj5NlSz8aAzYKg+eNqknrUJ24QBybeR5A==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], "engines": { - "node": "20 || >=22" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" + "node": ">=18" } }, - "node_modules/globals": { - "version": "14.0.0", - "dev": true, + "node_modules/esbuild/node_modules/@esbuild/openbsd-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.12.tgz", + "integrity": "sha512-MZyXUkZHjQxUvzK7rN8DJ3SRmrVrke8ZyRusHlP+kuwqTcfWLyqMOE3sScPPyeIXN/mDJIfGXvcMqCgYKekoQw==", + "cpu": [ + "x64" + ], "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], "engines": { "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/globalthis": { - "version": "1.0.4", - "dev": true, + "node_modules/esbuild/node_modules/@esbuild/sunos-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.12.tgz", + "integrity": "sha512-3wGSCDyuTHQUzt0nV7bocDy72r2lI33QL3gkDNGkod22EsYl04sMf0qLb8luNKTOmgF/eDEDP5BFNwoBKH441w==", + "cpu": [ + "x64" + ], "license": "MIT", - "dependencies": { - "define-properties": "^1.2.1", - "gopd": "^1.0.1" - }, + "optional": true, + "os": [ + "sunos" + ], "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": ">=18" } }, - "node_modules/gopd": { - "version": "1.2.0", - "dev": true, + "node_modules/esbuild/node_modules/@esbuild/win32-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.12.tgz", + "integrity": "sha512-rMmLrur64A7+DKlnSuwqUdRKyd3UE7oPJZmnljqEptesKM8wx9J8gx5u0+9Pq0fQQW8vqeKebwNXdfOyP+8Bsg==", + "cpu": [ + "arm64" + ], "license": "MIT", + "optional": true, + "os": [ + "win32" + ], "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": ">=18" } }, - "node_modules/graceful-fs": { - "version": "4.2.11", - "dev": true, - "license": "ISC" - }, - "node_modules/graphemer": { - "version": "1.4.0", - "dev": true, - "license": "MIT" + "node_modules/esbuild/node_modules/@esbuild/win32-ia32": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.12.tgz", + "integrity": "sha512-HkqnmmBoCbCwxUKKNPBixiWDGCpQGVsrQfJoVGYLPT41XWF8lHuE5N6WhVia2n4o5QK5M4tYr21827fNhi4byQ==", + "cpu": [ + "ia32" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } }, - "node_modules/handlebars": { - "version": "4.7.8", - "dev": true, + "node_modules/esbuild/node_modules/@esbuild/win32-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.12.tgz", + "integrity": "sha512-alJC0uCZpTFrSL0CCDjcgleBXPnCrEAhTBILpeAp7M/OFgoqtAetfBzX0xM00MUsVVPpVjlPuMbREqnZCXaTnA==", + "cpu": [ + "x64" + ], "license": "MIT", - "dependencies": { - "minimist": "^1.2.5", - "neo-async": "^2.6.2", - "source-map": "^0.6.1", - "wordwrap": "^1.0.0" - }, - "bin": { - "handlebars": "bin/handlebars" - }, + "optional": true, + "os": [ + "win32" + ], "engines": { - "node": ">=0.4.7" - }, - "optionalDependencies": { - "uglify-js": "^3.1.4" + "node": ">=18" } }, - "node_modules/has-bigints": { - "version": "1.1.0", - "dev": true, + "node_modules/escalade": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", + "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", "license": "MIT", "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": ">=6" } }, - "node_modules/has-flag": { + "node_modules/escape-html": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==", + "license": "MIT" + }, + "node_modules/escape-string-regexp": { "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "dev": true, "license": "MIT", "engines": { - "node": ">=8" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/has-property-descriptors": { - "version": "1.0.2", + "node_modules/escodegen": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-2.1.0.tgz", + "integrity": "sha512-2NlIDTwUWJN0mRPQOdtQBzbUHvdGY2P1VXSyU83Q3xKxM7WHX2Ql8dKq782Q9TgQUNOLEzEYu9bzLNj1q88I5w==", "dev": true, - "license": "MIT", + "license": "BSD-2-Clause", "dependencies": { - "es-define-property": "^1.0.0" + "esprima": "^4.0.1", + "estraverse": "^5.2.0", + "esutils": "^2.0.2" }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "bin": { + "escodegen": "bin/escodegen.js", + "esgenerate": "bin/esgenerate.js" + }, + "engines": { + "node": ">=6.0" + }, + "optionalDependencies": { + "source-map": "~0.6.1" } }, - "node_modules/has-proto": { - "version": "1.2.0", + "node_modules/eslint": { + "version": "9.39.2", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.39.2.tgz", + "integrity": "sha512-LEyamqS7W5HB3ujJyvi0HQK/dtVINZvd5mAAp9eT5S/ujByGjiZLCzPcHVzuXbpJDJF/cxwHlfceVUDZ2lnSTw==", "dev": true, "license": "MIT", "dependencies": { - "dunder-proto": "^1.0.0" + "@eslint-community/eslint-utils": "^4.8.0", + "@eslint-community/regexpp": "^4.12.1", + "@eslint/config-array": "^0.21.1", + "@eslint/config-helpers": "^0.4.2", + "@eslint/core": "^0.17.0", + "@eslint/eslintrc": "^3.3.1", + "@eslint/js": "9.39.2", + "@eslint/plugin-kit": "^0.4.1", + "@humanfs/node": "^0.16.6", + "@humanwhocodes/module-importer": "^1.0.1", + "@humanwhocodes/retry": "^0.4.2", + "@types/estree": "^1.0.6", + "ajv": "^6.12.4", + "chalk": "^4.0.0", + "cross-spawn": "^7.0.6", + "debug": "^4.3.2", + "escape-string-regexp": "^4.0.0", + "eslint-scope": "^8.4.0", + "eslint-visitor-keys": "^4.2.1", + "espree": "^10.4.0", + "esquery": "^1.5.0", + "esutils": "^2.0.2", + "fast-deep-equal": "^3.1.3", + "file-entry-cache": "^8.0.0", + "find-up": "^5.0.0", + "glob-parent": "^6.0.2", + "ignore": "^5.2.0", + "imurmurhash": "^0.1.4", + "is-glob": "^4.0.0", + "json-stable-stringify-without-jsonify": "^1.0.1", + "lodash.merge": "^4.6.2", + "minimatch": "^3.1.2", + "natural-compare": "^1.4.0", + "optionator": "^0.9.3" + }, + "bin": { + "eslint": "bin/eslint.js" }, "engines": { - "node": ">= 0.4" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { - "url": "https://github.com/sponsors/ljharb" + "url": "https://eslint.org/donate" + }, + "peerDependencies": { + "jiti": "*" + }, + "peerDependenciesMeta": { + "jiti": { + "optional": true + } } }, - "node_modules/has-symbols": { - "version": "1.1.0", + "node_modules/eslint-config-airbnb-extended": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/eslint-config-airbnb-extended/-/eslint-config-airbnb-extended-1.0.11.tgz", + "integrity": "sha512-dtSU+blkYae887ur+IhZ6ZVzw2x3WABA/T1WUqmr6WcrD9HnrJ3KdAEu1joWxJObKT0GjcxeLOjH5DrHo3ZryQ==", "dev": true, "license": "MIT", - "engines": { - "node": ">= 0.4" + "dependencies": { + "confusing-browser-globals": "^1.0.11", + "globals": "^16.0.0" }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "peerDependencies": { + "@next/eslint-plugin-next": "15.x", + "@stylistic/eslint-plugin": "3.x", + "@types/eslint-plugin-jsx-a11y": "6.x", + "eslint": "9.x", + "eslint-import-resolver-typescript": "4.x", + "eslint-plugin-import-x": "4.x", + "eslint-plugin-jsx-a11y": "6.x", + "eslint-plugin-n": "17.x", + "eslint-plugin-react": "7.x", + "eslint-plugin-react-hooks": "5.x", + "typescript-eslint": "8.x" + }, + "peerDependenciesMeta": { + "@next/eslint-plugin-next": { + "optional": true + }, + "@stylistic/eslint-plugin": { + "optional": true + }, + "@types/eslint-plugin-jsx-a11y": { + "optional": true + }, + "eslint": { + "optional": false + }, + "eslint-import-resolver-typescript": { + "optional": false + }, + "eslint-plugin-jsx-a11y": { + "optional": false + }, + "eslint-plugin-n": { + "optional": true + }, + "eslint-plugin-react": { + "optional": true + }, + "eslint-plugin-react-hooks": { + "optional": true + }, + "typescript-eslint": { + "optional": true + } } }, - "node_modules/has-tostringtag": { - "version": "1.0.2", + "node_modules/eslint-config-airbnb-extended/node_modules/globals": { + "version": "16.5.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-16.5.0.tgz", + "integrity": "sha512-c/c15i26VrJ4IRt5Z89DnIzCGDn9EcebibhAOjw5ibqEHsE1wLUgkPn9RDmNcUKyU87GeaL633nyJ+pplFR2ZQ==", "dev": true, "license": "MIT", - "dependencies": { - "has-symbols": "^1.0.3" - }, "engines": { - "node": ">= 0.4" + "node": ">=18" }, "funding": { - "url": "https://github.com/sponsors/ljharb" + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/hasown": { - "version": "2.0.2", + "node_modules/eslint-config-next": { + "version": "15.5.9", + "resolved": "https://registry.npmjs.org/eslint-config-next/-/eslint-config-next-15.5.9.tgz", + "integrity": "sha512-852JYI3NkFNzW8CqsMhI0K2CDRxTObdZ2jQJj5CtpEaOkYHn13107tHpNuD/h0WRpU4FAbCdUaxQsrfBtNK9Kw==", "dev": true, "license": "MIT", "dependencies": { - "function-bind": "^1.1.2" + "@next/eslint-plugin-next": "15.5.9", + "@rushstack/eslint-patch": "^1.10.3", + "@typescript-eslint/eslint-plugin": "^5.4.2 || ^6.0.0 || ^7.0.0 || ^8.0.0", + "@typescript-eslint/parser": "^5.4.2 || ^6.0.0 || ^7.0.0 || ^8.0.0", + "eslint-import-resolver-node": "^0.3.6", + "eslint-import-resolver-typescript": "^3.5.2", + "eslint-plugin-import": "^2.31.0", + "eslint-plugin-jsx-a11y": "^6.10.0", + "eslint-plugin-react": "^7.37.0", + "eslint-plugin-react-hooks": "^5.0.0" }, - "engines": { - "node": ">= 0.4" + "peerDependencies": { + "eslint": "^7.23.0 || ^8.0.0 || ^9.0.0", + "typescript": ">=3.3.1" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } } }, - "node_modules/html-encoding-sniffer": { - "version": "4.0.0", + "node_modules/eslint-config-next/node_modules/eslint-import-resolver-typescript": { + "version": "3.10.1", + "resolved": "https://registry.npmjs.org/eslint-import-resolver-typescript/-/eslint-import-resolver-typescript-3.10.1.tgz", + "integrity": "sha512-A1rHYb06zjMGAxdLSkN2fXPBwuSaQ0iO5M/hdyS0Ajj1VBaRp0sPD3dn1FhME3c/JluGFbwSxyCfqdSbtQLAHQ==", "dev": true, - "license": "MIT", + "license": "ISC", "dependencies": { - "whatwg-encoding": "^3.1.1" + "@nolyfill/is-core-module": "1.0.39", + "debug": "^4.4.0", + "get-tsconfig": "^4.10.0", + "is-bun-module": "^2.0.0", + "stable-hash": "^0.0.5", + "tinyglobby": "^0.2.13", + "unrs-resolver": "^1.6.2" }, "engines": { - "node": ">=18" - } - }, - "node_modules/html-escaper": { - "version": "2.0.2", - "dev": true, - "license": "MIT" - }, - "node_modules/htmlparser2": { - "version": "10.0.0", - "dev": true, - "funding": [ - "https://github.com/fb55/htmlparser2?sponsor=1", - { - "type": "github", - "url": "https://github.com/sponsors/fb55" + "node": "^14.18.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint-import-resolver-typescript" + }, + "peerDependencies": { + "eslint": "*", + "eslint-plugin-import": "*", + "eslint-plugin-import-x": "*" + }, + "peerDependenciesMeta": { + "eslint-plugin-import": { + "optional": true + }, + "eslint-plugin-import-x": { + "optional": true } - ], - "license": "MIT", - "dependencies": { - "domelementtype": "^2.3.0", - "domhandler": "^5.0.3", - "domutils": "^3.2.1", - "entities": "^6.0.0" } }, - "node_modules/http-proxy-agent": { - "version": "7.0.2", + "node_modules/eslint-config-prettier": { + "version": "10.1.8", + "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-10.1.8.tgz", + "integrity": "sha512-82GZUjRS0p/jganf6q1rEO25VSoHH0hKPCTrgillPjdI/3bgBhAE1QzHrHTizjpRvy6pGAvKjDJtk2pF9NDq8w==", "dev": true, "license": "MIT", - "dependencies": { - "agent-base": "^7.1.0", - "debug": "^4.3.4" + "bin": { + "eslint-config-prettier": "bin/cli.js" }, - "engines": { - "node": ">= 14" + "funding": { + "url": "https://opencollective.com/eslint-config-prettier" + }, + "peerDependencies": { + "eslint": ">=7.0.0" } }, - "node_modules/http2-client": { - "version": "1.3.5", - "dev": true, - "license": "MIT" - }, - "node_modules/https-proxy-agent": { - "version": "7.0.6", + "node_modules/eslint-import-context": { + "version": "0.1.9", + "resolved": "https://registry.npmjs.org/eslint-import-context/-/eslint-import-context-0.1.9.tgz", + "integrity": "sha512-K9Hb+yRaGAGUbwjhFNHvSmmkZs9+zbuoe3kFQ4V1wYjrepUFYM2dZAfNtjbbj3qsPfUfsA68Bx/ICWQMi+C8Eg==", "dev": true, "license": "MIT", "dependencies": { - "agent-base": "^7.1.2", - "debug": "4" + "get-tsconfig": "^4.10.1", + "stable-hash-x": "^0.2.0" }, "engines": { - "node": ">= 14" - } - }, - "node_modules/human-signals": { - "version": "2.1.0", - "license": "Apache-2.0", - "engines": { - "node": ">=10.17.0" + "node": "^12.20.0 || ^14.18.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint-import-context" + }, + "peerDependencies": { + "unrs-resolver": "^1.0.0" + }, + "peerDependenciesMeta": { + "unrs-resolver": { + "optional": true + } } }, - "node_modules/iconv-lite": { - "version": "0.6.3", + "node_modules/eslint-import-resolver-node": { + "version": "0.3.9", + "resolved": "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.9.tgz", + "integrity": "sha512-WFj2isz22JahUv+B788TlO3N6zL3nNJGU8CcZbPZvVEkBPaJdCV4vy5wyghty5ROFbCRnm132v8BScu5/1BQ8g==", "dev": true, "license": "MIT", "dependencies": { - "safer-buffer": ">= 2.1.2 < 3.0.0" - }, - "engines": { - "node": ">=0.10.0" + "debug": "^3.2.7", + "is-core-module": "^2.13.0", + "resolve": "^1.22.4" } }, - "node_modules/ieee754": { - "version": "1.2.1", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "BSD-3-Clause" - }, - "node_modules/ignore": { - "version": "7.0.5", + "node_modules/eslint-import-resolver-node/node_modules/debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", "dev": true, "license": "MIT", - "engines": { - "node": ">= 4" + "dependencies": { + "ms": "^2.1.1" } }, - "node_modules/import-fresh": { - "version": "3.3.1", + "node_modules/eslint-import-resolver-typescript": { + "version": "4.4.4", + "resolved": "https://registry.npmjs.org/eslint-import-resolver-typescript/-/eslint-import-resolver-typescript-4.4.4.tgz", + "integrity": "sha512-1iM2zeBvrYmUNTj2vSC/90JTHDth+dfOfiNKkxApWRsTJYNrc8rOdxxIf5vazX+BiAXTeOT0UvWpGI/7qIWQOw==", "dev": true, - "license": "MIT", + "license": "ISC", "dependencies": { - "parent-module": "^1.0.0", - "resolve-from": "^4.0.0" + "debug": "^4.4.1", + "eslint-import-context": "^0.1.8", + "get-tsconfig": "^4.10.1", + "is-bun-module": "^2.0.0", + "stable-hash-x": "^0.2.0", + "tinyglobby": "^0.2.14", + "unrs-resolver": "^1.7.11" }, "engines": { - "node": ">=6" + "node": "^16.17.0 || >=18.6.0" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "url": "https://opencollective.com/eslint-import-resolver-typescript" + }, + "peerDependencies": { + "eslint": "*", + "eslint-plugin-import": "*", + "eslint-plugin-import-x": "*" + }, + "peerDependenciesMeta": { + "eslint-plugin-import": { + "optional": true + }, + "eslint-plugin-import-x": { + "optional": true + } } }, - "node_modules/import-local": { - "version": "3.2.0", + "node_modules/eslint-module-utils": { + "version": "2.12.1", + "resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.12.1.tgz", + "integrity": "sha512-L8jSWTze7K2mTg0vos/RuLRS5soomksDPoJLXIslC7c8Wmut3bx7CPpJijDcBZtxQ5lrbUdM+s0OlNbz0DCDNw==", "dev": true, "license": "MIT", "dependencies": { - "pkg-dir": "^4.2.0", - "resolve-cwd": "^3.0.0" - }, - "bin": { - "import-local-fixture": "fixtures/cli.js" + "debug": "^3.2.7" }, "engines": { - "node": ">=8" + "node": ">=4" }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "peerDependenciesMeta": { + "eslint": { + "optional": true + } } }, - "node_modules/imurmurhash": { - "version": "0.1.4", + "node_modules/eslint-module-utils/node_modules/debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", "dev": true, "license": "MIT", + "dependencies": { + "ms": "^2.1.1" + } + }, + "node_modules/eslint-plugin-html": { + "version": "8.1.3", + "resolved": "https://registry.npmjs.org/eslint-plugin-html/-/eslint-plugin-html-8.1.3.tgz", + "integrity": "sha512-cnCdO7yb/jrvgSJJAfRkGDOwLu1AOvNdw8WCD6nh/2C4RnxuI4tz6QjMEAmmSiHSeugq/fXcIO8yBpIBQrMZCg==", + "dev": true, + "license": "ISC", + "dependencies": { + "htmlparser2": "^10.0.0" + }, "engines": { - "node": ">=0.8.19" + "node": ">=16.0.0" } }, - "node_modules/indent-string": { - "version": "5.0.0", + "node_modules/eslint-plugin-import": { + "version": "2.32.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.32.0.tgz", + "integrity": "sha512-whOE1HFo/qJDyX4SnXzP4N6zOWn79WhnCUY/iDR0mPfQZO8wcYE4JClzI2oZrhBnnMUCBCHZhO6VQyoBU95mZA==", "dev": true, "license": "MIT", + "dependencies": { + "@rtsao/scc": "^1.1.0", + "array-includes": "^3.1.9", + "array.prototype.findlastindex": "^1.2.6", + "array.prototype.flat": "^1.3.3", + "array.prototype.flatmap": "^1.3.3", + "debug": "^3.2.7", + "doctrine": "^2.1.0", + "eslint-import-resolver-node": "^0.3.9", + "eslint-module-utils": "^2.12.1", + "hasown": "^2.0.2", + "is-core-module": "^2.16.1", + "is-glob": "^4.0.3", + "minimatch": "^3.1.2", + "object.fromentries": "^2.0.8", + "object.groupby": "^1.0.3", + "object.values": "^1.2.1", + "semver": "^6.3.1", + "string.prototype.trimend": "^1.0.9", + "tsconfig-paths": "^3.15.0" + }, "engines": { - "node": ">=12" + "node": ">=4" }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "peerDependencies": { + "eslint": "^2 || ^3 || ^4 || ^5 || ^6 || ^7.2.0 || ^8 || ^9" } }, - "node_modules/index-to-position": { - "version": "1.1.0", + "node_modules/eslint-plugin-import-x": { + "version": "4.16.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-import-x/-/eslint-plugin-import-x-4.16.1.tgz", + "integrity": "sha512-vPZZsiOKaBAIATpFE2uMI4w5IRwdv/FpQ+qZZMR4E+PeOcM4OeoEbqxRMnywdxP19TyB/3h6QBB0EWon7letSQ==", "dev": true, "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "^8.35.0", + "comment-parser": "^1.4.1", + "debug": "^4.4.1", + "eslint-import-context": "^0.1.9", + "is-glob": "^4.0.3", + "minimatch": "^9.0.3 || ^10.0.1", + "semver": "^7.7.2", + "stable-hash-x": "^0.2.0", + "unrs-resolver": "^1.9.2" + }, "engines": { - "node": ">=18" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "url": "https://opencollective.com/eslint-plugin-import-x" + }, + "peerDependencies": { + "@typescript-eslint/utils": "^8.0.0", + "eslint": "^8.57.0 || ^9.0.0", + "eslint-import-resolver-node": "*" + }, + "peerDependenciesMeta": { + "@typescript-eslint/utils": { + "optional": true + }, + "eslint-import-resolver-node": { + "optional": true + } } }, - "node_modules/inflight": { - "version": "1.0.6", + "node_modules/eslint-plugin-import/node_modules/brace-expansion": { + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", + "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", "dev": true, - "license": "ISC", + "license": "MIT", "dependencies": { - "once": "^1.3.0", - "wrappy": "1" + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" } }, - "node_modules/inherits": { - "version": "2.0.4", - "dev": true, - "license": "ISC" - }, - "node_modules/ini": { - "version": "1.3.8", - "license": "ISC" - }, - "node_modules/inquirer": { - "version": "8.2.7", + "node_modules/eslint-plugin-import/node_modules/debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", "dev": true, "license": "MIT", "dependencies": { - "@inquirer/external-editor": "^1.0.0", - "ansi-escapes": "^4.2.1", - "chalk": "^4.1.1", - "cli-cursor": "^3.1.0", - "cli-width": "^3.0.0", - "figures": "^3.0.0", - "lodash": "^4.17.21", - "mute-stream": "0.0.8", - "ora": "^5.4.1", - "run-async": "^2.4.0", - "rxjs": "^7.5.5", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0", - "through": "^2.3.6", - "wrap-ansi": "^6.0.1" - }, - "engines": { - "node": ">=12.0.0" + "ms": "^2.1.1" } }, - "node_modules/internal-slot": { - "version": "1.1.0", + "node_modules/eslint-plugin-import/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", "dev": true, - "license": "MIT", + "license": "ISC", "dependencies": { - "es-errors": "^1.3.0", - "hasown": "^2.0.2", - "side-channel": "^1.1.0" + "brace-expansion": "^1.1.7" }, "engines": { - "node": ">= 0.4" + "node": "*" } }, - "node_modules/ip-address": { - "version": "10.0.1", + "node_modules/eslint-plugin-import/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", "dev": true, - "license": "MIT", - "engines": { - "node": ">= 12" + "license": "ISC", + "bin": { + "semver": "bin/semver.js" } }, - "node_modules/is-array-buffer": { - "version": "3.0.5", + "node_modules/eslint-plugin-jest": { + "version": "29.5.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-jest/-/eslint-plugin-jest-29.5.0.tgz", + "integrity": "sha512-DAi9H8xN/TUuNOt+xDP1RqpCJLsSxBb5u1zXSpCyp0VAWGL8MBAg5t7/Dk+76iX7d1LhWu4DDH77IQNUolLDyg==", "dev": true, "license": "MIT", "dependencies": { - "call-bind": "^1.0.8", - "call-bound": "^1.0.3", - "get-intrinsic": "^1.2.6" + "@typescript-eslint/utils": "^8.0.0" }, "engines": { - "node": ">= 0.4" + "node": "^20.12.0 || ^22.0.0 || >=24.0.0" }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "peerDependencies": { + "@typescript-eslint/eslint-plugin": "^8.0.0", + "eslint": "^8.57.0 || ^9.0.0", + "jest": "*" + }, + "peerDependenciesMeta": { + "@typescript-eslint/eslint-plugin": { + "optional": true + }, + "jest": { + "optional": true + } } }, - "node_modules/is-arrayish": { - "version": "0.2.1", - "dev": true, - "license": "MIT" - }, - "node_modules/is-async-function": { - "version": "2.1.1", + "node_modules/eslint-plugin-json": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-json/-/eslint-plugin-json-4.0.1.tgz", + "integrity": "sha512-3An5ISV5dq/kHfXdNyY5TUe2ONC3yXFSkLX2gu+W8xAhKhfvrRvkSAeKXCxZqZ0KJLX15ojBuLPyj+UikQMkOA==", "dev": true, "license": "MIT", "dependencies": { - "async-function": "^1.0.0", - "call-bound": "^1.0.3", - "get-proto": "^1.0.1", - "has-tostringtag": "^1.0.2", - "safe-regex-test": "^1.1.0" + "lodash": "^4.17.21", + "vscode-json-languageservice": "^4.1.6" }, "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": ">=18.0" } }, - "node_modules/is-bigint": { - "version": "1.1.0", + "node_modules/eslint-plugin-jsx-a11y": { + "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==", "dev": true, "license": "MIT", "dependencies": { - "has-bigints": "^1.0.2" + "aria-query": "^5.3.2", + "array-includes": "^3.1.8", + "array.prototype.flatmap": "^1.3.2", + "ast-types-flow": "^0.0.8", + "axe-core": "^4.10.0", + "axobject-query": "^4.1.0", + "damerau-levenshtein": "^1.0.8", + "emoji-regex": "^9.2.2", + "hasown": "^2.0.2", + "jsx-ast-utils": "^3.3.5", + "language-tags": "^1.0.9", + "minimatch": "^3.1.2", + "object.fromentries": "^2.0.8", + "safe-regex-test": "^1.0.3", + "string.prototype.includes": "^2.0.1" }, "engines": { - "node": ">= 0.4" + "node": ">=4.0" }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "peerDependencies": { + "eslint": "^3 || ^4 || ^5 || ^6 || ^7 || ^8 || ^9" } }, - "node_modules/is-binary-path": { - "version": "2.1.0", + "node_modules/eslint-plugin-jsx-a11y/node_modules/brace-expansion": { + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", + "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", "dev": true, "license": "MIT", "dependencies": { - "binary-extensions": "^2.0.0" - }, - "engines": { - "node": ">=8" + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" } }, - "node_modules/is-boolean-object": { - "version": "1.2.2", + "node_modules/eslint-plugin-jsx-a11y/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", "dev": true, - "license": "MIT", + "license": "ISC", "dependencies": { - "call-bound": "^1.0.3", - "has-tostringtag": "^1.0.2" + "brace-expansion": "^1.1.7" }, "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": "*" } }, - "node_modules/is-builtin-module": { - "version": "5.0.0", + "node_modules/eslint-plugin-no-relative-import-paths": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-no-relative-import-paths/-/eslint-plugin-no-relative-import-paths-1.6.1.tgz", + "integrity": "sha512-YZNeOnsOrJcwhFw0X29MXjIzu2P/f5X2BZDPWw1R3VUYBRFxNIh77lyoL/XrMU9ewZNQPcEvAgL/cBOT1P330A==", + "dev": true, + "license": "ISC" + }, + "node_modules/eslint-plugin-prettier": { + "version": "5.5.4", + "resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-5.5.4.tgz", + "integrity": "sha512-swNtI95SToIz05YINMA6Ox5R057IMAmWZ26GqPxusAp1TZzj+IdY9tXNWWD3vkF/wEqydCONcwjTFpxybBqZsg==", "dev": true, "license": "MIT", "dependencies": { - "builtin-modules": "^5.0.0" + "prettier-linter-helpers": "^1.0.0", + "synckit": "^0.11.7" }, "engines": { - "node": ">=18.20" + "node": "^14.18.0 || >=16.0.0" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "url": "https://opencollective.com/eslint-plugin-prettier" + }, + "peerDependencies": { + "@types/eslint": ">=8.0.0", + "eslint": ">=8.0.0", + "eslint-config-prettier": ">= 7.0.0 <10.0.0 || >=10.1.0", + "prettier": ">=3.0.0" + }, + "peerDependenciesMeta": { + "@types/eslint": { + "optional": true + }, + "eslint-config-prettier": { + "optional": true + } } }, - "node_modules/is-builtin-module/node_modules/builtin-modules": { - "version": "5.0.0", + "node_modules/eslint-plugin-react": { + "version": "7.37.5", + "resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.37.5.tgz", + "integrity": "sha512-Qteup0SqU15kdocexFNAJMvCJEfa2xUKNV4CC1xsVMrIIqEy3SQ/rqyxCWNzfrd3/ldy6HMlD2e0JDVpDg2qIA==", "dev": true, "license": "MIT", + "dependencies": { + "array-includes": "^3.1.8", + "array.prototype.findlast": "^1.2.5", + "array.prototype.flatmap": "^1.3.3", + "array.prototype.tosorted": "^1.1.4", + "doctrine": "^2.1.0", + "es-iterator-helpers": "^1.2.1", + "estraverse": "^5.3.0", + "hasown": "^2.0.2", + "jsx-ast-utils": "^2.4.1 || ^3.0.0", + "minimatch": "^3.1.2", + "object.entries": "^1.1.9", + "object.fromentries": "^2.0.8", + "object.values": "^1.2.1", + "prop-types": "^15.8.1", + "resolve": "^2.0.0-next.5", + "semver": "^6.3.1", + "string.prototype.matchall": "^4.0.12", + "string.prototype.repeat": "^1.0.0" + }, "engines": { - "node": ">=18.20" + "node": ">=4" }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/is-bun-module": { - "version": "2.0.0", - "dev": true, - "license": "MIT", - "dependencies": { - "semver": "^7.7.1" + "peerDependencies": { + "eslint": "^3 || ^4 || ^5 || ^6 || ^7 || ^8 || ^9.7" } }, - "node_modules/is-callable": { - "version": "1.2.7", + "node_modules/eslint-plugin-react-hooks": { + "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==", "dev": true, "license": "MIT", "engines": { - "node": ">= 0.4" + "node": ">=10" }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "peerDependencies": { + "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/is-core-module": { - "version": "2.16.1", + "node_modules/eslint-plugin-react/node_modules/brace-expansion": { + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", + "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", "dev": true, "license": "MIT", "dependencies": { - "hasown": "^2.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" } }, - "node_modules/is-data-view": { - "version": "1.0.2", + "node_modules/eslint-plugin-react/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", "dev": true, - "license": "MIT", + "license": "ISC", "dependencies": { - "call-bound": "^1.0.2", - "get-intrinsic": "^1.2.6", - "is-typed-array": "^1.1.13" + "brace-expansion": "^1.1.7" }, "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": "*" } }, - "node_modules/is-date-object": { - "version": "1.1.0", + "node_modules/eslint-plugin-react/node_modules/resolve": { + "version": "2.0.0-next.5", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-2.0.0-next.5.tgz", + "integrity": "sha512-U7WjGVG9sH8tvjW5SmGbQuui75FiyjAX72HX15DwBBwF9dNiQZRQAg9nnPhYy+TUnE0+VcrttuvNI8oSxZcocA==", "dev": true, "license": "MIT", "dependencies": { - "call-bound": "^1.0.2", - "has-tostringtag": "^1.0.2" + "is-core-module": "^2.13.0", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" }, - "engines": { - "node": ">= 0.4" + "bin": { + "resolve": "bin/resolve" }, "funding": { "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/is-docker": { - "version": "3.0.0", + "node_modules/eslint-plugin-react/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", "dev": true, - "license": "MIT", + "license": "ISC", "bin": { - "is-docker": "cli.js" - }, - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "semver": "bin/semver.js" } }, - "node_modules/is-extglob": { - "version": "2.1.1", + "node_modules/eslint-plugin-security": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-security/-/eslint-plugin-security-3.0.1.tgz", + "integrity": "sha512-XjVGBhtDZJfyuhIxnQ/WMm385RbX3DBu7H1J7HNNhmB2tnGxMeqVSnYv79oAj992ayvIBZghsymwkYFS6cGH4Q==", "dev": true, - "license": "MIT", + "license": "Apache-2.0", + "dependencies": { + "safe-regex": "^2.1.1" + }, "engines": { - "node": ">=0.10.0" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" } }, - "node_modules/is-finalizationregistry": { - "version": "1.1.1", + "node_modules/eslint-plugin-sonarjs": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/eslint-plugin-sonarjs/-/eslint-plugin-sonarjs-3.0.5.tgz", + "integrity": "sha512-dI62Ff3zMezUToi161hs2i1HX1ie8Ia2hO0jtNBfdgRBicAG4ydy2WPt0rMTrAe3ZrlqhpAO3w1jcQEdneYoFA==", "dev": true, - "license": "MIT", + "license": "LGPL-3.0-only", "dependencies": { - "call-bound": "^1.0.3" - }, - "engines": { - "node": ">= 0.4" + "@eslint-community/regexpp": "4.12.1", + "builtin-modules": "3.3.0", + "bytes": "3.1.2", + "functional-red-black-tree": "1.0.1", + "jsx-ast-utils-x": "0.1.0", + "lodash.merge": "4.6.2", + "minimatch": "9.0.5", + "scslre": "0.3.0", + "semver": "7.7.2", + "typescript": ">=5" }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "peerDependencies": { + "eslint": "^8.0.0 || ^9.0.0" } }, - "node_modules/is-fullwidth-code-point": { - "version": "3.0.0", + "node_modules/eslint-plugin-sonarjs/node_modules/@eslint-community/regexpp": { + "version": "4.12.1", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.1.tgz", + "integrity": "sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ==", + "dev": true, "license": "MIT", "engines": { - "node": ">=8" + "node": "^12.0.0 || ^14.0.0 || >=16.0.0" } }, - "node_modules/is-generator-fn": { - "version": "2.1.0", + "node_modules/eslint-plugin-sonarjs/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==", "dev": true, - "license": "MIT", + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, "engines": { - "node": ">=6" + "node": ">=10" } }, - "node_modules/is-generator-function": { - "version": "1.1.0", + "node_modules/eslint-plugin-sort-destructure-keys": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-sort-destructure-keys/-/eslint-plugin-sort-destructure-keys-2.0.0.tgz", + "integrity": "sha512-4w1UQCa3o/YdfWaLr9jY8LfGowwjwjmwClyFLxIsToiyIdZMq3x9Ti44nDn34DtTPP7PWg96tUONKVmATKhYGQ==", "dev": true, - "license": "MIT", + "license": "ISC", "dependencies": { - "call-bound": "^1.0.3", - "get-proto": "^1.0.0", - "has-tostringtag": "^1.0.2", - "safe-regex-test": "^1.1.0" + "natural-compare-lite": "^1.4.0" }, "engines": { - "node": ">= 0.4" + "node": ">=12" }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "peerDependencies": { + "eslint": "5 - 9" } }, - "node_modules/is-glob": { - "version": "4.0.3", + "node_modules/eslint-plugin-unicorn": { + "version": "62.0.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-unicorn/-/eslint-plugin-unicorn-62.0.0.tgz", + "integrity": "sha512-HIlIkGLkvf29YEiS/ImuDZQbP12gWyx5i3C6XrRxMvVdqMroCI9qoVYCoIl17ChN+U89pn9sVwLxhIWj5nEc7g==", "dev": true, "license": "MIT", "dependencies": { - "is-extglob": "^2.1.1" + "@babel/helper-validator-identifier": "^7.28.5", + "@eslint-community/eslint-utils": "^4.9.0", + "@eslint/plugin-kit": "^0.4.0", + "change-case": "^5.4.4", + "ci-info": "^4.3.1", + "clean-regexp": "^1.0.0", + "core-js-compat": "^3.46.0", + "esquery": "^1.6.0", + "find-up-simple": "^1.0.1", + "globals": "^16.4.0", + "indent-string": "^5.0.0", + "is-builtin-module": "^5.0.0", + "jsesc": "^3.1.0", + "pluralize": "^8.0.0", + "regexp-tree": "^0.1.27", + "regjsparser": "^0.13.0", + "semver": "^7.7.3", + "strip-indent": "^4.1.1" }, "engines": { - "node": ">=0.10.0" + "node": "^20.10.0 || >=21.0.0" + }, + "funding": { + "url": "https://github.com/sindresorhus/eslint-plugin-unicorn?sponsor=1" + }, + "peerDependencies": { + "eslint": ">=9.38.0" } }, - "node_modules/is-inside-container": { - "version": "1.0.0", + "node_modules/eslint-plugin-unicorn/node_modules/globals": { + "version": "16.5.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-16.5.0.tgz", + "integrity": "sha512-c/c15i26VrJ4IRt5Z89DnIzCGDn9EcebibhAOjw5ibqEHsE1wLUgkPn9RDmNcUKyU87GeaL633nyJ+pplFR2ZQ==", "dev": true, "license": "MIT", - "dependencies": { - "is-docker": "^3.0.0" - }, - "bin": { - "is-inside-container": "cli.js" - }, "engines": { - "node": ">=14.16" + "node": ">=18" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/is-interactive": { - "version": "1.0.0", + "node_modules/eslint-scope": { + "version": "8.4.0", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.4.0.tgz", + "integrity": "sha512-sNXOfKCn74rt8RICKMvJS7XKV/Xk9kA7DyJr8mJik3S7Cwgy3qlkkmyS2uQB3jiJg6VNdZd/pDBJu0nvG2NlTg==", "dev": true, - "license": "MIT", + "license": "BSD-2-Clause", + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^5.2.0" + }, "engines": { - "node": ">=8" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" } }, - "node_modules/is-map": { - "version": "2.0.3", + "node_modules/eslint-visitor-keys": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz", + "integrity": "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==", "dev": true, - "license": "MIT", + "license": "Apache-2.0", "engines": { - "node": ">= 0.4" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { - "url": "https://github.com/sponsors/ljharb" + "url": "https://opencollective.com/eslint" } }, - "node_modules/is-negative-zero": { - "version": "2.0.3", + "node_modules/eslint/node_modules/ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", "dev": true, "license": "MIT", - "engines": { - "node": ">= 0.4" + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" }, "funding": { - "url": "https://github.com/sponsors/ljharb" + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" } }, - "node_modules/is-number": { - "version": "7.0.0", + "node_modules/eslint/node_modules/brace-expansion": { + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", + "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", "dev": true, "license": "MIT", - "engines": { - "node": ">=0.12.0" + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" } }, - "node_modules/is-number-object": { - "version": "1.1.1", + "node_modules/eslint/node_modules/glob-parent": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", "dev": true, - "license": "MIT", + "license": "ISC", "dependencies": { - "call-bound": "^1.0.3", - "has-tostringtag": "^1.0.2" + "is-glob": "^4.0.3" }, "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": ">=10.13.0" } }, - "node_modules/is-port-reachable": { - "version": "4.0.0", + "node_modules/eslint/node_modules/ignore": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", + "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", + "dev": true, "license": "MIT", "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">= 4" } }, - "node_modules/is-potential-custom-element-name": { - "version": "1.0.1", + "node_modules/eslint/node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", "dev": true, "license": "MIT" }, - "node_modules/is-regex": { - "version": "1.2.1", + "node_modules/eslint/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", "dev": true, - "license": "MIT", + "license": "ISC", "dependencies": { - "call-bound": "^1.0.2", - "gopd": "^1.2.0", - "has-tostringtag": "^1.0.2", - "hasown": "^2.0.2" - }, - "engines": { - "node": ">= 0.4" + "brace-expansion": "^1.1.7" }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-set": { - "version": "2.0.3", - "dev": true, - "license": "MIT", "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": "*" } }, - "node_modules/is-shared-array-buffer": { - "version": "1.0.4", + "node_modules/espree": { + "version": "10.4.0", + "resolved": "https://registry.npmjs.org/espree/-/espree-10.4.0.tgz", + "integrity": "sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ==", "dev": true, - "license": "MIT", + "license": "BSD-2-Clause", "dependencies": { - "call-bound": "^1.0.3" + "acorn": "^8.15.0", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^4.2.1" }, "engines": { - "node": ">= 0.4" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { - "url": "https://github.com/sponsors/ljharb" + "url": "https://opencollective.com/eslint" } }, - "node_modules/is-stream": { - "version": "2.0.1", - "license": "MIT", - "engines": { - "node": ">=8" + "node_modules/esprima": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", + "license": "BSD-2-Clause", + "bin": { + "esparse": "bin/esparse.js", + "esvalidate": "bin/esvalidate.js" }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "engines": { + "node": ">=4" } }, - "node_modules/is-string": { - "version": "1.1.1", + "node_modules/esquery": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.6.0.tgz", + "integrity": "sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==", "dev": true, - "license": "MIT", + "license": "BSD-3-Clause", "dependencies": { - "call-bound": "^1.0.3", - "has-tostringtag": "^1.0.2" + "estraverse": "^5.1.0" }, "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": ">=0.10" } }, - "node_modules/is-symbol": { - "version": "1.1.1", + "node_modules/esrecurse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", + "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", "dev": true, - "license": "MIT", + "license": "BSD-2-Clause", "dependencies": { - "call-bound": "^1.0.2", - "has-symbols": "^1.1.0", - "safe-regex-test": "^1.1.0" + "estraverse": "^5.2.0" }, "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": ">=4.0" } }, - "node_modules/is-typed-array": { - "version": "1.1.15", + "node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", "dev": true, - "license": "MIT", - "dependencies": { - "which-typed-array": "^1.1.16" - }, + "license": "BSD-2-Clause", "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": ">=4.0" } }, - "node_modules/is-unicode-supported": { - "version": "0.1.0", + "node_modules/estree-walker": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz", + "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==", "dev": true, + "license": "MIT" + }, + "node_modules/esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/etag": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", + "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", "license": "MIT", "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">= 0.6" } }, - "node_modules/is-weakmap": { - "version": "2.0.2", + "node_modules/event-target-shim": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/event-target-shim/-/event-target-shim-5.0.1.tgz", + "integrity": "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==", "dev": true, "license": "MIT", "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": ">=6" } }, - "node_modules/is-weakref": { + "node_modules/eventemitter3": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz", + "integrity": "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==", + "license": "MIT" + }, + "node_modules/events": { "version": "1.1.1", + "resolved": "https://registry.npmjs.org/events/-/events-1.1.1.tgz", + "integrity": "sha512-kEcvvCBByWXGnZy6JUlgAp2gBIUjfCAV6P6TgT1/aaQKcmuAEC4OZTV1I4EWQLz2gxZw76atuVyvHhTxvi0Flw==", + "license": "MIT", + "engines": { + "node": ">=0.4.x" + } + }, + "node_modules/events-universal": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/events-universal/-/events-universal-1.0.1.tgz", + "integrity": "sha512-LUd5euvbMLpwOF8m6ivPCbhQeSiYVNb8Vs0fQ8QjXo0JTkEHpz8pxdQf0gStltaPpw0Cca8b39KxvK9cfKRiAw==", "dev": true, + "license": "Apache-2.0", + "dependencies": { + "bare-events": "^2.7.0" + } + }, + "node_modules/execa": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", + "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==", "license": "MIT", "dependencies": { - "call-bound": "^1.0.3" + "cross-spawn": "^7.0.3", + "get-stream": "^6.0.0", + "human-signals": "^2.1.0", + "is-stream": "^2.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^4.0.1", + "onetime": "^5.1.2", + "signal-exit": "^3.0.3", + "strip-final-newline": "^2.0.0" }, "engines": { - "node": ">= 0.4" + "node": ">=10" }, "funding": { - "url": "https://github.com/sponsors/ljharb" + "url": "https://github.com/sindresorhus/execa?sponsor=1" } }, - "node_modules/is-weakset": { - "version": "2.0.4", + "node_modules/exit-x": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/exit-x/-/exit-x-0.2.2.tgz", + "integrity": "sha512-+I6B/IkJc1o/2tiURyz/ivu/O0nKNEArIUB5O7zBrlDVJr22SCLH3xTeEry428LvFhRzIA1g8izguxJ/gbNcVQ==", "dev": true, "license": "MIT", - "dependencies": { - "call-bound": "^1.0.3", - "get-intrinsic": "^1.2.6" - }, "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": ">= 0.8.0" } }, - "node_modules/is-wsl": { - "version": "2.2.0", + "node_modules/expect": { + "version": "30.2.0", + "resolved": "https://registry.npmjs.org/expect/-/expect-30.2.0.tgz", + "integrity": "sha512-u/feCi0GPsI+988gU2FLcsHyAHTU0MX1Wg68NhAnN7z/+C5wqG+CY8J53N9ioe8RXgaoz0nBR/TYMf3AycUuPw==", + "dev": true, "license": "MIT", "dependencies": { - "is-docker": "^2.0.0" + "@jest/expect-utils": "30.2.0", + "@jest/get-type": "30.1.0", + "jest-matcher-utils": "30.2.0", + "jest-message-util": "30.2.0", + "jest-mock": "30.2.0", + "jest-util": "30.2.0" }, "engines": { - "node": ">=8" + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" } }, - "node_modules/is-wsl/node_modules/is-docker": { - "version": "2.2.1", + "node_modules/express": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/express/-/express-5.2.1.tgz", + "integrity": "sha512-hIS4idWWai69NezIdRt2xFVofaF4j+6INOpJlVOLDO8zXGpUVEVzIYk12UUi2JzjEzWL3IOAxcTubgz9Po0yXw==", "license": "MIT", - "bin": { - "is-docker": "cli.js" - }, - "engines": { - "node": ">=8" + "dependencies": { + "accepts": "^2.0.0", + "body-parser": "^2.2.1", + "content-disposition": "^1.0.0", + "content-type": "^1.0.5", + "cookie": "^0.7.1", + "cookie-signature": "^1.2.1", + "debug": "^4.4.0", + "depd": "^2.0.0", + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "etag": "^1.8.1", + "finalhandler": "^2.1.0", + "fresh": "^2.0.0", + "http-errors": "^2.0.0", + "merge-descriptors": "^2.0.0", + "mime-types": "^3.0.0", + "on-finished": "^2.4.1", + "once": "^1.4.0", + "parseurl": "^1.3.3", + "proxy-addr": "^2.0.7", + "qs": "^6.14.0", + "range-parser": "^1.2.1", + "router": "^2.2.0", + "send": "^1.1.0", + "serve-static": "^2.2.0", + "statuses": "^2.0.1", + "type-is": "^2.0.1", + "vary": "^1.1.2" + }, + "engines": { + "node": ">= 18" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "type": "opencollective", + "url": "https://opencollective.com/express" } }, - "node_modules/isarray": { - "version": "2.0.5", - "dev": true, + "node_modules/fast-copy": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/fast-copy/-/fast-copy-4.0.2.tgz", + "integrity": "sha512-ybA6PDXIXOXivLJK/z9e+Otk7ve13I4ckBvGO5I2RRmBU1gMHLVDJYEuJYhGwez7YNlYji2M2DvVU+a9mSFDlw==", "license": "MIT" }, - "node_modules/isexe": { - "version": "2.0.0", - "license": "ISC" - }, - "node_modules/istanbul-lib-coverage": { - "version": "3.2.2", - "dev": true, - "license": "BSD-3-Clause", - "engines": { - "node": ">=8" - } + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "license": "MIT" }, - "node_modules/istanbul-lib-instrument": { - "version": "6.0.3", + "node_modules/fast-diff": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/fast-diff/-/fast-diff-1.3.0.tgz", + "integrity": "sha512-VxPP4NqbUjj6MaAOafWeUn2cXWLcCtljklUtZf0Ind4XQ+QPtmA0b18zZy0jIQx+ExRVCR/ZQpBmik5lXshNsw==", "dev": true, - "license": "BSD-3-Clause", - "dependencies": { - "@babel/core": "^7.23.9", - "@babel/parser": "^7.23.9", - "@istanbuljs/schema": "^0.1.3", - "istanbul-lib-coverage": "^3.2.0", - "semver": "^7.5.4" - }, - "engines": { - "node": ">=10" - } + "license": "Apache-2.0" }, - "node_modules/istanbul-lib-report": { - "version": "3.0.1", + "node_modules/fast-fifo": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/fast-fifo/-/fast-fifo-1.3.2.tgz", + "integrity": "sha512-/d9sfos4yxzpwkDkuN7k2SqFKtYNmCTzgfEpz82x34IM9/zc8KGxQoXg1liNC/izpRM/MBdt44Nmx41ZWqk+FQ==", "dev": true, - "license": "BSD-3-Clause", - "dependencies": { - "istanbul-lib-coverage": "^3.0.0", - "make-dir": "^4.0.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - } + "license": "MIT" }, - "node_modules/istanbul-lib-source-maps": { - "version": "5.0.6", + "node_modules/fast-glob": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.1.tgz", + "integrity": "sha512-kNFPyjhh5cKjrUltxs+wFx+ZkbRaxxmZ+X0ZU31SOsxCEtP9VPgtq2teZw1DebupL5GmDaNQ6yKMMVcM41iqDg==", "dev": true, - "license": "BSD-3-Clause", + "license": "MIT", "dependencies": { - "@jridgewell/trace-mapping": "^0.3.23", - "debug": "^4.1.1", - "istanbul-lib-coverage": "^3.0.0" + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.4" }, "engines": { - "node": ">=10" + "node": ">=8.6.0" } }, - "node_modules/istanbul-reports": { - "version": "3.2.0", + "node_modules/fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", "dev": true, - "license": "BSD-3-Clause", - "dependencies": { - "html-escaper": "^2.0.0", - "istanbul-lib-report": "^3.0.0" - }, - "engines": { - "node": ">=8" - } + "license": "MIT" }, - "node_modules/iterare": { - "version": "1.2.1", + "node_modules/fast-levenshtein": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", "dev": true, - "license": "ISC", - "engines": { - "node": ">=6" - } + "license": "MIT" }, - "node_modules/iterator.prototype": { - "version": "1.1.5", + "node_modules/fast-memoize": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/fast-memoize/-/fast-memoize-2.5.2.tgz", + "integrity": "sha512-Ue0LwpDYErFbmNnZSF0UH6eImUwDmogUO1jyE+JbN2gsQz/jICm1Ve7t9QT0rNSsfJt+Hs4/S3GnsDVjL4HVrw==", "dev": true, + "license": "MIT" + }, + "node_modules/fast-safe-stringify": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/fast-safe-stringify/-/fast-safe-stringify-2.1.1.tgz", + "integrity": "sha512-W+KJc2dmILlPplD/H4K9l9LcAHAfPtP6BY84uVLXQ6Evcz9Lcg33Y2z1IVblT6xdY54PXYVHEv+0Wpq8Io6zkA==", + "license": "MIT" + }, + "node_modules/fast-uri": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.0.tgz", + "integrity": "sha512-iPeeDKJSWf4IEOasVVrknXpaBV0IApz/gp7S2bb7Z4Lljbl2MGJRqInZiUrQwV16cpzw/D3S5j5Julj/gT52AA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fastify" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fastify" + } + ], + "license": "BSD-3-Clause" + }, + "node_modules/fast-xml-parser": { + "version": "5.2.5", + "resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-5.2.5.tgz", + "integrity": "sha512-pfX9uG9Ki0yekDHx2SiuRIyFdyAr1kMIMitPvb0YBo8SUfKvia7w7FIyd/l6av85pFYRhZscS75MwMnbvY+hcQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/NaturalIntelligence" + } + ], "license": "MIT", "dependencies": { - "define-data-property": "^1.1.4", - "es-object-atoms": "^1.0.0", - "get-intrinsic": "^1.2.6", - "get-proto": "^1.0.0", - "has-symbols": "^1.1.0", - "set-function-name": "^2.0.2" + "strnum": "^2.1.0" }, - "engines": { - "node": ">= 0.4" + "bin": { + "fxparser": "src/cli/cli.js" } }, - "node_modules/jackspeak": { - "version": "4.1.1", + "node_modules/fastq": { + "version": "1.19.1", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.19.1.tgz", + "integrity": "sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ==", "dev": true, - "license": "BlueOak-1.0.0", + "license": "ISC", "dependencies": { - "@isaacs/cliui": "^8.0.2" - }, - "engines": { - "node": "20 || >=22" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" + "reusify": "^1.0.4" } }, - "node_modules/jest": { - "version": "30.2.0", - "resolved": "https://registry.npmjs.org/jest/-/jest-30.2.0.tgz", - "integrity": "sha512-F26gjC0yWN8uAA5m5Ss8ZQf5nDHWGlN/xWZIh8S5SRbsEKBovwZhxGd6LJlbZYxBgCYOtreSUyb8hpXyGC5O4A==", + "node_modules/fb-watchman": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/fb-watchman/-/fb-watchman-2.0.2.tgz", + "integrity": "sha512-p5161BqbuCaSnB8jIbzQHOlpgsPmK5rJVDfDKO91Axs5NC1uu3HRQm6wt9cd9/+GtQQIO53JdGXXoyDpTAsgYA==", "dev": true, - "license": "MIT", + "license": "Apache-2.0", "dependencies": { - "@jest/core": "30.2.0", - "@jest/types": "30.2.0", - "import-local": "^3.2.0", - "jest-cli": "30.2.0" - }, - "bin": { - "jest": "bin/jest.js" - }, + "bser": "2.1.1" + } + }, + "node_modules/fdir": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", + "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", + "dev": true, + "license": "MIT", "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + "node": ">=12.0.0" }, "peerDependencies": { - "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" + "picomatch": "^3 || ^4" }, "peerDependenciesMeta": { - "node-notifier": { + "picomatch": { "optional": true } } }, - "node_modules/jest-changed-files": { - "version": "30.2.0", + "node_modules/fflate": { + "version": "0.8.2", + "resolved": "https://registry.npmjs.org/fflate/-/fflate-0.8.2.tgz", + "integrity": "sha512-cPJU47OaAoCbg0pBvzsgpTPhmhqI5eJjh/JIu8tPj5q+T7iLvW/JAYUqmE7KOB4R1ZyEhzBaIQpQpardBF5z8A==", "dev": true, - "license": "MIT", - "dependencies": { - "execa": "^5.1.1", - "jest-util": "30.2.0", - "p-limit": "^3.1.0" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } + "license": "MIT" }, - "node_modules/jest-changed-files/node_modules/@jest/schemas": { - "version": "30.0.5", + "node_modules/figures": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/figures/-/figures-3.2.0.tgz", + "integrity": "sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg==", "dev": true, "license": "MIT", "dependencies": { - "@sinclair/typebox": "^0.34.0" + "escape-string-regexp": "^1.0.5" }, "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/jest-changed-files/node_modules/@jest/types": { - "version": "30.2.0", + "node_modules/figures/node_modules/escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", "dev": true, "license": "MIT", - "dependencies": { - "@jest/pattern": "30.0.1", - "@jest/schemas": "30.0.5", - "@types/istanbul-lib-coverage": "^2.0.6", - "@types/istanbul-reports": "^3.0.4", - "@types/node": "*", - "@types/yargs": "^17.0.33", - "chalk": "^4.1.2" - }, "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + "node": ">=0.8.0" } }, - "node_modules/jest-changed-files/node_modules/@sinclair/typebox": { - "version": "0.34.41", - "dev": true, - "license": "MIT" - }, - "node_modules/jest-changed-files/node_modules/jest-util": { - "version": "30.2.0", + "node_modules/file-entry-cache": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-8.0.0.tgz", + "integrity": "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==", "dev": true, "license": "MIT", "dependencies": { - "@jest/types": "30.2.0", - "@types/node": "*", - "chalk": "^4.1.2", - "ci-info": "^4.2.0", - "graceful-fs": "^4.2.11", - "picomatch": "^4.0.2" + "flat-cache": "^4.0.0" }, "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + "node": ">=16.0.0" } }, - "node_modules/jest-changed-files/node_modules/picomatch": { - "version": "4.0.3", + "node_modules/file-type": { + "version": "21.1.0", + "resolved": "https://registry.npmjs.org/file-type/-/file-type-21.1.0.tgz", + "integrity": "sha512-boU4EHmP3JXkwDo4uhyBhTt5pPstxB6eEXKJBu2yu2l7aAMMm7QQYQEzssJmKReZYrFdFOJS8koVo6bXIBGDqA==", "dev": true, "license": "MIT", + "dependencies": { + "@tokenizer/inflate": "^0.3.1", + "strtok3": "^10.3.1", + "token-types": "^6.0.0", + "uint8array-extras": "^1.4.0" + }, "engines": { - "node": ">=12" + "node": ">=20" }, "funding": { - "url": "https://github.com/sponsors/jonschlinkert" + "url": "https://github.com/sindresorhus/file-type?sponsor=1" } }, - "node_modules/jest-circus": { - "version": "30.2.0", + "node_modules/fill-range": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", "dev": true, "license": "MIT", "dependencies": { - "@jest/environment": "30.2.0", - "@jest/expect": "30.2.0", - "@jest/test-result": "30.2.0", - "@jest/types": "30.2.0", - "@types/node": "*", - "chalk": "^4.1.2", - "co": "^4.6.0", - "dedent": "^1.6.0", - "is-generator-fn": "^2.1.0", - "jest-each": "30.2.0", - "jest-matcher-utils": "30.2.0", - "jest-message-util": "30.2.0", - "jest-runtime": "30.2.0", - "jest-snapshot": "30.2.0", - "jest-util": "30.2.0", - "p-limit": "^3.1.0", - "pretty-format": "30.2.0", - "pure-rand": "^7.0.0", - "slash": "^3.0.0", - "stack-utils": "^2.0.6" + "to-regex-range": "^5.0.1" }, "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + "node": ">=8" } }, - "node_modules/jest-circus/node_modules/@jest/console": { - "version": "30.2.0", - "dev": true, + "node_modules/finalhandler": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-2.1.1.tgz", + "integrity": "sha512-S8KoZgRZN+a5rNwqTxlZZePjT/4cnm0ROV70LedRHZ0p8u9fRID0hJUZQpkKLzro8LfmC8sx23bY6tVNxv8pQA==", "license": "MIT", "dependencies": { - "@jest/types": "30.2.0", - "@types/node": "*", - "chalk": "^4.1.2", - "jest-message-util": "30.2.0", - "jest-util": "30.2.0", - "slash": "^3.0.0" + "debug": "^4.4.0", + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "on-finished": "^2.4.1", + "parseurl": "^1.3.3", + "statuses": "^2.0.1" }, "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + "node": ">= 18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" } }, - "node_modules/jest-circus/node_modules/@jest/schemas": { - "version": "30.0.5", + "node_modules/find-up": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", "dev": true, "license": "MIT", "dependencies": { - "@sinclair/typebox": "^0.34.0" + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" }, "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/jest-circus/node_modules/@jest/test-result": { - "version": "30.2.0", + "node_modules/find-up-simple": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/find-up-simple/-/find-up-simple-1.0.1.tgz", + "integrity": "sha512-afd4O7zpqHeRyg4PfDQsXmlDe2PfdHtJt6Akt8jOWaApLOZk5JXs6VMR29lz03pRe9mpykrRCYIYxaJYcfpncQ==", "dev": true, "license": "MIT", - "dependencies": { - "@jest/console": "30.2.0", - "@jest/types": "30.2.0", - "@types/istanbul-lib-coverage": "^2.0.6", - "collect-v8-coverage": "^1.0.2" - }, "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/jest-circus/node_modules/@jest/types": { - "version": "30.2.0", + "node_modules/flat-cache": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-4.0.1.tgz", + "integrity": "sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==", "dev": true, "license": "MIT", "dependencies": { - "@jest/pattern": "30.0.1", - "@jest/schemas": "30.0.5", - "@types/istanbul-lib-coverage": "^2.0.6", - "@types/istanbul-reports": "^3.0.4", - "@types/node": "*", - "@types/yargs": "^17.0.33", - "chalk": "^4.1.2" + "flatted": "^3.2.9", + "keyv": "^4.5.4" }, "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + "node": ">=16" } }, - "node_modules/jest-circus/node_modules/@sinclair/typebox": { - "version": "0.34.41", + "node_modules/flatted": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.3.tgz", + "integrity": "sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg==", "dev": true, - "license": "MIT" + "license": "ISC" }, - "node_modules/jest-circus/node_modules/ansi-styles": { - "version": "5.2.0", - "dev": true, + "node_modules/follow-redirects": { + "version": "1.15.11", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.11.tgz", + "integrity": "sha512-deG2P0JfjrTxl50XGCDyfI97ZGVCxIpfKYmfyrQ54n5FO/0gfIES8C/Psl6kWVDolizcaaxZJnTS0QSMxvnsBQ==", + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/RubenVerborgh" + } + ], "license": "MIT", "engines": { - "node": ">=10" + "node": ">=4.0" }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" + "peerDependenciesMeta": { + "debug": { + "optional": true + } } }, - "node_modules/jest-circus/node_modules/jest-diff": { - "version": "30.2.0", - "dev": true, + "node_modules/for-each": { + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.5.tgz", + "integrity": "sha512-dKx12eRCVIzqCxFGplyFKJMPvLEWgmNtUrpTiJIR5u97zEhRG8ySrtboPHZXx7daLxQVrl643cTzbab2tkQjxg==", "license": "MIT", "dependencies": { - "@jest/diff-sequences": "30.0.1", - "@jest/get-type": "30.1.0", - "chalk": "^4.1.2", - "pretty-format": "30.2.0" + "is-callable": "^1.2.7" }, "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/jest-circus/node_modules/jest-matcher-utils": { - "version": "30.2.0", + "node_modules/foreach": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/foreach/-/foreach-2.0.6.tgz", + "integrity": "sha512-k6GAGDyqLe9JaebCsFCoudPPWfihKu8pylYXRlqP1J7ms39iPoTtk2fviNglIeQEwdh0bQeKJ01ZPyuyQvKzwg==", "dev": true, - "license": "MIT", + "license": "MIT" + }, + "node_modules/foreground-child": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.1.tgz", + "integrity": "sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==", + "dev": true, + "license": "ISC", "dependencies": { - "@jest/get-type": "30.1.0", - "chalk": "^4.1.2", - "jest-diff": "30.2.0", - "pretty-format": "30.2.0" + "cross-spawn": "^7.0.6", + "signal-exit": "^4.0.1" }, "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/jest-circus/node_modules/jest-message-util": { - "version": "30.2.0", + "node_modules/foreground-child/node_modules/signal-exit": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", "dev": true, - "license": "MIT", - "dependencies": { - "@babel/code-frame": "^7.27.1", - "@jest/types": "30.2.0", - "@types/stack-utils": "^2.0.3", - "chalk": "^4.1.2", - "graceful-fs": "^4.2.11", - "micromatch": "^4.0.8", - "pretty-format": "30.2.0", - "slash": "^3.0.0", - "stack-utils": "^2.0.6" - }, + "license": "ISC", "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/jest-circus/node_modules/jest-util": { - "version": "30.2.0", - "dev": true, + "node_modules/form-data": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.5.tgz", + "integrity": "sha512-8RipRLol37bNs2bhoV67fiTEvdTrbMUYcFTiy3+wuuOnUog2QBHCZWXDRijWQfAkhBj2Uf5UnVaiWwA5vdd82w==", "license": "MIT", "dependencies": { - "@jest/types": "30.2.0", - "@types/node": "*", - "chalk": "^4.1.2", - "ci-info": "^4.2.0", - "graceful-fs": "^4.2.11", - "picomatch": "^4.0.2" + "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": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + "node": ">= 6" } }, - "node_modules/jest-circus/node_modules/picomatch": { - "version": "4.0.3", - "dev": true, + "node_modules/form-data/node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", "license": "MIT", "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" + "node": ">= 0.6" } }, - "node_modules/jest-circus/node_modules/pretty-format": { - "version": "30.2.0", - "dev": true, + "node_modules/form-data/node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", "license": "MIT", "dependencies": { - "@jest/schemas": "30.0.5", - "ansi-styles": "^5.2.0", - "react-is": "^18.3.1" + "mime-db": "1.52.0" }, "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + "node": ">= 0.6" } }, - "node_modules/jest-cli": { - "version": "30.2.0", - "dev": true, + "node_modules/forwarded": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", + "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", "license": "MIT", - "dependencies": { - "@jest/core": "30.2.0", - "@jest/test-result": "30.2.0", - "@jest/types": "30.2.0", - "chalk": "^4.1.2", - "exit-x": "^0.2.2", - "import-local": "^3.2.0", - "jest-config": "30.2.0", - "jest-util": "30.2.0", - "jest-validate": "30.2.0", - "yargs": "^17.7.2" - }, - "bin": { - "jest": "bin/jest.js" - }, "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - }, - "peerDependencies": { - "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" - }, - "peerDependenciesMeta": { - "node-notifier": { - "optional": true - } + "node": ">= 0.6" } }, - "node_modules/jest-cli/node_modules/@jest/console": { - "version": "30.2.0", - "dev": true, + "node_modules/fresh": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-2.0.0.tgz", + "integrity": "sha512-Rx/WycZ60HOaqLKAi6cHRKKI7zxWbJ31MhntmtwMoaTeF7XFH9hhBp8vITaMidfljRQ6eYWCKkaTK+ykVJHP2A==", "license": "MIT", - "dependencies": { - "@jest/types": "30.2.0", - "@types/node": "*", - "chalk": "^4.1.2", - "jest-message-util": "30.2.0", - "jest-util": "30.2.0", - "slash": "^3.0.0" - }, "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + "node": ">= 0.8" } }, - "node_modules/jest-cli/node_modules/@jest/schemas": { - "version": "30.0.5", + "node_modules/fs-constants": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz", + "integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==", + "dev": true, + "license": "MIT" + }, + "node_modules/fs-extra": { + "version": "11.3.2", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.3.2.tgz", + "integrity": "sha512-Xr9F6z6up6Ws+NjzMCZc6WXg2YFRlrLP9NQDO3VQrWrfiojdhS56TzueT88ze0uBdCTwEIhQ3ptnmKeWGFAe0A==", "dev": true, "license": "MIT", "dependencies": { - "@sinclair/typebox": "^0.34.0" + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" }, "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + "node": ">=14.14" } }, - "node_modules/jest-cli/node_modules/@jest/test-result": { - "version": "30.2.0", + "node_modules/fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", + "dev": true, + "license": "ISC" + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", "dev": true, + "hasInstallScript": true, "license": "MIT", - "dependencies": { - "@jest/console": "30.2.0", - "@jest/types": "30.2.0", - "@types/istanbul-lib-coverage": "^2.0.6", - "collect-v8-coverage": "^1.0.2" - }, + "optional": true, + "os": [ + "darwin" + ], "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" } }, - "node_modules/jest-cli/node_modules/@jest/types": { - "version": "30.2.0", + "node_modules/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/function.prototype.name": { + "version": "1.1.8", + "resolved": "https://registry.npmjs.org/function.prototype.name/-/function.prototype.name-1.1.8.tgz", + "integrity": "sha512-e5iwyodOHhbMr/yNrc7fDYG4qlbIvI5gajyzPnb5TCwyhjApznQh1BMFou9b30SevY43gCJKXycoCBjMbsuW0Q==", "dev": true, "license": "MIT", "dependencies": { - "@jest/pattern": "30.0.1", - "@jest/schemas": "30.0.5", - "@types/istanbul-lib-coverage": "^2.0.6", - "@types/istanbul-reports": "^3.0.4", - "@types/node": "*", - "@types/yargs": "^17.0.33", - "chalk": "^4.1.2" + "call-bind": "^1.0.8", + "call-bound": "^1.0.3", + "define-properties": "^1.2.1", + "functions-have-names": "^1.2.3", + "hasown": "^2.0.2", + "is-callable": "^1.2.7" }, "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/jest-cli/node_modules/@sinclair/typebox": { - "version": "0.34.41", + "node_modules/functional-red-black-tree": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz", + "integrity": "sha512-dsKNQNdj6xA3T+QlADDA7mOSlX0qiMINjn0cgr+eGHGsbSHzTabcIogz2+p/iqP1Xs6EP/sS2SbqH+brGTbq0g==", "dev": true, "license": "MIT" }, - "node_modules/jest-cli/node_modules/ansi-styles": { - "version": "5.2.0", + "node_modules/functions-have-names": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/functions-have-names/-/functions-have-names-1.2.3.tgz", + "integrity": "sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==", "dev": true, "license": "MIT", - "engines": { - "node": ">=10" - }, "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/jest-cli/node_modules/cliui": { - "version": "8.0.1", + "node_modules/generator-function": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/generator-function/-/generator-function-2.0.1.tgz", + "integrity": "sha512-SFdFmIJi+ybC0vjlHN0ZGVGHc3lgE0DxPAT0djjVg+kjOnSqclqmj0KQ7ykTOLP6YxoqOvuAODGdcHJn+43q3g==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/gensync": { + "version": "1.0.0-beta.2", + "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", + "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", "license": "ISC", - "dependencies": { - "string-width": "^4.2.0", - "strip-ansi": "^6.0.1", - "wrap-ansi": "^7.0.0" - }, "engines": { - "node": ">=12" + "node": "6.* || 8.* || >= 10.*" + } + }, + "node_modules/get-east-asian-width": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/get-east-asian-width/-/get-east-asian-width-1.4.0.tgz", + "integrity": "sha512-QZjmEOC+IT1uk6Rx0sX22V6uHWVwbdbxf1faPqJ1QhLdGgsRGCZoyaQBm/piRdJy/D2um6hM1UP7ZEeQ4EkP+Q==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/jest-cli/node_modules/jest-message-util": { - "version": "30.2.0", - "dev": true, + "node_modules/get-intrinsic": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", + "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", "license": "MIT", "dependencies": { - "@babel/code-frame": "^7.27.1", - "@jest/types": "30.2.0", - "@types/stack-utils": "^2.0.3", - "chalk": "^4.1.2", - "graceful-fs": "^4.2.11", - "micromatch": "^4.0.8", - "pretty-format": "30.2.0", - "slash": "^3.0.0", - "stack-utils": "^2.0.6" + "call-bind-apply-helpers": "^1.0.2", + "es-define-property": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.1.1", + "function-bind": "^1.1.2", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2", + "math-intrinsics": "^1.1.0" }, "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/jest-cli/node_modules/jest-util": { - "version": "30.2.0", + "node_modules/get-package-type": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/get-package-type/-/get-package-type-0.1.0.tgz", + "integrity": "sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==", "dev": true, "license": "MIT", - "dependencies": { - "@jest/types": "30.2.0", - "@types/node": "*", - "chalk": "^4.1.2", - "ci-info": "^4.2.0", - "graceful-fs": "^4.2.11", - "picomatch": "^4.0.2" - }, "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + "node": ">=8.0.0" } }, - "node_modules/jest-cli/node_modules/picomatch": { - "version": "4.0.3", + "node_modules/get-port": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/get-port/-/get-port-7.1.0.tgz", + "integrity": "sha512-QB9NKEeDg3xxVwCCwJQ9+xycaz6pBB6iQ76wiWMl1927n0Kir6alPiP+yuiICLLU4jpMe08dXfpebuQppFA2zw==", "dev": true, "license": "MIT", "engines": { - "node": ">=12" + "node": ">=16" }, "funding": { - "url": "https://github.com/sponsors/jonschlinkert" + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/jest-cli/node_modules/pretty-format": { - "version": "30.2.0", - "dev": true, + "node_modules/get-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", + "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", "license": "MIT", "dependencies": { - "@jest/schemas": "30.0.5", - "ansi-styles": "^5.2.0", - "react-is": "^18.3.1" + "dunder-proto": "^1.0.1", + "es-object-atoms": "^1.0.0" }, "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + "node": ">= 0.4" } }, - "node_modules/jest-cli/node_modules/wrap-ansi": { - "version": "7.0.0", + "node_modules/get-source": { + "version": "2.0.12", + "resolved": "https://registry.npmjs.org/get-source/-/get-source-2.0.12.tgz", + "integrity": "sha512-X5+4+iD+HoSeEED+uwrQ07BOQr0kEDFMVqqpBuI+RaZBpBpHCuXxo70bjar6f0b0u/DQJsJ7ssurpP0V60Az+w==", "dev": true, - "license": "MIT", + "license": "Unlicense", "dependencies": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - }, + "data-uri-to-buffer": "^2.0.0", + "source-map": "^0.6.1" + } + }, + "node_modules/get-source/node_modules/data-uri-to-buffer": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-2.0.2.tgz", + "integrity": "sha512-ND9qDTLc6diwj+Xe5cdAgVTbLVdXbtxTJRXRhli8Mowuaan+0EJOtdqJ0QCHNSSPyoXGx9HX2/VMnKeC34AChA==", + "dev": true, + "license": "MIT" + }, + "node_modules/get-stream": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", + "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", + "license": "MIT", "engines": { "node": ">=10" }, "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/jest-cli/node_modules/wrap-ansi/node_modules/ansi-styles": { - "version": "4.3.0", + "node_modules/get-symbol-description": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.1.0.tgz", + "integrity": "sha512-w9UMqWwJxHNOvoNzSJ2oPF5wvYcvP7jUvYzhp67yEhTi17ZDBBC1z9pTdGuzjD+EFIqLSYRweZjqfiPzQ06Ebg==", "dev": true, "license": "MIT", "dependencies": { - "color-convert": "^2.0.1" + "call-bound": "^1.0.3", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.6" }, "engines": { - "node": ">=8" + "node": ">= 0.4" }, "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/jest-cli/node_modules/yargs": { - "version": "17.7.2", + "node_modules/get-tsconfig": { + "version": "4.13.0", + "resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.13.0.tgz", + "integrity": "sha512-1VKTZJCwBrvbd+Wn3AOgQP/2Av+TfTCOlE4AcRJE72W1ksZXbAx8PPBR9RzgTeSPzlPMHrbANMH3LbltH73wxQ==", "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" + "resolve-pkg-maps": "^1.0.0" }, - "engines": { - "node": ">=12" + "funding": { + "url": "https://github.com/privatenumber/get-tsconfig?sponsor=1" } }, - "node_modules/jest-config": { - "version": "30.2.0", + "node_modules/get-uri": { + "version": "6.0.5", + "resolved": "https://registry.npmjs.org/get-uri/-/get-uri-6.0.5.tgz", + "integrity": "sha512-b1O07XYq8eRuVzBNgJLstU6FYc1tS6wnMtF1I1D9lE8LxZSOGZ7LhxN54yPP6mGw5f2CkXY2BQUL9Fx41qvcIg==", "dev": true, "license": "MIT", "dependencies": { - "@babel/core": "^7.27.4", - "@jest/get-type": "30.1.0", - "@jest/pattern": "30.0.1", - "@jest/test-sequencer": "30.2.0", - "@jest/types": "30.2.0", - "babel-jest": "30.2.0", - "chalk": "^4.1.2", - "ci-info": "^4.2.0", - "deepmerge": "^4.3.1", - "glob": "^10.3.10", - "graceful-fs": "^4.2.11", - "jest-circus": "30.2.0", - "jest-docblock": "30.2.0", - "jest-environment-node": "30.2.0", - "jest-regex-util": "30.0.1", - "jest-resolve": "30.2.0", - "jest-runner": "30.2.0", - "jest-util": "30.2.0", - "jest-validate": "30.2.0", - "micromatch": "^4.0.8", - "parse-json": "^5.2.0", - "pretty-format": "30.2.0", - "slash": "^3.0.0", - "strip-json-comments": "^3.1.1" + "basic-ftp": "^5.0.2", + "data-uri-to-buffer": "^6.0.2", + "debug": "^4.3.4" }, "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - }, - "peerDependencies": { - "@types/node": "*", - "esbuild-register": ">=3.4.0", - "ts-node": ">=9.0.0" - }, - "peerDependenciesMeta": { - "@types/node": { - "optional": true - }, - "esbuild-register": { - "optional": true - }, - "ts-node": { - "optional": true - } + "node": ">= 14" } }, - "node_modules/jest-config/node_modules/@jest/schemas": { - "version": "30.0.5", + "node_modules/glob": { + "version": "13.0.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-13.0.0.tgz", + "integrity": "sha512-tvZgpqk6fz4BaNZ66ZsRaZnbHvP/jG3uKJvAZOwEVUL4RTA5nJeeLYfyN9/VA8NX/V3IBG+hkeuGpKjvELkVhA==", "dev": true, - "license": "MIT", + "license": "BlueOak-1.0.0", "dependencies": { - "@sinclair/typebox": "^0.34.0" + "minimatch": "^10.1.1", + "minipass": "^7.1.2", + "path-scurry": "^2.0.0" }, "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + "node": "20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/jest-config/node_modules/@jest/types": { - "version": "30.2.0", + "node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", "dev": true, - "license": "MIT", + "license": "ISC", "dependencies": { - "@jest/pattern": "30.0.1", - "@jest/schemas": "30.0.5", - "@types/istanbul-lib-coverage": "^2.0.6", - "@types/istanbul-reports": "^3.0.4", - "@types/node": "*", - "@types/yargs": "^17.0.33", - "chalk": "^4.1.2" + "is-glob": "^4.0.1" }, "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + "node": ">= 6" } }, - "node_modules/jest-config/node_modules/@sinclair/typebox": { - "version": "0.34.41", - "dev": true, - "license": "MIT" + "node_modules/glob-to-regexp": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz", + "integrity": "sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==", + "license": "BSD-2-Clause" }, - "node_modules/jest-config/node_modules/ansi-styles": { - "version": "5.2.0", + "node_modules/glob/node_modules/minimatch": { + "version": "10.1.1", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.1.1.tgz", + "integrity": "sha512-enIvLvRAFZYXJzkCYG5RKmPfrFArdLv+R+lbQ53BmIMLIry74bjKzX6iHAm8WYamJkhSSEabrWN5D97XnKObjQ==", "dev": true, - "license": "MIT", + "license": "BlueOak-1.0.0", + "dependencies": { + "@isaacs/brace-expansion": "^5.0.0" + }, "engines": { - "node": ">=10" + "node": "20 || >=22" }, "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" + "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/jest-config/node_modules/glob": { - "version": "10.4.5", + "node_modules/globals": { + "version": "14.0.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-14.0.0.tgz", + "integrity": "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==", "dev": true, - "license": "ISC", - "dependencies": { - "foreground-child": "^3.1.0", - "jackspeak": "^3.1.2", - "minimatch": "^9.0.4", - "minipass": "^7.1.2", - "package-json-from-dist": "^1.0.0", - "path-scurry": "^1.11.1" - }, - "bin": { - "glob": "dist/esm/bin.mjs" + "license": "MIT", + "engines": { + "node": ">=18" }, "funding": { - "url": "https://github.com/sponsors/isaacs" + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/jest-config/node_modules/jackspeak": { - "version": "3.4.3", + "node_modules/globalthis": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/globalthis/-/globalthis-1.0.4.tgz", + "integrity": "sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ==", "dev": true, - "license": "BlueOak-1.0.0", + "license": "MIT", "dependencies": { - "@isaacs/cliui": "^8.0.2" + "define-properties": "^1.2.1", + "gopd": "^1.0.1" }, - "funding": { - "url": "https://github.com/sponsors/isaacs" + "engines": { + "node": ">= 0.4" }, - "optionalDependencies": { - "@pkgjs/parseargs": "^0.11.0" + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/jest-config/node_modules/jest-util": { - "version": "30.2.0", - "dev": true, + "node_modules/gopd": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", + "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", "license": "MIT", - "dependencies": { - "@jest/types": "30.2.0", - "@types/node": "*", - "chalk": "^4.1.2", - "ci-info": "^4.2.0", - "graceful-fs": "^4.2.11", - "picomatch": "^4.0.2" - }, "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/jest-config/node_modules/lru-cache": { - "version": "10.4.3", - "dev": true, + "node_modules/graceful-fs": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", "license": "ISC" }, - "node_modules/jest-config/node_modules/minimatch": { - "version": "9.0.5", - "dev": true, - "license": "ISC", + "node_modules/graphql": { + "version": "16.12.0", + "resolved": "https://registry.npmjs.org/graphql/-/graphql-16.12.0.tgz", + "integrity": "sha512-DKKrynuQRne0PNpEbzuEdHlYOMksHSUI8Zc9Unei5gTsMNA2/vMpoMz/yKba50pejK56qj98qM0SjYxAKi13gQ==", + "license": "MIT", + "engines": { + "node": "^12.22.0 || ^14.16.0 || ^16.0.0 || >=17.0.0" + } + }, + "node_modules/graphql-tag": { + "version": "2.12.6", + "resolved": "https://registry.npmjs.org/graphql-tag/-/graphql-tag-2.12.6.tgz", + "integrity": "sha512-FdSNcu2QQcWnM2VNvSCCDCVS5PpPqpzgFT8+GXzqJuoDd0CBncxCY278u4mhRO7tMgo2JjgJA5aZ+nWSQ/Z+xg==", + "license": "MIT", "dependencies": { - "brace-expansion": "^2.0.1" + "tslib": "^2.1.0" }, "engines": { - "node": ">=16 || 14 >=14.17" + "node": ">=10" }, - "funding": { - "url": "https://github.com/sponsors/isaacs" + "peerDependencies": { + "graphql": "^0.9.0 || ^0.10.0 || ^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0 || ^16.0.0" } }, - "node_modules/jest-config/node_modules/path-scurry": { - "version": "1.11.1", + "node_modules/handlebars": { + "version": "4.7.8", + "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.7.8.tgz", + "integrity": "sha512-vafaFqs8MZkRrSX7sFVUdo3ap/eNiLnb4IakshzvP56X5Nr1iGKAIqdX6tMlm6HcNRIkr6AxO5jFEoJzzpT8aQ==", "dev": true, - "license": "BlueOak-1.0.0", + "license": "MIT", "dependencies": { - "lru-cache": "^10.2.0", - "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" + "minimist": "^1.2.5", + "neo-async": "^2.6.2", + "source-map": "^0.6.1", + "wordwrap": "^1.0.0" + }, + "bin": { + "handlebars": "bin/handlebars" }, "engines": { - "node": ">=16 || 14 >=14.18" + "node": ">=0.4.7" }, - "funding": { - "url": "https://github.com/sponsors/isaacs" + "optionalDependencies": { + "uglify-js": "^3.1.4" } }, - "node_modules/jest-config/node_modules/picomatch": { - "version": "4.0.3", + "node_modules/has-bigints": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.1.0.tgz", + "integrity": "sha512-R3pbpkcIqv2Pm3dUwgjclDRVmWpTJW2DcMzcIhEXEx1oh/CEMObMm3KLmRJOdvhM7o4uQBnwr8pzRK2sJWIqfg==", "dev": true, "license": "MIT", "engines": { - "node": ">=12" + "node": ">= 0.4" }, "funding": { - "url": "https://github.com/sponsors/jonschlinkert" + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/jest-config/node_modules/pretty-format": { - "version": "30.2.0", - "dev": true, + "node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", "license": "MIT", - "dependencies": { - "@jest/schemas": "30.0.5", - "ansi-styles": "^5.2.0", - "react-is": "^18.3.1" - }, "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + "node": ">=8" } }, - "node_modules/jest-diff": { - "version": "29.7.0", - "dev": true, + "node_modules/has-property-descriptors": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz", + "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==", "license": "MIT", "dependencies": { - "chalk": "^4.0.0", - "diff-sequences": "^29.6.3", - "jest-get-type": "^29.6.3", - "pretty-format": "^29.7.0" + "es-define-property": "^1.0.0" }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/jest-docblock": { - "version": "30.2.0", + "node_modules/has-proto": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.2.0.tgz", + "integrity": "sha512-KIL7eQPfHQRC8+XluaIw7BHUwwqL19bQn4hzNgdr+1wXoU0KKj6rufu47lhY7KbJR2C6T6+PfyN0Ea7wkSS+qQ==", "dev": true, "license": "MIT", "dependencies": { - "detect-newline": "^3.1.0" + "dunder-proto": "^1.0.0" }, "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/jest-each": { - "version": "30.2.0", - "dev": true, + "node_modules/has-symbols": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", + "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", "license": "MIT", - "dependencies": { - "@jest/get-type": "30.1.0", - "@jest/types": "30.2.0", - "chalk": "^4.1.2", - "jest-util": "30.2.0", - "pretty-format": "30.2.0" - }, "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/jest-each/node_modules/@jest/schemas": { - "version": "30.0.5", - "dev": true, + "node_modules/has-tostringtag": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", + "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", "license": "MIT", "dependencies": { - "@sinclair/typebox": "^0.34.0" + "has-symbols": "^1.0.3" }, "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/jest-each/node_modules/@jest/types": { - "version": "30.2.0", - "dev": true, + "node_modules/hasown": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", + "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", "license": "MIT", "dependencies": { - "@jest/pattern": "30.0.1", - "@jest/schemas": "30.0.5", - "@types/istanbul-lib-coverage": "^2.0.6", - "@types/istanbul-reports": "^3.0.4", - "@types/node": "*", - "@types/yargs": "^17.0.33", - "chalk": "^4.1.2" + "function-bind": "^1.1.2" }, "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + "node": ">= 0.4" } }, - "node_modules/jest-each/node_modules/@sinclair/typebox": { - "version": "0.34.41", - "dev": true, + "node_modules/help-me": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/help-me/-/help-me-5.0.0.tgz", + "integrity": "sha512-7xgomUX6ADmcYzFik0HzAxh/73YlKR9bmFzf51CZwR+b6YtzU2m0u49hQCqV6SvlqIqsaxovfwdvbnsw3b/zpg==", "license": "MIT" }, - "node_modules/jest-each/node_modules/ansi-styles": { - "version": "5.2.0", + "node_modules/hpagent": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/hpagent/-/hpagent-1.2.0.tgz", + "integrity": "sha512-A91dYTeIB6NoXG+PxTQpCCDDnfHsW9kc06Lvpu1TEe9gnd6ZFeiBoRO9JvzEv6xK7EX97/dUE8g/vBMTqTS3CA==", "dev": true, "license": "MIT", "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" + "node": ">=14" } }, - "node_modules/jest-each/node_modules/jest-util": { - "version": "30.2.0", + "node_modules/html-encoding-sniffer": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-4.0.0.tgz", + "integrity": "sha512-Y22oTqIU4uuPgEemfz7NDJz6OeKf12Lsu+QC+s3BVpda64lTiMYCyGwg5ki4vFxkMwQdeZDl2adZoqUgdFuTgQ==", "dev": true, "license": "MIT", "dependencies": { - "@jest/types": "30.2.0", - "@types/node": "*", - "chalk": "^4.1.2", - "ci-info": "^4.2.0", - "graceful-fs": "^4.2.11", - "picomatch": "^4.0.2" + "whatwg-encoding": "^3.1.1" }, "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + "node": ">=18" } }, - "node_modules/jest-each/node_modules/picomatch": { - "version": "4.0.3", + "node_modules/html-escaper": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", + "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==", "dev": true, - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } + "license": "MIT" }, - "node_modules/jest-each/node_modules/pretty-format": { - "version": "30.2.0", + "node_modules/htmlparser2": { + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-10.0.0.tgz", + "integrity": "sha512-TwAZM+zE5Tq3lrEHvOlvwgj1XLWQCtaaibSN11Q+gGBAS7Y1uZSWwXXRe4iF6OXnaq1riyQAPFOBtYc77Mxq0g==", "dev": true, + "funding": [ + "https://github.com/fb55/htmlparser2?sponsor=1", + { + "type": "github", + "url": "https://github.com/sponsors/fb55" + } + ], "license": "MIT", "dependencies": { - "@jest/schemas": "30.0.5", - "ansi-styles": "^5.2.0", - "react-is": "^18.3.1" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + "domelementtype": "^2.3.0", + "domhandler": "^5.0.3", + "domutils": "^3.2.1", + "entities": "^6.0.0" } }, - "node_modules/jest-environment-jsdom": { - "version": "30.0.5", - "dev": true, + "node_modules/http-errors": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.1.tgz", + "integrity": "sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ==", "license": "MIT", "dependencies": { - "@jest/environment": "30.0.5", - "@jest/environment-jsdom-abstract": "30.0.5", - "@types/jsdom": "^21.1.7", - "@types/node": "*", - "jsdom": "^26.1.0" + "depd": "~2.0.0", + "inherits": "~2.0.4", + "setprototypeof": "~1.2.0", + "statuses": "~2.0.2", + "toidentifier": "~1.0.1" }, "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - }, - "peerDependencies": { - "canvas": "^3.0.0" + "node": ">= 0.8" }, - "peerDependenciesMeta": { - "canvas": { - "optional": true - } + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" } }, - "node_modules/jest-environment-jsdom/node_modules/@jest/environment": { - "version": "30.0.5", - "dev": true, + "node_modules/http-proxy": { + "version": "1.18.1", + "resolved": "https://registry.npmjs.org/http-proxy/-/http-proxy-1.18.1.tgz", + "integrity": "sha512-7mz/721AbnJwIVbnaSv1Cz3Am0ZLT/UBwkC92VlxhXv/k/BBQfM2fXElQNC27BVGr0uwUpplYPQM9LnaBMR5NQ==", "license": "MIT", "dependencies": { - "@jest/fake-timers": "30.0.5", - "@jest/types": "30.0.5", - "@types/node": "*", - "jest-mock": "30.0.5" + "eventemitter3": "^4.0.0", + "follow-redirects": "^1.0.0", + "requires-port": "^1.0.0" }, "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + "node": ">=8.0.0" } }, - "node_modules/jest-environment-jsdom/node_modules/@jest/fake-timers": { - "version": "30.0.5", + "node_modules/http-proxy-agent": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-7.0.2.tgz", + "integrity": "sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==", "dev": true, "license": "MIT", "dependencies": { - "@jest/types": "30.0.5", - "@sinonjs/fake-timers": "^13.0.0", - "@types/node": "*", - "jest-message-util": "30.0.5", - "jest-mock": "30.0.5", - "jest-util": "30.0.5" + "agent-base": "^7.1.0", + "debug": "^4.3.4" }, "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + "node": ">= 14" } }, - "node_modules/jest-environment-jsdom/node_modules/@jest/schemas": { - "version": "30.0.5", + "node_modules/http2-client": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/http2-client/-/http2-client-1.3.5.tgz", + "integrity": "sha512-EC2utToWl4RKfs5zd36Mxq7nzHHBuomZboI0yYL6Y0RmBgT7Sgkq4rQ0ezFTYoIsSs7Tm9SJe+o2FcAg6GBhGA==", "dev": true, + "license": "MIT" + }, + "node_modules/https-proxy-agent": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.6.tgz", + "integrity": "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==", "license": "MIT", "dependencies": { - "@sinclair/typebox": "^0.34.0" + "agent-base": "^7.1.2", + "debug": "4" }, "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + "node": ">= 14" } }, - "node_modules/jest-environment-jsdom/node_modules/@jest/types": { - "version": "30.0.5", - "dev": true, + "node_modules/human-signals": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz", + "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==", + "license": "Apache-2.0", + "engines": { + "node": ">=10.17.0" + } + }, + "node_modules/iconv-lite": { + "version": "0.7.1", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.7.1.tgz", + "integrity": "sha512-2Tth85cXwGFHfvRgZWszZSvdo+0Xsqmw8k8ZwxScfcBneNUraK+dxRxRm24nszx80Y0TVio8kKLt5sLE7ZCLlw==", "license": "MIT", "dependencies": { - "@jest/pattern": "30.0.1", - "@jest/schemas": "30.0.5", - "@types/istanbul-lib-coverage": "^2.0.6", - "@types/istanbul-reports": "^3.0.4", - "@types/node": "*", - "@types/yargs": "^17.0.33", - "chalk": "^4.1.2" + "safer-buffer": ">= 2.1.2 < 3.0.0" }, "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + "node": ">=0.10.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" } }, - "node_modules/jest-environment-jsdom/node_modules/@sinclair/typebox": { - "version": "0.34.38", - "dev": true, - "license": "MIT" + "node_modules/ieee754": { + "version": "1.1.13", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.1.13.tgz", + "integrity": "sha512-4vf7I2LYV/HaWerSo3XmlMkp5eZ83i+/CDluXi/IGTs/O1sejBNhTtnxzmRZfvOUqj7lZjqHkeTvpgSFDlWZTg==", + "license": "BSD-3-Clause" }, - "node_modules/jest-environment-jsdom/node_modules/ansi-styles": { - "version": "5.2.0", + "node_modules/ignore": { + "version": "7.0.5", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.5.tgz", + "integrity": "sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==", "dev": true, "license": "MIT", "engines": { - "node": ">=10" - }, + "node": ">= 4" + } + }, + "node_modules/immer": { + "version": "9.0.21", + "resolved": "https://registry.npmjs.org/immer/-/immer-9.0.21.tgz", + "integrity": "sha512-bc4NBHqOqSfRW7POMkHd51LvClaeMXpm8dx0e8oE2GORbq5aRK7Bxl4FyzVLdGtLmvLKL7BTDBG5ACQm4HWjTA==", + "dev": true, + "license": "MIT", "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" + "type": "opencollective", + "url": "https://opencollective.com/immer" } }, - "node_modules/jest-environment-jsdom/node_modules/jest-message-util": { - "version": "30.0.5", + "node_modules/import-fresh": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz", + "integrity": "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==", "dev": true, "license": "MIT", "dependencies": { - "@babel/code-frame": "^7.27.1", - "@jest/types": "30.0.5", - "@types/stack-utils": "^2.0.3", - "chalk": "^4.1.2", - "graceful-fs": "^4.2.11", - "micromatch": "^4.0.8", - "pretty-format": "30.0.5", - "slash": "^3.0.0", - "stack-utils": "^2.0.6" + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" }, "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/jest-environment-jsdom/node_modules/jest-mock": { - "version": "30.0.5", + "node_modules/import-local": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/import-local/-/import-local-3.2.0.tgz", + "integrity": "sha512-2SPlun1JUPWoM6t3F0dw0FkCF/jWY8kttcY4f599GLTSjh2OCuuhdTkJQsEcZzBqbXZGKMK2OqW1oZsjtf/gQA==", "dev": true, "license": "MIT", "dependencies": { - "@jest/types": "30.0.5", - "@types/node": "*", - "jest-util": "30.0.5" + "pkg-dir": "^4.2.0", + "resolve-cwd": "^3.0.0" + }, + "bin": { + "import-local-fixture": "fixtures/cli.js" }, "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/jest-environment-jsdom/node_modules/jest-util": { - "version": "30.0.5", + "node_modules/imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", "dev": true, "license": "MIT", - "dependencies": { - "@jest/types": "30.0.5", - "@types/node": "*", - "chalk": "^4.1.2", - "ci-info": "^4.2.0", - "graceful-fs": "^4.2.11", - "picomatch": "^4.0.2" - }, "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + "node": ">=0.8.19" } }, - "node_modules/jest-environment-jsdom/node_modules/picomatch": { - "version": "4.0.3", + "node_modules/indent-string": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-5.0.0.tgz", + "integrity": "sha512-m6FAo/spmsW2Ab2fU35JTYwtOKa2yAwXSwgjSv1TJzh4Mh7mC3lzAOVLBprb72XsTrgkEIsl7YrFNAiDiRhIGg==", "dev": true, "license": "MIT", "engines": { "node": ">=12" }, "funding": { - "url": "https://github.com/sponsors/jonschlinkert" + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/jest-environment-jsdom/node_modules/pretty-format": { - "version": "30.0.5", + "node_modules/index-to-position": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/index-to-position/-/index-to-position-1.2.0.tgz", + "integrity": "sha512-Yg7+ztRkqslMAS2iFaU+Oa4KTSidr63OsFGlOrJoW981kIYO3CGCS3wA95P1mUi/IVSJkn0D479KTJpVpvFNuw==", "dev": true, "license": "MIT", - "dependencies": { - "@jest/schemas": "30.0.5", - "ansi-styles": "^5.2.0", - "react-is": "^18.3.1" - }, "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/jest-environment-node": { - "version": "30.2.0", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/environment": "30.2.0", - "@jest/fake-timers": "30.2.0", - "@jest/types": "30.2.0", - "@types/node": "*", - "jest-mock": "30.2.0", - "jest-util": "30.2.0", - "jest-validate": "30.2.0" + "node": ">=18" }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/jest-environment-node/node_modules/@jest/schemas": { - "version": "30.0.5", + "node_modules/inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", + "deprecated": "This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.", "dev": true, - "license": "MIT", + "license": "ISC", "dependencies": { - "@sinclair/typebox": "^0.34.0" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + "once": "^1.3.0", + "wrappy": "1" } }, - "node_modules/jest-environment-node/node_modules/@jest/types": { - "version": "30.2.0", + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "license": "ISC" + }, + "node_modules/ini": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", + "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==", + "license": "ISC" + }, + "node_modules/inquirer": { + "version": "8.2.7", + "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-8.2.7.tgz", + "integrity": "sha512-UjOaSel/iddGZJ5xP/Eixh6dY1XghiBw4XK13rCCIJcJfyhhoul/7KhLLUGtebEj6GDYM6Vnx/mVsjx2L/mFIA==", "dev": true, "license": "MIT", "dependencies": { - "@jest/pattern": "30.0.1", - "@jest/schemas": "30.0.5", - "@types/istanbul-lib-coverage": "^2.0.6", - "@types/istanbul-reports": "^3.0.4", - "@types/node": "*", - "@types/yargs": "^17.0.33", - "chalk": "^4.1.2" + "@inquirer/external-editor": "^1.0.0", + "ansi-escapes": "^4.2.1", + "chalk": "^4.1.1", + "cli-cursor": "^3.1.0", + "cli-width": "^3.0.0", + "figures": "^3.0.0", + "lodash": "^4.17.21", + "mute-stream": "0.0.8", + "ora": "^5.4.1", + "run-async": "^2.4.0", + "rxjs": "^7.5.5", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0", + "through": "^2.3.6", + "wrap-ansi": "^6.0.1" }, "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + "node": ">=12.0.0" } }, - "node_modules/jest-environment-node/node_modules/@sinclair/typebox": { - "version": "0.34.41", - "dev": true, - "license": "MIT" - }, - "node_modules/jest-environment-node/node_modules/jest-util": { - "version": "30.2.0", + "node_modules/internal-slot": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.1.0.tgz", + "integrity": "sha512-4gd7VpWNQNB4UKKCFFVcp1AVv+FMOgs9NKzjHKusc8jTMhd5eL1NqQqOpE0KzMds804/yHlglp3uxgluOqAPLw==", "dev": true, "license": "MIT", "dependencies": { - "@jest/types": "30.2.0", - "@types/node": "*", - "chalk": "^4.1.2", - "ci-info": "^4.2.0", - "graceful-fs": "^4.2.11", - "picomatch": "^4.0.2" + "es-errors": "^1.3.0", + "hasown": "^2.0.2", + "side-channel": "^1.1.0" }, "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + "node": ">= 0.4" } }, - "node_modules/jest-environment-node/node_modules/picomatch": { - "version": "4.0.3", + "node_modules/ip-address": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/ip-address/-/ip-address-10.1.0.tgz", + "integrity": "sha512-XXADHxXmvT9+CRxhXg56LJovE+bmWnEWB78LB83VZTprKTmaC5QfruXocxzTZ2Kl0DNwKuBdlIhjL8LeY8Sf8Q==", "dev": true, "license": "MIT", "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" + "node": ">= 12" } }, - "node_modules/jest-get-type": { - "version": "29.6.3", - "dev": true, + "node_modules/ipaddr.js": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", + "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", "license": "MIT", "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + "node": ">= 0.10" } }, - "node_modules/jest-haste-map": { - "version": "30.2.0", - "dev": true, + "node_modules/is-arguments": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/is-arguments/-/is-arguments-1.2.0.tgz", + "integrity": "sha512-7bVbi0huj/wrIAOzb8U1aszg9kdi3KN/CyU19CTI7tAoZYEZoL9yCDXpbXN+uPsuWnP02cyug1gleqq+TU+YCA==", "license": "MIT", "dependencies": { - "@jest/types": "30.2.0", - "@types/node": "*", - "anymatch": "^3.1.3", - "fb-watchman": "^2.0.2", - "graceful-fs": "^4.2.11", - "jest-regex-util": "30.0.1", - "jest-util": "30.2.0", - "jest-worker": "30.2.0", - "micromatch": "^4.0.8", - "walker": "^1.0.8" + "call-bound": "^1.0.2", + "has-tostringtag": "^1.0.2" }, "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + "node": ">= 0.4" }, - "optionalDependencies": { - "fsevents": "^2.3.3" + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/jest-haste-map/node_modules/@jest/schemas": { - "version": "30.0.5", + "node_modules/is-array-buffer": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.5.tgz", + "integrity": "sha512-DDfANUiiG2wC1qawP66qlTugJeL5HyzMpfr8lLK+jMQirGzNod0B12cFB/9q838Ru27sBwfw78/rdoU7RERz6A==", "dev": true, "license": "MIT", "dependencies": { - "@sinclair/typebox": "^0.34.0" + "call-bind": "^1.0.8", + "call-bound": "^1.0.3", + "get-intrinsic": "^1.2.6" }, "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/jest-haste-map/node_modules/@jest/types": { - "version": "30.2.0", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/pattern": "30.0.1", - "@jest/schemas": "30.0.5", - "@types/istanbul-lib-coverage": "^2.0.6", - "@types/istanbul-reports": "^3.0.4", - "@types/node": "*", - "@types/yargs": "^17.0.33", - "chalk": "^4.1.2" + "node": ">= 0.4" }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/jest-haste-map/node_modules/@sinclair/typebox": { - "version": "0.34.41", + "node_modules/is-arrayish": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", + "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==", "dev": true, "license": "MIT" }, - "node_modules/jest-haste-map/node_modules/jest-util": { - "version": "30.2.0", + "node_modules/is-async-function": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-async-function/-/is-async-function-2.1.1.tgz", + "integrity": "sha512-9dgM/cZBnNvjzaMYHVoxxfPj2QXt22Ev7SuuPrs+xav0ukGB0S6d4ydZdEiM48kLx5kDV+QBPrpVnFyefL8kkQ==", "dev": true, "license": "MIT", "dependencies": { - "@jest/types": "30.2.0", - "@types/node": "*", - "chalk": "^4.1.2", - "ci-info": "^4.2.0", - "graceful-fs": "^4.2.11", - "picomatch": "^4.0.2" + "async-function": "^1.0.0", + "call-bound": "^1.0.3", + "get-proto": "^1.0.1", + "has-tostringtag": "^1.0.2", + "safe-regex-test": "^1.1.0" }, "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/jest-haste-map/node_modules/picomatch": { - "version": "4.0.3", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12" + "node": ">= 0.4" }, "funding": { - "url": "https://github.com/sponsors/jonschlinkert" + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/jest-html-reporter": { - "version": "4.3.0", + "node_modules/is-bigint": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.1.0.tgz", + "integrity": "sha512-n4ZT37wG78iz03xPRKJrHTdZbe3IicyucEtdRsV5yglwc3GyUfbAfpSeD0FJ41NbUNSt5wbhqfp1fS+BgnvDFQ==", "dev": true, "license": "MIT", "dependencies": { - "@jest/reporters": "^30.0.2", - "@jest/test-result": "^30.0.2", - "@jest/types": "^30.0.1", - "dateformat": "3.0.2", - "mkdirp": "^1.0.3", - "strip-ansi": "6.0.1", - "xmlbuilder": "15.0.0" + "has-bigints": "^1.0.2" }, "engines": { - "node": ">=14.0.0" + "node": ">= 0.4" }, - "peerDependencies": { - "jest": "19.x - 30.x", - "typescript": "^3.7.x || ^4.3.x || ^5.x" + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/jest-html-reporter/node_modules/@jest/console": { - "version": "30.2.0", + "node_modules/is-binary-path": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", + "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", "dev": true, "license": "MIT", "dependencies": { - "@jest/types": "30.2.0", - "@types/node": "*", - "chalk": "^4.1.2", - "jest-message-util": "30.2.0", - "jest-util": "30.2.0", - "slash": "^3.0.0" + "binary-extensions": "^2.0.0" }, "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + "node": ">=8" } }, - "node_modules/jest-html-reporter/node_modules/@jest/schemas": { - "version": "30.0.5", + "node_modules/is-boolean-object": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.2.2.tgz", + "integrity": "sha512-wa56o2/ElJMYqjCjGkXri7it5FbebW5usLw/nPmCMs5DeZ7eziSYZhSmPRn0txqeW4LnAmQQU7FgqLpsEFKM4A==", "dev": true, "license": "MIT", "dependencies": { - "@sinclair/typebox": "^0.34.0" + "call-bound": "^1.0.3", + "has-tostringtag": "^1.0.2" }, "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/jest-html-reporter/node_modules/@jest/test-result": { - "version": "30.2.0", + "node_modules/is-buffer": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", + "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==", + "license": "MIT" + }, + "node_modules/is-builtin-module": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/is-builtin-module/-/is-builtin-module-5.0.0.tgz", + "integrity": "sha512-f4RqJKBUe5rQkJ2eJEJBXSticB3hGbN9j0yxxMQFqIW89Jp9WYFtzfTcRlstDKVUTRzSOTLKRfO9vIztenwtxA==", "dev": true, "license": "MIT", "dependencies": { - "@jest/console": "30.2.0", - "@jest/types": "30.2.0", - "@types/istanbul-lib-coverage": "^2.0.6", - "collect-v8-coverage": "^1.0.2" + "builtin-modules": "^5.0.0" }, "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + "node": ">=18.20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/jest-html-reporter/node_modules/@jest/types": { - "version": "30.2.0", + "node_modules/is-builtin-module/node_modules/builtin-modules": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-5.0.0.tgz", + "integrity": "sha512-bkXY9WsVpY7CvMhKSR6pZilZu9Ln5WDrKVBUXf2S443etkmEO4V58heTecXcUIsNsi4Rx8JUO4NfX1IcQl4deg==", "dev": true, "license": "MIT", - "dependencies": { - "@jest/pattern": "30.0.1", - "@jest/schemas": "30.0.5", - "@types/istanbul-lib-coverage": "^2.0.6", - "@types/istanbul-reports": "^3.0.4", - "@types/node": "*", - "@types/yargs": "^17.0.33", - "chalk": "^4.1.2" - }, "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + "node": ">=18.20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/jest-html-reporter/node_modules/@sinclair/typebox": { - "version": "0.34.41", + "node_modules/is-bun-module": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-bun-module/-/is-bun-module-2.0.0.tgz", + "integrity": "sha512-gNCGbnnnnFAUGKeZ9PdbyeGYJqewpmc2aKHUEMO5nQPWU9lOmv7jcmQIv+qHD8fXW6W7qfuCwX4rY9LNRjXrkQ==", "dev": true, - "license": "MIT" + "license": "MIT", + "dependencies": { + "semver": "^7.7.1" + } }, - "node_modules/jest-html-reporter/node_modules/ansi-styles": { - "version": "5.2.0", - "dev": true, + "node_modules/is-callable": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz", + "integrity": "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==", "license": "MIT", "engines": { - "node": ">=10" + "node": ">= 0.4" }, "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/jest-html-reporter/node_modules/jest-message-util": { - "version": "30.2.0", + "node_modules/is-core-module": { + "version": "2.16.1", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.1.tgz", + "integrity": "sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==", "dev": true, "license": "MIT", "dependencies": { - "@babel/code-frame": "^7.27.1", - "@jest/types": "30.2.0", - "@types/stack-utils": "^2.0.3", - "chalk": "^4.1.2", - "graceful-fs": "^4.2.11", - "micromatch": "^4.0.8", - "pretty-format": "30.2.0", - "slash": "^3.0.0", - "stack-utils": "^2.0.6" + "hasown": "^2.0.2" }, "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/jest-html-reporter/node_modules/jest-util": { - "version": "30.2.0", + "node_modules/is-data-view": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-data-view/-/is-data-view-1.0.2.tgz", + "integrity": "sha512-RKtWF8pGmS87i2D6gqQu/l7EYRlVdfzemCJN/P3UOs//x1QE7mfhvzHIApBTRf7axvT6DMGwSwBXYCT0nfB9xw==", "dev": true, "license": "MIT", "dependencies": { - "@jest/types": "30.2.0", - "@types/node": "*", - "chalk": "^4.1.2", - "ci-info": "^4.2.0", - "graceful-fs": "^4.2.11", - "picomatch": "^4.0.2" + "call-bound": "^1.0.2", + "get-intrinsic": "^1.2.6", + "is-typed-array": "^1.1.13" }, "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/jest-html-reporter/node_modules/picomatch": { - "version": "4.0.3", + "node_modules/is-date-object": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.1.0.tgz", + "integrity": "sha512-PwwhEakHVKTdRNVOw+/Gyh0+MzlCl4R6qKvkhuvLtPMggI1WAHt9sOwZxQLSGpUaDnrdyDsomoRgNnCfKNSXXg==", "dev": true, "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "has-tostringtag": "^1.0.2" + }, "engines": { - "node": ">=12" + "node": ">= 0.4" }, "funding": { - "url": "https://github.com/sponsors/jonschlinkert" + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/jest-html-reporter/node_modules/pretty-format": { - "version": "30.2.0", - "dev": true, + "node_modules/is-docker": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.2.1.tgz", + "integrity": "sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==", "license": "MIT", - "dependencies": { - "@jest/schemas": "30.0.5", - "ansi-styles": "^5.2.0", - "react-is": "^18.3.1" + "bin": { + "is-docker": "cli.js" }, "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/jest-leak-detector": { - "version": "30.2.0", + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", "dev": true, "license": "MIT", - "dependencies": { - "@jest/get-type": "30.1.0", - "pretty-format": "30.2.0" - }, "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + "node": ">=0.10.0" } }, - "node_modules/jest-leak-detector/node_modules/@jest/schemas": { - "version": "30.0.5", + "node_modules/is-finalizationregistry": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-finalizationregistry/-/is-finalizationregistry-1.1.1.tgz", + "integrity": "sha512-1pC6N8qWJbWoPtEjgcL2xyhQOP491EQjeUo3qTKcmV8YSDDJrOepfG8pcC7h/QgnQHYSv0mJ3Z/ZWxmatVrysg==", "dev": true, "license": "MIT", "dependencies": { - "@sinclair/typebox": "^0.34.0" + "call-bound": "^1.0.3" }, "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/jest-leak-detector/node_modules/@sinclair/typebox": { - "version": "0.34.41", - "dev": true, - "license": "MIT" + "node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "license": "MIT", + "engines": { + "node": ">=8" + } }, - "node_modules/jest-leak-detector/node_modules/ansi-styles": { - "version": "5.2.0", + "node_modules/is-generator-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-generator-fn/-/is-generator-fn-2.1.0.tgz", + "integrity": "sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ==", "dev": true, "license": "MIT", "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" + "node": ">=6" } }, - "node_modules/jest-leak-detector/node_modules/pretty-format": { - "version": "30.2.0", - "dev": true, + "node_modules/is-generator-function": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/is-generator-function/-/is-generator-function-1.1.2.tgz", + "integrity": "sha512-upqt1SkGkODW9tsGNG5mtXTXtECizwtS2kA161M+gJPc1xdb/Ax629af6YrTwcOeQHbewrPNlE5Dx7kzvXTizA==", "license": "MIT", "dependencies": { - "@jest/schemas": "30.0.5", - "ansi-styles": "^5.2.0", - "react-is": "^18.3.1" + "call-bound": "^1.0.4", + "generator-function": "^2.0.0", + "get-proto": "^1.0.1", + "has-tostringtag": "^1.0.2", + "safe-regex-test": "^1.1.0" }, "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/jest-matcher-utils": { - "version": "29.7.0", + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", "dev": true, "license": "MIT", "dependencies": { - "chalk": "^4.0.0", - "jest-diff": "^29.7.0", - "jest-get-type": "^29.6.3", - "pretty-format": "^29.7.0" + "is-extglob": "^2.1.1" }, "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + "node": ">=0.10.0" } }, - "node_modules/jest-message-util": { - "version": "29.7.0", + "node_modules/is-interactive": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-interactive/-/is-interactive-1.0.0.tgz", + "integrity": "sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w==", "dev": true, "license": "MIT", - "dependencies": { - "@babel/code-frame": "^7.12.13", - "@jest/types": "^29.6.3", - "@types/stack-utils": "^2.0.0", - "chalk": "^4.0.0", - "graceful-fs": "^4.2.9", - "micromatch": "^4.0.4", - "pretty-format": "^29.7.0", - "slash": "^3.0.0", - "stack-utils": "^2.0.3" - }, "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + "node": ">=8" } }, - "node_modules/jest-mock": { - "version": "30.2.0", + "node_modules/is-map": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/is-map/-/is-map-2.0.3.tgz", + "integrity": "sha512-1Qed0/Hr2m+YqxnM09CjA2d/i6YZNfF6R2oRAOj36eUdS6qIV/huPJNSEpKbupewFs+ZsJlxsjjPbc0/afW6Lw==", "dev": true, "license": "MIT", - "dependencies": { - "@jest/types": "30.2.0", - "@types/node": "*", - "jest-util": "30.2.0" - }, "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/jest-mock-extended": { - "version": "4.0.0", + "node_modules/is-negative-zero": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.3.tgz", + "integrity": "sha512-5KoIu2Ngpyek75jXodFvnafB6DJgr3u8uuK0LEZJjrU19DrMD3EVERaR8sjz8CCGgpZvxPl9SuE1GMVPFHx1mw==", "dev": true, "license": "MIT", - "dependencies": { - "ts-essentials": "^10.0.2" + "engines": { + "node": ">= 0.4" }, - "peerDependencies": { - "@jest/globals": "^28.0.0 || ^29.0.0 || ^30.0.0", - "jest": "^24.0.0 || ^25.0.0 || ^26.0.0 || ^27.0.0 || ^28.0.0 || ^29.0.0 || ^30.0.0", - "typescript": "^3.0.0 || ^4.0.0 || ^5.0.0" + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/jest-mock/node_modules/@jest/schemas": { - "version": "30.0.5", + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", "dev": true, "license": "MIT", - "dependencies": { - "@sinclair/typebox": "^0.34.0" - }, "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + "node": ">=0.12.0" } }, - "node_modules/jest-mock/node_modules/@jest/types": { - "version": "30.2.0", + "node_modules/is-number-object": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.1.1.tgz", + "integrity": "sha512-lZhclumE1G6VYD8VHe35wFaIif+CTy5SJIi5+3y4psDgWu4wPDoBhF8NxUOinEc7pHgiTsT6MaBb92rKhhD+Xw==", "dev": true, "license": "MIT", "dependencies": { - "@jest/pattern": "30.0.1", - "@jest/schemas": "30.0.5", - "@types/istanbul-lib-coverage": "^2.0.6", - "@types/istanbul-reports": "^3.0.4", - "@types/node": "*", - "@types/yargs": "^17.0.33", - "chalk": "^4.1.2" + "call-bound": "^1.0.3", + "has-tostringtag": "^1.0.2" }, "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/jest-mock/node_modules/@sinclair/typebox": { - "version": "0.34.41", + "node_modules/is-port-reachable": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/is-port-reachable/-/is-port-reachable-4.0.0.tgz", + "integrity": "sha512-9UoipoxYmSk6Xy7QFgRv2HDyaysmgSG75TFQs6S+3pDM7ZhKTF/bskZV+0UlABHzKjNVhPjYCLfeZUEg1wXxig==", + "license": "MIT", + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-potential-custom-element-name": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.1.tgz", + "integrity": "sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==", "dev": true, "license": "MIT" }, - "node_modules/jest-mock/node_modules/jest-util": { - "version": "30.2.0", + "node_modules/is-promise": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-4.0.0.tgz", + "integrity": "sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ==", + "license": "MIT" + }, + "node_modules/is-reference": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/is-reference/-/is-reference-1.2.1.tgz", + "integrity": "sha512-U82MsXXiFIrjCK4otLT+o2NA2Cd2g5MLoOVXUZjIOhLurrRxpEXzI8O0KZHr3IjLvlAH1kTPYSuqer5T9ZVBKQ==", "dev": true, "license": "MIT", "dependencies": { - "@jest/types": "30.2.0", - "@types/node": "*", - "chalk": "^4.1.2", - "ci-info": "^4.2.0", - "graceful-fs": "^4.2.11", - "picomatch": "^4.0.2" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + "@types/estree": "*" } }, - "node_modules/jest-mock/node_modules/picomatch": { - "version": "4.0.3", - "dev": true, + "node_modules/is-regex": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.2.1.tgz", + "integrity": "sha512-MjYsKHO5O7mCsmRGxWcLWheFqN9DJ/2TmngvjKXihe6efViPqc274+Fx/4fYj/r03+ESvBdTXK0V6tA3rgez1g==", "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "gopd": "^1.2.0", + "has-tostringtag": "^1.0.2", + "hasown": "^2.0.2" + }, "engines": { - "node": ">=12" + "node": ">= 0.4" }, "funding": { - "url": "https://github.com/sponsors/jonschlinkert" + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/jest-pnp-resolver": { - "version": "1.2.3", + "node_modules/is-set": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/is-set/-/is-set-2.0.3.tgz", + "integrity": "sha512-iPAjerrse27/ygGLxw+EBR9agv9Y6uLeYVJMu+QNCoouJ1/1ri0mGrcWpfCqFZuzzx3WjtwxG098X+n4OuRkPg==", "dev": true, "license": "MIT", "engines": { - "node": ">=6" - }, - "peerDependencies": { - "jest-resolve": "*" + "node": ">= 0.4" }, - "peerDependenciesMeta": { - "jest-resolve": { - "optional": true - } + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/jest-regex-util": { - "version": "30.0.1", + "node_modules/is-shared-array-buffer": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.4.tgz", + "integrity": "sha512-ISWac8drv4ZGfwKl5slpHG9OwPNty4jOWPRIhBpxOoD+hqITiwuipOQ2bNthAzwA3B4fIjO4Nln74N0S9byq8A==", "dev": true, "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3" + }, "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/jest-resolve": { - "version": "30.2.0", - "dev": true, + "node_modules/is-stream": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", + "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", "license": "MIT", - "dependencies": { - "chalk": "^4.1.2", - "graceful-fs": "^4.2.11", - "jest-haste-map": "30.2.0", - "jest-pnp-resolver": "^1.2.3", - "jest-util": "30.2.0", - "jest-validate": "30.2.0", - "slash": "^3.0.0", - "unrs-resolver": "^1.7.11" - }, "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/jest-resolve-dependencies": { - "version": "30.2.0", + "node_modules/is-string": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.1.1.tgz", + "integrity": "sha512-BtEeSsoaQjlSPBemMQIrY1MY0uM6vnS1g5fmufYOtnxLGUZM2178PKbhsk7Ffv58IX+ZtcvoGwccYsh0PglkAA==", "dev": true, "license": "MIT", "dependencies": { - "jest-regex-util": "30.0.1", - "jest-snapshot": "30.2.0" + "call-bound": "^1.0.3", + "has-tostringtag": "^1.0.2" }, "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/jest-resolve/node_modules/@jest/schemas": { - "version": "30.0.5", + "node_modules/is-symbol": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.1.1.tgz", + "integrity": "sha512-9gGx6GTtCQM73BgmHQXfDmLtfjjTUDSyoxTCbp5WtoixAhfgsDirWIcVQ/IHpvI5Vgd5i/J5F7B9cN/WlVbC/w==", "dev": true, "license": "MIT", "dependencies": { - "@sinclair/typebox": "^0.34.0" + "call-bound": "^1.0.2", + "has-symbols": "^1.1.0", + "safe-regex-test": "^1.1.0" }, "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/jest-resolve/node_modules/@jest/types": { - "version": "30.2.0", - "dev": true, + "node_modules/is-typed-array": { + "version": "1.1.15", + "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.15.tgz", + "integrity": "sha512-p3EcsicXjit7SaskXHs1hA91QxgTw46Fv6EFKKGS5DRFLD8yKnohjF3hxoju94b/OcMZoQukzpPpBE9uLVKzgQ==", "license": "MIT", "dependencies": { - "@jest/pattern": "30.0.1", - "@jest/schemas": "30.0.5", - "@types/istanbul-lib-coverage": "^2.0.6", - "@types/istanbul-reports": "^3.0.4", - "@types/node": "*", - "@types/yargs": "^17.0.33", - "chalk": "^4.1.2" + "which-typed-array": "^1.1.16" }, "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/jest-resolve/node_modules/@sinclair/typebox": { - "version": "0.34.41", - "dev": true, - "license": "MIT" - }, - "node_modules/jest-resolve/node_modules/jest-util": { - "version": "30.2.0", + "node_modules/is-unicode-supported": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz", + "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==", "dev": true, "license": "MIT", - "dependencies": { - "@jest/types": "30.2.0", - "@types/node": "*", - "chalk": "^4.1.2", - "ci-info": "^4.2.0", - "graceful-fs": "^4.2.11", - "picomatch": "^4.0.2" - }, "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/jest-resolve/node_modules/picomatch": { - "version": "4.0.3", + "node_modules/is-weakmap": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/is-weakmap/-/is-weakmap-2.0.2.tgz", + "integrity": "sha512-K5pXYOm9wqY1RgjpL3YTkF39tni1XajUIkawTLUo9EZEVUFga5gSQJF8nNS7ZwJQ02y+1YCNYcMh+HIf1ZqE+w==", "dev": true, "license": "MIT", "engines": { - "node": ">=12" + "node": ">= 0.4" }, "funding": { - "url": "https://github.com/sponsors/jonschlinkert" + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/jest-runner": { - "version": "30.2.0", + "node_modules/is-weakref": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.1.1.tgz", + "integrity": "sha512-6i9mGWSlqzNMEqpCp93KwRS1uUOodk2OJ6b+sq7ZPDSy2WuI5NFIxp/254TytR8ftefexkWn5xNiHUNpPOfSew==", "dev": true, "license": "MIT", "dependencies": { - "@jest/console": "30.2.0", - "@jest/environment": "30.2.0", - "@jest/test-result": "30.2.0", - "@jest/transform": "30.2.0", - "@jest/types": "30.2.0", - "@types/node": "*", - "chalk": "^4.1.2", - "emittery": "^0.13.1", - "exit-x": "^0.2.2", - "graceful-fs": "^4.2.11", - "jest-docblock": "30.2.0", - "jest-environment-node": "30.2.0", - "jest-haste-map": "30.2.0", - "jest-leak-detector": "30.2.0", - "jest-message-util": "30.2.0", - "jest-resolve": "30.2.0", - "jest-runtime": "30.2.0", - "jest-util": "30.2.0", - "jest-watcher": "30.2.0", - "jest-worker": "30.2.0", - "p-limit": "^3.1.0", - "source-map-support": "0.5.13" + "call-bound": "^1.0.3" }, "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/jest-runner/node_modules/@jest/console": { - "version": "30.2.0", + "node_modules/is-weakset": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/is-weakset/-/is-weakset-2.0.4.tgz", + "integrity": "sha512-mfcwb6IzQyOKTs84CQMrOwW4gQcaTOAWJ0zzJCl2WSPDrWk/OzDaImWFH3djXhb24g4eudZfLRozAvPGw4d9hQ==", "dev": true, "license": "MIT", "dependencies": { - "@jest/types": "30.2.0", - "@types/node": "*", - "chalk": "^4.1.2", - "jest-message-util": "30.2.0", - "jest-util": "30.2.0", - "slash": "^3.0.0" + "call-bound": "^1.0.3", + "get-intrinsic": "^1.2.6" }, "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/jest-runner/node_modules/@jest/schemas": { - "version": "30.0.5", - "dev": true, + "node_modules/is-wsl": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz", + "integrity": "sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==", "license": "MIT", "dependencies": { - "@sinclair/typebox": "^0.34.0" + "is-docker": "^2.0.0" }, "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + "node": ">=8" } }, - "node_modules/jest-runner/node_modules/@jest/test-result": { - "version": "30.2.0", + "node_modules/isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", + "license": "MIT" + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "license": "ISC" + }, + "node_modules/istanbul-lib-coverage": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.2.tgz", + "integrity": "sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg==", "dev": true, - "license": "MIT", - "dependencies": { - "@jest/console": "30.2.0", - "@jest/types": "30.2.0", - "@types/istanbul-lib-coverage": "^2.0.6", - "collect-v8-coverage": "^1.0.2" - }, + "license": "BSD-3-Clause", "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + "node": ">=8" } }, - "node_modules/jest-runner/node_modules/@jest/types": { - "version": "30.2.0", + "node_modules/istanbul-lib-instrument": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-6.0.3.tgz", + "integrity": "sha512-Vtgk7L/R2JHyyGW07spoFlB8/lpjiOLTjMdms6AFMraYt3BaJauod/NGrfnVG/y4Ix1JEuMRPDPEj2ua+zz1/Q==", "dev": true, - "license": "MIT", + "license": "BSD-3-Clause", "dependencies": { - "@jest/pattern": "30.0.1", - "@jest/schemas": "30.0.5", - "@types/istanbul-lib-coverage": "^2.0.6", - "@types/istanbul-reports": "^3.0.4", - "@types/node": "*", - "@types/yargs": "^17.0.33", - "chalk": "^4.1.2" + "@babel/core": "^7.23.9", + "@babel/parser": "^7.23.9", + "@istanbuljs/schema": "^0.1.3", + "istanbul-lib-coverage": "^3.2.0", + "semver": "^7.5.4" }, "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + "node": ">=10" } }, - "node_modules/jest-runner/node_modules/@sinclair/typebox": { - "version": "0.34.41", - "dev": true, - "license": "MIT" - }, - "node_modules/jest-runner/node_modules/ansi-styles": { - "version": "5.2.0", + "node_modules/istanbul-lib-report": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.1.tgz", + "integrity": "sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw==", "dev": true, - "license": "MIT", + "license": "BSD-3-Clause", + "dependencies": { + "istanbul-lib-coverage": "^3.0.0", + "make-dir": "^4.0.0", + "supports-color": "^7.1.0" + }, "engines": { "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "node_modules/jest-runner/node_modules/jest-message-util": { - "version": "30.2.0", + "node_modules/istanbul-lib-source-maps": { + "version": "5.0.6", + "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-5.0.6.tgz", + "integrity": "sha512-yg2d+Em4KizZC5niWhQaIomgf5WlL4vOOjZ5xGCmF8SnPE/mDWWXgvRExdcpCgh9lLRRa1/fSYp2ymmbJ1pI+A==", "dev": true, - "license": "MIT", + "license": "BSD-3-Clause", "dependencies": { - "@babel/code-frame": "^7.27.1", - "@jest/types": "30.2.0", - "@types/stack-utils": "^2.0.3", - "chalk": "^4.1.2", - "graceful-fs": "^4.2.11", - "micromatch": "^4.0.8", - "pretty-format": "30.2.0", - "slash": "^3.0.0", - "stack-utils": "^2.0.6" + "@jridgewell/trace-mapping": "^0.3.23", + "debug": "^4.1.1", + "istanbul-lib-coverage": "^3.0.0" }, "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + "node": ">=10" } }, - "node_modules/jest-runner/node_modules/jest-util": { - "version": "30.2.0", + "node_modules/istanbul-reports": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.2.0.tgz", + "integrity": "sha512-HGYWWS/ehqTV3xN10i23tkPkpH46MLCIMFNCaaKNavAXTF1RkqxawEPtnjnGZ6XKSInBKkiOA5BKS+aZiY3AvA==", "dev": true, - "license": "MIT", + "license": "BSD-3-Clause", "dependencies": { - "@jest/types": "30.2.0", - "@types/node": "*", - "chalk": "^4.1.2", - "ci-info": "^4.2.0", - "graceful-fs": "^4.2.11", - "picomatch": "^4.0.2" + "html-escaper": "^2.0.0", + "istanbul-lib-report": "^3.0.0" }, "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + "node": ">=8" } }, - "node_modules/jest-runner/node_modules/picomatch": { - "version": "4.0.3", + "node_modules/iterare": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/iterare/-/iterare-1.2.1.tgz", + "integrity": "sha512-RKYVTCjAnRthyJes037NX/IiqeidgN1xc3j1RjFfECFp28A1GVwK9nA+i0rJPaHqSZwygLzRnFlzUuHFoWWy+Q==", "dev": true, - "license": "MIT", + "license": "ISC", "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" + "node": ">=6" } }, - "node_modules/jest-runner/node_modules/pretty-format": { - "version": "30.2.0", + "node_modules/iterator.prototype": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/iterator.prototype/-/iterator.prototype-1.1.5.tgz", + "integrity": "sha512-H0dkQoCa3b2VEeKQBOxFph+JAbcrQdE7KC0UkqwpLmv2EC4P41QXP+rqo9wYodACiG5/WM5s9oDApTU8utwj9g==", "dev": true, "license": "MIT", "dependencies": { - "@jest/schemas": "30.0.5", - "ansi-styles": "^5.2.0", - "react-is": "^18.3.1" + "define-data-property": "^1.1.4", + "es-object-atoms": "^1.0.0", + "get-intrinsic": "^1.2.6", + "get-proto": "^1.0.0", + "has-symbols": "^1.1.0", + "set-function-name": "^2.0.2" }, "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + "node": ">= 0.4" } }, - "node_modules/jest-runtime": { - "version": "30.2.0", + "node_modules/jackspeak": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.3.tgz", + "integrity": "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==", "dev": true, - "license": "MIT", + "license": "BlueOak-1.0.0", "dependencies": { - "@jest/environment": "30.2.0", - "@jest/fake-timers": "30.2.0", - "@jest/globals": "30.2.0", - "@jest/source-map": "30.0.1", - "@jest/test-result": "30.2.0", - "@jest/transform": "30.2.0", - "@jest/types": "30.2.0", - "@types/node": "*", - "chalk": "^4.1.2", - "cjs-module-lexer": "^2.1.0", - "collect-v8-coverage": "^1.0.2", - "glob": "^10.3.10", - "graceful-fs": "^4.2.11", - "jest-haste-map": "30.2.0", - "jest-message-util": "30.2.0", - "jest-mock": "30.2.0", - "jest-regex-util": "30.0.1", - "jest-resolve": "30.2.0", - "jest-snapshot": "30.2.0", - "jest-util": "30.2.0", - "slash": "^3.0.0", - "strip-bom": "^4.0.0" + "@isaacs/cliui": "^8.0.2" }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + "funding": { + "url": "https://github.com/sponsors/isaacs" + }, + "optionalDependencies": { + "@pkgjs/parseargs": "^0.11.0" } }, - "node_modules/jest-runtime/node_modules/@jest/console": { + "node_modules/jest": { "version": "30.2.0", + "resolved": "https://registry.npmjs.org/jest/-/jest-30.2.0.tgz", + "integrity": "sha512-F26gjC0yWN8uAA5m5Ss8ZQf5nDHWGlN/xWZIh8S5SRbsEKBovwZhxGd6LJlbZYxBgCYOtreSUyb8hpXyGC5O4A==", "dev": true, "license": "MIT", "dependencies": { + "@jest/core": "30.2.0", "@jest/types": "30.2.0", - "@types/node": "*", - "chalk": "^4.1.2", - "jest-message-util": "30.2.0", - "jest-util": "30.2.0", - "slash": "^3.0.0" + "import-local": "^3.2.0", + "jest-cli": "30.2.0" }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/jest-runtime/node_modules/@jest/schemas": { - "version": "30.0.5", - "dev": true, - "license": "MIT", - "dependencies": { - "@sinclair/typebox": "^0.34.0" + "bin": { + "jest": "bin/jest.js" }, "engines": { "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + }, + "peerDependencies": { + "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" + }, + "peerDependenciesMeta": { + "node-notifier": { + "optional": true + } } }, - "node_modules/jest-runtime/node_modules/@jest/test-result": { + "node_modules/jest-changed-files": { "version": "30.2.0", + "resolved": "https://registry.npmjs.org/jest-changed-files/-/jest-changed-files-30.2.0.tgz", + "integrity": "sha512-L8lR1ChrRnSdfeOvTrwZMlnWV8G/LLjQ0nG9MBclwWZidA2N5FviRki0Bvh20WRMOX31/JYvzdqTJrk5oBdydQ==", "dev": true, "license": "MIT", "dependencies": { - "@jest/console": "30.2.0", - "@jest/types": "30.2.0", - "@types/istanbul-lib-coverage": "^2.0.6", - "collect-v8-coverage": "^1.0.2" + "execa": "^5.1.1", + "jest-util": "30.2.0", + "p-limit": "^3.1.0" }, "engines": { "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" } }, - "node_modules/jest-runtime/node_modules/@jest/types": { + "node_modules/jest-circus": { "version": "30.2.0", + "resolved": "https://registry.npmjs.org/jest-circus/-/jest-circus-30.2.0.tgz", + "integrity": "sha512-Fh0096NC3ZkFx05EP2OXCxJAREVxj1BcW/i6EWqqymcgYKWjyyDpral3fMxVcHXg6oZM7iULer9wGRFvfpl+Tg==", "dev": true, "license": "MIT", "dependencies": { - "@jest/pattern": "30.0.1", - "@jest/schemas": "30.0.5", - "@types/istanbul-lib-coverage": "^2.0.6", - "@types/istanbul-reports": "^3.0.4", + "@jest/environment": "30.2.0", + "@jest/expect": "30.2.0", + "@jest/test-result": "30.2.0", + "@jest/types": "30.2.0", "@types/node": "*", - "@types/yargs": "^17.0.33", - "chalk": "^4.1.2" + "chalk": "^4.1.2", + "co": "^4.6.0", + "dedent": "^1.0.0", + "is-generator-fn": "^2.0.0", + "jest-each": "^29.7.0", + "jest-matcher-utils": "^29.7.0", + "jest-message-util": "^29.7.0", + "jest-runtime": "^29.7.0", + "jest-snapshot": "^29.7.0", + "jest-util": "^29.7.0", + "p-limit": "^3.1.0", + "pretty-format": "^29.7.0", + "pure-rand": "^6.0.0", + "slash": "^3.0.0", + "stack-utils": "^2.0.3" }, "engines": { "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" } }, - "node_modules/jest-runtime/node_modules/@sinclair/typebox": { - "version": "0.34.41", - "dev": true, - "license": "MIT" - }, - "node_modules/jest-runtime/node_modules/ansi-styles": { - "version": "5.2.0", + "node_modules/jest-cli": { + "version": "30.2.0", + "resolved": "https://registry.npmjs.org/jest-cli/-/jest-cli-30.2.0.tgz", + "integrity": "sha512-Os9ukIvADX/A9sLt6Zse3+nmHtHaE6hqOsjQtNiugFTbKRHYIYtZXNGNK9NChseXy7djFPjndX1tL0sCTlfpAA==", "dev": true, "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/jest-runtime/node_modules/glob": { - "version": "10.4.5", - "dev": true, - "license": "ISC", "dependencies": { - "foreground-child": "^3.1.0", - "jackspeak": "^3.1.2", - "minimatch": "^9.0.4", - "minipass": "^7.1.2", - "package-json-from-dist": "^1.0.0", - "path-scurry": "^1.11.1" + "@jest/core": "30.2.0", + "@jest/test-result": "30.2.0", + "@jest/types": "30.2.0", + "chalk": "^4.1.2", + "exit-x": "^0.2.2", + "import-local": "^3.2.0", + "jest-config": "30.2.0", + "jest-util": "30.2.0", + "jest-validate": "30.2.0", + "yargs": "^17.7.2" }, "bin": { - "glob": "dist/esm/bin.mjs" + "jest": "bin/jest.js" }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/jest-runtime/node_modules/jackspeak": { - "version": "3.4.3", - "dev": true, - "license": "BlueOak-1.0.0", - "dependencies": { - "@isaacs/cliui": "^8.0.2" + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" }, - "funding": { - "url": "https://github.com/sponsors/isaacs" + "peerDependencies": { + "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" }, - "optionalDependencies": { - "@pkgjs/parseargs": "^0.11.0" + "peerDependenciesMeta": { + "node-notifier": { + "optional": true + } } }, - "node_modules/jest-runtime/node_modules/jest-message-util": { + "node_modules/jest-config": { "version": "30.2.0", + "resolved": "https://registry.npmjs.org/jest-config/-/jest-config-30.2.0.tgz", + "integrity": "sha512-g4WkyzFQVWHtu6uqGmQR4CQxz/CH3yDSlhzXMWzNjDx843gYjReZnMRanjRCq5XZFuQrGDxgUaiYWE8BRfVckA==", "dev": true, "license": "MIT", "dependencies": { - "@babel/code-frame": "^7.27.1", + "@babel/core": "^7.27.4", + "@jest/get-type": "30.1.0", + "@jest/pattern": "30.0.1", + "@jest/test-sequencer": "30.2.0", "@jest/types": "30.2.0", - "@types/stack-utils": "^2.0.3", + "babel-jest": "30.2.0", "chalk": "^4.1.2", + "ci-info": "^4.2.0", + "deepmerge": "^4.3.1", + "glob": "^10.3.10", "graceful-fs": "^4.2.11", + "jest-circus": "30.2.0", + "jest-docblock": "30.2.0", + "jest-environment-node": "30.2.0", + "jest-regex-util": "30.0.1", + "jest-resolve": "30.2.0", + "jest-runner": "30.2.0", + "jest-util": "30.2.0", + "jest-validate": "30.2.0", "micromatch": "^4.0.8", + "parse-json": "^5.2.0", "pretty-format": "30.2.0", "slash": "^3.0.0", - "stack-utils": "^2.0.6" + "strip-json-comments": "^3.1.1" }, "engines": { "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/jest-runtime/node_modules/jest-util": { - "version": "30.2.0", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/types": "30.2.0", + }, + "peerDependencies": { "@types/node": "*", - "chalk": "^4.1.2", - "ci-info": "^4.2.0", - "graceful-fs": "^4.2.11", - "picomatch": "^4.0.2" + "esbuild-register": ">=3.4.0", + "ts-node": ">=9.0.0" }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "esbuild-register": { + "optional": true + }, + "ts-node": { + "optional": true + } } }, - "node_modules/jest-runtime/node_modules/lru-cache": { - "version": "10.4.3", - "dev": true, - "license": "ISC" - }, - "node_modules/jest-runtime/node_modules/minimatch": { - "version": "9.0.5", + "node_modules/jest-config/node_modules/glob": { + "version": "10.5.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.5.0.tgz", + "integrity": "sha512-DfXN8DfhJ7NH3Oe7cFmu3NCu1wKbkReJ8TorzSAFbSKrlNaQSKfIzqYqVY8zlbs2NLBbWpRiU52GX2PbaBVNkg==", "dev": true, "license": "ISC", "dependencies": { - "brace-expansion": "^2.0.1" + "foreground-child": "^3.1.0", + "jackspeak": "^3.1.2", + "minimatch": "^9.0.4", + "minipass": "^7.1.2", + "package-json-from-dist": "^1.0.0", + "path-scurry": "^1.11.1" }, - "engines": { - "node": ">=16 || 14 >=14.17" + "bin": { + "glob": "dist/esm/bin.mjs" }, "funding": { "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/jest-runtime/node_modules/path-scurry": { + "node_modules/jest-config/node_modules/lru-cache": { + "version": "10.4.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", + "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/jest-config/node_modules/path-scurry": { "version": "1.11.1", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz", + "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==", "dev": true, "license": "BlueOak-1.0.0", "dependencies": { @@ -17041,148 +14500,162 @@ "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/jest-runtime/node_modules/picomatch": { - "version": "4.0.3", + "node_modules/jest-diff": { + "version": "30.2.0", + "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-30.2.0.tgz", + "integrity": "sha512-dQHFo3Pt4/NLlG5z4PxZ/3yZTZ1C7s9hveiOj+GCN+uT109NC2QgsoVZsVOAvbJ3RgKkvyLGXZV9+piDpWbm6A==", "dev": true, "license": "MIT", - "engines": { - "node": ">=12" + "dependencies": { + "@jest/diff-sequences": "30.0.1", + "@jest/get-type": "30.1.0", + "chalk": "^4.1.2", + "pretty-format": "30.2.0" }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" } }, - "node_modules/jest-runtime/node_modules/pretty-format": { + "node_modules/jest-docblock": { "version": "30.2.0", + "resolved": "https://registry.npmjs.org/jest-docblock/-/jest-docblock-30.2.0.tgz", + "integrity": "sha512-tR/FFgZKS1CXluOQzZvNH3+0z9jXr3ldGSD8bhyuxvlVUwbeLOGynkunvlTMxchC5urrKndYiwCFC0DLVjpOCA==", "dev": true, "license": "MIT", "dependencies": { - "@jest/schemas": "30.0.5", - "ansi-styles": "^5.2.0", - "react-is": "^18.3.1" + "detect-newline": "^3.1.0" }, "engines": { "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" } }, - "node_modules/jest-snapshot": { + "node_modules/jest-each": { "version": "30.2.0", + "resolved": "https://registry.npmjs.org/jest-each/-/jest-each-30.2.0.tgz", + "integrity": "sha512-lpWlJlM7bCUf1mfmuqTA8+j2lNURW9eNafOy99knBM01i5CQeY5UH1vZjgT9071nDJac1M4XsbyI44oNOdhlDQ==", "dev": true, "license": "MIT", "dependencies": { - "@babel/core": "^7.27.4", - "@babel/generator": "^7.27.5", - "@babel/plugin-syntax-jsx": "^7.27.1", - "@babel/plugin-syntax-typescript": "^7.27.1", - "@babel/types": "^7.27.3", - "@jest/expect-utils": "30.2.0", "@jest/get-type": "30.1.0", - "@jest/snapshot-utils": "30.2.0", - "@jest/transform": "30.2.0", "@jest/types": "30.2.0", - "babel-preset-current-node-syntax": "^1.2.0", "chalk": "^4.1.2", - "expect": "30.2.0", - "graceful-fs": "^4.2.11", - "jest-diff": "30.2.0", - "jest-matcher-utils": "30.2.0", - "jest-message-util": "30.2.0", "jest-util": "30.2.0", - "pretty-format": "30.2.0", - "semver": "^7.7.2", - "synckit": "^0.11.8" + "pretty-format": "30.2.0" }, "engines": { "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" } }, - "node_modules/jest-snapshot/node_modules/@jest/expect-utils": { + "node_modules/jest-environment-jsdom": { "version": "30.2.0", + "resolved": "https://registry.npmjs.org/jest-environment-jsdom/-/jest-environment-jsdom-30.2.0.tgz", + "integrity": "sha512-zbBTiqr2Vl78pKp/laGBREYzbZx9ZtqPjOK4++lL4BNDhxRnahg51HtoDrk9/VjIy9IthNEWdKVd7H5bqBhiWQ==", "dev": true, "license": "MIT", "dependencies": { - "@jest/get-type": "30.1.0" + "@jest/environment": "30.2.0", + "@jest/environment-jsdom-abstract": "30.2.0", + "@types/jsdom": "^21.1.7", + "@types/node": "*", + "jsdom": "^26.1.0" }, "engines": { "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + }, + "peerDependencies": { + "canvas": "^3.0.0" + }, + "peerDependenciesMeta": { + "canvas": { + "optional": true + } } }, - "node_modules/jest-snapshot/node_modules/@jest/schemas": { - "version": "30.0.5", + "node_modules/jest-environment-node": { + "version": "30.2.0", + "resolved": "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-30.2.0.tgz", + "integrity": "sha512-ElU8v92QJ9UrYsKrxDIKCxu6PfNj4Hdcktcn0JX12zqNdqWHB0N+hwOnnBBXvjLd2vApZtuLUGs1QSY+MsXoNA==", "dev": true, "license": "MIT", "dependencies": { - "@sinclair/typebox": "^0.34.0" + "@jest/environment": "30.2.0", + "@jest/fake-timers": "30.2.0", + "@jest/types": "30.2.0", + "@types/node": "*", + "jest-mock": "30.2.0", + "jest-util": "30.2.0", + "jest-validate": "30.2.0" }, "engines": { "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" } }, - "node_modules/jest-snapshot/node_modules/@jest/types": { + "node_modules/jest-haste-map": { "version": "30.2.0", + "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-30.2.0.tgz", + "integrity": "sha512-sQA/jCb9kNt+neM0anSj6eZhLZUIhQgwDt7cPGjumgLM4rXsfb9kpnlacmvZz3Q5tb80nS+oG/if+NBKrHC+Xw==", "dev": true, "license": "MIT", "dependencies": { - "@jest/pattern": "30.0.1", - "@jest/schemas": "30.0.5", - "@types/istanbul-lib-coverage": "^2.0.6", - "@types/istanbul-reports": "^3.0.4", + "@jest/types": "30.2.0", "@types/node": "*", - "@types/yargs": "^17.0.33", - "chalk": "^4.1.2" + "anymatch": "^3.1.3", + "fb-watchman": "^2.0.2", + "graceful-fs": "^4.2.11", + "jest-regex-util": "30.0.1", + "jest-util": "30.2.0", + "jest-worker": "30.2.0", + "micromatch": "^4.0.8", + "walker": "^1.0.8" }, "engines": { "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/jest-snapshot/node_modules/@sinclair/typebox": { - "version": "0.34.41", - "dev": true, - "license": "MIT" - }, - "node_modules/jest-snapshot/node_modules/ansi-styles": { - "version": "5.2.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10" }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" + "optionalDependencies": { + "fsevents": "^2.3.3" } }, - "node_modules/jest-snapshot/node_modules/expect": { - "version": "30.2.0", + "node_modules/jest-html-reporter": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/jest-html-reporter/-/jest-html-reporter-4.3.0.tgz", + "integrity": "sha512-lq4Zx35yc6Ehw513CXJ1ok3wUmkSiOImWcyLAmylfzrz7DAqtrhDF9V73F4qfstmGxlr8X0QrEjWsl/oqhf4sQ==", "dev": true, "license": "MIT", "dependencies": { - "@jest/expect-utils": "30.2.0", - "@jest/get-type": "30.1.0", - "jest-matcher-utils": "30.2.0", - "jest-message-util": "30.2.0", - "jest-mock": "30.2.0", - "jest-util": "30.2.0" + "@jest/reporters": "^30.0.2", + "@jest/test-result": "^30.0.2", + "@jest/types": "^30.0.1", + "dateformat": "3.0.2", + "mkdirp": "^1.0.3", + "strip-ansi": "6.0.1", + "xmlbuilder": "15.0.0" }, "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + "node": ">=14.0.0" + }, + "peerDependencies": { + "jest": "19.x - 30.x", + "typescript": "^3.7.x || ^4.3.x || ^5.x" } }, - "node_modules/jest-snapshot/node_modules/jest-diff": { + "node_modules/jest-leak-detector": { "version": "30.2.0", + "resolved": "https://registry.npmjs.org/jest-leak-detector/-/jest-leak-detector-30.2.0.tgz", + "integrity": "sha512-M6jKAjyzjHG0SrQgwhgZGy9hFazcudwCNovY/9HPIicmNSBuockPSedAP9vlPK6ONFJ1zfyH/M2/YYJxOz5cdQ==", "dev": true, "license": "MIT", "dependencies": { - "@jest/diff-sequences": "30.0.1", "@jest/get-type": "30.1.0", - "chalk": "^4.1.2", "pretty-format": "30.2.0" }, "engines": { "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" } }, - "node_modules/jest-snapshot/node_modules/jest-matcher-utils": { + "node_modules/jest-matcher-utils": { "version": "30.2.0", + "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-30.2.0.tgz", + "integrity": "sha512-dQ94Nq4dbzmUWkQ0ANAWS9tBRfqCrn0bV9AMYdOi/MHW726xn7eQmMeRTpX2ViC00bpNaWXq+7o4lIQ3AX13Hg==", "dev": true, "license": "MIT", "dependencies": { @@ -17195,8 +14668,10 @@ "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" } }, - "node_modules/jest-snapshot/node_modules/jest-message-util": { + "node_modules/jest-message-util": { "version": "30.2.0", + "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-30.2.0.tgz", + "integrity": "sha512-y4DKFLZ2y6DxTWD4cDe07RglV88ZiNEdlRfGtqahfbIjfsw1nMCPx49Uev4IA/hWn3sDKyAnSPwoYSsAEdcimw==", "dev": true, "license": "MIT", "dependencies": { @@ -17214,273 +14689,248 @@ "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" } }, - "node_modules/jest-snapshot/node_modules/jest-util": { + "node_modules/jest-mock": { "version": "30.2.0", + "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-30.2.0.tgz", + "integrity": "sha512-JNNNl2rj4b5ICpmAcq+WbLH83XswjPbjH4T7yvGzfAGCPh1rw+xVNbtk+FnRslvt9lkCcdn9i1oAoKUuFsOxRw==", "dev": true, "license": "MIT", "dependencies": { "@jest/types": "30.2.0", "@types/node": "*", - "chalk": "^4.1.2", - "ci-info": "^4.2.0", - "graceful-fs": "^4.2.11", - "picomatch": "^4.0.2" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/jest-snapshot/node_modules/picomatch": { - "version": "4.0.3", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } - }, - "node_modules/jest-snapshot/node_modules/pretty-format": { - "version": "30.2.0", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/schemas": "30.0.5", - "ansi-styles": "^5.2.0", - "react-is": "^18.3.1" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/jest-util": { - "version": "29.7.0", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/types": "^29.6.3", - "@types/node": "*", - "chalk": "^4.0.0", - "ci-info": "^3.2.0", - "graceful-fs": "^4.2.9", - "picomatch": "^2.2.3" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-util/node_modules/ci-info": { - "version": "3.9.0", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/sibiraj-s" - } - ], - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-validate": { - "version": "30.2.0", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/get-type": "30.1.0", - "@jest/types": "30.2.0", - "camelcase": "^6.3.0", - "chalk": "^4.1.2", - "leven": "^3.1.0", - "pretty-format": "30.2.0" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/jest-validate/node_modules/@jest/schemas": { - "version": "30.0.5", - "dev": true, - "license": "MIT", - "dependencies": { - "@sinclair/typebox": "^0.34.0" + "jest-util": "30.2.0" }, "engines": { "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" } }, - "node_modules/jest-validate/node_modules/@jest/types": { - "version": "30.2.0", + "node_modules/jest-mock-extended": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/jest-mock-extended/-/jest-mock-extended-4.0.0.tgz", + "integrity": "sha512-7BZpfuvLam+/HC+NxifIi9b+5VXj/utUDMPUqrDJehGWVuXPtLS9Jqlob2mJLrI/pg2k1S8DMfKDvEB88QNjaQ==", "dev": true, "license": "MIT", "dependencies": { - "@jest/pattern": "30.0.1", - "@jest/schemas": "30.0.5", - "@types/istanbul-lib-coverage": "^2.0.6", - "@types/istanbul-reports": "^3.0.4", - "@types/node": "*", - "@types/yargs": "^17.0.33", - "chalk": "^4.1.2" + "ts-essentials": "^10.0.2" }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + "peerDependencies": { + "@jest/globals": "^28.0.0 || ^29.0.0 || ^30.0.0", + "jest": "^24.0.0 || ^25.0.0 || ^26.0.0 || ^27.0.0 || ^28.0.0 || ^29.0.0 || ^30.0.0", + "typescript": "^3.0.0 || ^4.0.0 || ^5.0.0" } }, - "node_modules/jest-validate/node_modules/@sinclair/typebox": { - "version": "0.34.41", - "dev": true, - "license": "MIT" - }, - "node_modules/jest-validate/node_modules/ansi-styles": { - "version": "5.2.0", + "node_modules/jest-pnp-resolver": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/jest-pnp-resolver/-/jest-pnp-resolver-1.2.3.tgz", + "integrity": "sha512-+3NpwQEnRoIBtx4fyhblQDPgJI0H1IEIkX7ShLUjPGA7TtUTvI1oiKi3SR4oBR0hQhQR80l4WAe5RrXBwWMA8w==", "dev": true, "license": "MIT", "engines": { - "node": ">=10" + "node": ">=6" }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" + "peerDependencies": { + "jest-resolve": "*" + }, + "peerDependenciesMeta": { + "jest-resolve": { + "optional": true + } } }, - "node_modules/jest-validate/node_modules/camelcase": { - "version": "6.3.0", + "node_modules/jest-regex-util": { + "version": "30.0.1", + "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-30.0.1.tgz", + "integrity": "sha512-jHEQgBXAgc+Gh4g0p3bCevgRCVRkB4VB70zhoAE48gxeSr1hfUOsM/C2WoJgVL7Eyg//hudYENbm3Ne+/dRVVA==", "dev": true, "license": "MIT", "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" } }, - "node_modules/jest-validate/node_modules/pretty-format": { + "node_modules/jest-resolve": { "version": "30.2.0", + "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-30.2.0.tgz", + "integrity": "sha512-TCrHSxPlx3tBY3hWNtRQKbtgLhsXa1WmbJEqBlTBrGafd5fiQFByy2GNCEoGR+Tns8d15GaL9cxEzKOO3GEb2A==", "dev": true, "license": "MIT", "dependencies": { - "@jest/schemas": "30.0.5", - "ansi-styles": "^5.2.0", - "react-is": "^18.3.1" + "chalk": "^4.1.2", + "graceful-fs": "^4.2.11", + "jest-haste-map": "30.2.0", + "jest-pnp-resolver": "^1.2.3", + "jest-util": "30.2.0", + "jest-validate": "30.2.0", + "slash": "^3.0.0", + "unrs-resolver": "^1.7.11" }, "engines": { "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" } }, - "node_modules/jest-watcher": { + "node_modules/jest-resolve-dependencies": { "version": "30.2.0", + "resolved": "https://registry.npmjs.org/jest-resolve-dependencies/-/jest-resolve-dependencies-30.2.0.tgz", + "integrity": "sha512-xTOIGug/0RmIe3mmCqCT95yO0vj6JURrn1TKWlNbhiAefJRWINNPgwVkrVgt/YaerPzY3iItufd80v3lOrFJ2w==", "dev": true, "license": "MIT", "dependencies": { - "@jest/test-result": "30.2.0", - "@jest/types": "30.2.0", - "@types/node": "*", - "ansi-escapes": "^4.3.2", - "chalk": "^4.1.2", - "emittery": "^0.13.1", - "jest-util": "30.2.0", - "string-length": "^4.0.2" + "jest-regex-util": "30.0.1", + "jest-snapshot": "30.2.0" }, "engines": { "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" } }, - "node_modules/jest-watcher/node_modules/@jest/console": { + "node_modules/jest-runner": { "version": "30.2.0", + "resolved": "https://registry.npmjs.org/jest-runner/-/jest-runner-30.2.0.tgz", + "integrity": "sha512-PqvZ2B2XEyPEbclp+gV6KO/F1FIFSbIwewRgmROCMBo/aZ6J1w8Qypoj2pEOcg3G2HzLlaP6VUtvwCI8dM3oqQ==", "dev": true, "license": "MIT", "dependencies": { + "@jest/console": "30.2.0", + "@jest/environment": "30.2.0", + "@jest/test-result": "30.2.0", + "@jest/transform": "30.2.0", "@jest/types": "30.2.0", "@types/node": "*", "chalk": "^4.1.2", + "emittery": "^0.13.1", + "exit-x": "^0.2.2", + "graceful-fs": "^4.2.11", + "jest-docblock": "30.2.0", + "jest-environment-node": "30.2.0", + "jest-haste-map": "30.2.0", + "jest-leak-detector": "30.2.0", "jest-message-util": "30.2.0", + "jest-resolve": "30.2.0", + "jest-runtime": "30.2.0", "jest-util": "30.2.0", - "slash": "^3.0.0" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/jest-watcher/node_modules/@jest/schemas": { - "version": "30.0.5", - "dev": true, - "license": "MIT", - "dependencies": { - "@sinclair/typebox": "^0.34.0" + "jest-watcher": "30.2.0", + "jest-worker": "30.2.0", + "p-limit": "^3.1.0", + "source-map-support": "0.5.13" }, "engines": { "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" } }, - "node_modules/jest-watcher/node_modules/@jest/test-result": { + "node_modules/jest-runtime": { "version": "30.2.0", + "resolved": "https://registry.npmjs.org/jest-runtime/-/jest-runtime-30.2.0.tgz", + "integrity": "sha512-p1+GVX/PJqTucvsmERPMgCPvQJpFt4hFbM+VN3n8TMo47decMUcJbt+rgzwrEme0MQUA/R+1de2axftTHkKckg==", "dev": true, "license": "MIT", "dependencies": { - "@jest/console": "30.2.0", + "@jest/environment": "30.2.0", + "@jest/fake-timers": "30.2.0", + "@jest/globals": "30.2.0", + "@jest/source-map": "30.0.1", + "@jest/test-result": "30.2.0", + "@jest/transform": "30.2.0", "@jest/types": "30.2.0", - "@types/istanbul-lib-coverage": "^2.0.6", - "collect-v8-coverage": "^1.0.2" + "@types/node": "*", + "chalk": "^4.1.2", + "cjs-module-lexer": "^2.1.0", + "collect-v8-coverage": "^1.0.2", + "glob": "^10.3.10", + "graceful-fs": "^4.2.11", + "jest-haste-map": "30.2.0", + "jest-message-util": "30.2.0", + "jest-mock": "30.2.0", + "jest-regex-util": "30.0.1", + "jest-resolve": "30.2.0", + "jest-snapshot": "30.2.0", + "jest-util": "30.2.0", + "slash": "^3.0.0", + "strip-bom": "^4.0.0" }, "engines": { "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" } }, - "node_modules/jest-watcher/node_modules/@jest/types": { - "version": "30.2.0", + "node_modules/jest-runtime/node_modules/glob": { + "version": "10.5.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.5.0.tgz", + "integrity": "sha512-DfXN8DfhJ7NH3Oe7cFmu3NCu1wKbkReJ8TorzSAFbSKrlNaQSKfIzqYqVY8zlbs2NLBbWpRiU52GX2PbaBVNkg==", "dev": true, - "license": "MIT", + "license": "ISC", "dependencies": { - "@jest/pattern": "30.0.1", - "@jest/schemas": "30.0.5", - "@types/istanbul-lib-coverage": "^2.0.6", - "@types/istanbul-reports": "^3.0.4", - "@types/node": "*", - "@types/yargs": "^17.0.33", - "chalk": "^4.1.2" + "foreground-child": "^3.1.0", + "jackspeak": "^3.1.2", + "minimatch": "^9.0.4", + "minipass": "^7.1.2", + "package-json-from-dist": "^1.0.0", + "path-scurry": "^1.11.1" }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + "bin": { + "glob": "dist/esm/bin.mjs" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/jest-watcher/node_modules/@sinclair/typebox": { - "version": "0.34.41", + "node_modules/jest-runtime/node_modules/lru-cache": { + "version": "10.4.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", + "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", "dev": true, - "license": "MIT" + "license": "ISC" }, - "node_modules/jest-watcher/node_modules/ansi-styles": { - "version": "5.2.0", + "node_modules/jest-runtime/node_modules/path-scurry": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz", + "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==", "dev": true, - "license": "MIT", + "license": "BlueOak-1.0.0", + "dependencies": { + "lru-cache": "^10.2.0", + "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" + }, "engines": { - "node": ">=10" + "node": ">=16 || 14 >=14.18" }, "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" + "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/jest-watcher/node_modules/jest-message-util": { + "node_modules/jest-snapshot": { "version": "30.2.0", + "resolved": "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-30.2.0.tgz", + "integrity": "sha512-5WEtTy2jXPFypadKNpbNkZ72puZCa6UjSr/7djeecHWOu7iYhSXSnHScT8wBz3Rn8Ena5d5RYRcsyKIeqG1IyA==", "dev": true, "license": "MIT", "dependencies": { - "@babel/code-frame": "^7.27.1", + "@babel/core": "^7.27.4", + "@babel/generator": "^7.27.5", + "@babel/plugin-syntax-jsx": "^7.27.1", + "@babel/plugin-syntax-typescript": "^7.27.1", + "@babel/types": "^7.27.3", + "@jest/expect-utils": "30.2.0", + "@jest/get-type": "30.1.0", + "@jest/snapshot-utils": "30.2.0", + "@jest/transform": "30.2.0", "@jest/types": "30.2.0", - "@types/stack-utils": "^2.0.3", + "babel-preset-current-node-syntax": "^1.2.0", "chalk": "^4.1.2", + "expect": "30.2.0", "graceful-fs": "^4.2.11", - "micromatch": "^4.0.8", + "jest-diff": "30.2.0", + "jest-matcher-utils": "30.2.0", + "jest-message-util": "30.2.0", + "jest-util": "30.2.0", "pretty-format": "30.2.0", - "slash": "^3.0.0", - "stack-utils": "^2.0.6" + "semver": "^7.7.2", + "synckit": "^0.11.8" }, "engines": { "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" } }, - "node_modules/jest-watcher/node_modules/jest-util": { + "node_modules/jest-util": { "version": "30.2.0", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-30.2.0.tgz", + "integrity": "sha512-QKNsM0o3Xe6ISQU869e+DhG+4CK/48aHYdJZGlFQVTjnbvgpcKyxpzk29fGiO7i/J8VENZ+d2iGnSsvmuHywlA==", "dev": true, "license": "MIT", "dependencies": { @@ -17495,107 +14945,78 @@ "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" } }, - "node_modules/jest-watcher/node_modules/picomatch": { - "version": "4.0.3", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } - }, - "node_modules/jest-watcher/node_modules/pretty-format": { + "node_modules/jest-validate": { "version": "30.2.0", + "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-30.2.0.tgz", + "integrity": "sha512-FBGWi7dP2hpdi8nBoWxSsLvBFewKAg0+uSQwBaof4Y4DPgBabXgpSYC5/lR7VmnIlSpASmCi/ntRWPbv7089Pw==", "dev": true, "license": "MIT", "dependencies": { - "@jest/schemas": "30.0.5", - "ansi-styles": "^5.2.0", - "react-is": "^18.3.1" + "@jest/get-type": "30.1.0", + "@jest/types": "30.2.0", + "camelcase": "^6.3.0", + "chalk": "^4.1.2", + "leven": "^3.1.0", + "pretty-format": "30.2.0" }, "engines": { "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" } }, - "node_modules/jest-worker": { - "version": "30.2.0", + "node_modules/jest-validate/node_modules/camelcase": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", + "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", "dev": true, "license": "MIT", - "dependencies": { - "@types/node": "*", - "@ungap/structured-clone": "^1.3.0", - "jest-util": "30.2.0", - "merge-stream": "^2.0.0", - "supports-color": "^8.1.1" - }, "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/jest-worker/node_modules/@jest/schemas": { - "version": "30.0.5", - "dev": true, - "license": "MIT", - "dependencies": { - "@sinclair/typebox": "^0.34.0" + "node": ">=10" }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/jest-worker/node_modules/@jest/types": { + "node_modules/jest-watcher": { "version": "30.2.0", + "resolved": "https://registry.npmjs.org/jest-watcher/-/jest-watcher-30.2.0.tgz", + "integrity": "sha512-PYxa28dxJ9g777pGm/7PrbnMeA0Jr7osHP9bS7eJy9DuAjMgdGtxgf0uKMyoIsTWAkIbUW5hSDdJ3urmgXBqxg==", "dev": true, "license": "MIT", "dependencies": { - "@jest/pattern": "30.0.1", - "@jest/schemas": "30.0.5", - "@types/istanbul-lib-coverage": "^2.0.6", - "@types/istanbul-reports": "^3.0.4", + "@jest/test-result": "30.2.0", + "@jest/types": "30.2.0", "@types/node": "*", - "@types/yargs": "^17.0.33", - "chalk": "^4.1.2" + "ansi-escapes": "^4.3.2", + "chalk": "^4.1.2", + "emittery": "^0.13.1", + "jest-util": "30.2.0", + "string-length": "^4.0.2" }, "engines": { "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" } }, - "node_modules/jest-worker/node_modules/@sinclair/typebox": { - "version": "0.34.41", - "dev": true, - "license": "MIT" - }, - "node_modules/jest-worker/node_modules/jest-util": { + "node_modules/jest-worker": { "version": "30.2.0", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-30.2.0.tgz", + "integrity": "sha512-0Q4Uk8WF7BUwqXHuAjc23vmopWJw5WH7w2tqBoUOZpOjW/ZnR44GXXd1r82RvnmI2GZge3ivrYXk/BE2+VtW2g==", "dev": true, "license": "MIT", "dependencies": { - "@jest/types": "30.2.0", "@types/node": "*", - "chalk": "^4.1.2", - "ci-info": "^4.2.0", - "graceful-fs": "^4.2.11", - "picomatch": "^4.0.2" + "@ungap/structured-clone": "^1.3.0", + "jest-util": "30.2.0", + "merge-stream": "^2.0.0", + "supports-color": "^8.1.1" }, "engines": { "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" } }, - "node_modules/jest-worker/node_modules/picomatch": { - "version": "4.0.3", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } - }, "node_modules/jest-worker/node_modules/supports-color": { "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", "dev": true, "license": "MIT", "dependencies": { @@ -17608,41 +15029,34 @@ "url": "https://github.com/chalk/supports-color?sponsor=1" } }, - "node_modules/jest/node_modules/@jest/schemas": { - "version": "30.0.5", - "dev": true, - "license": "MIT", - "dependencies": { - "@sinclair/typebox": "^0.34.0" - }, + "node_modules/jmespath": { + "version": "0.16.0", + "resolved": "https://registry.npmjs.org/jmespath/-/jmespath-0.16.0.tgz", + "integrity": "sha512-9FzQjJ7MATs1tSpnco1K6ayiYE3figslrXA72G2HQ/n76RzvYlofyi5QM+iX4YRs/pu3yzxlVQSST23+dMDknw==", + "license": "Apache-2.0", "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + "node": ">= 0.6.0" } }, - "node_modules/jest/node_modules/@jest/types": { - "version": "30.2.0", - "dev": true, + "node_modules/joycon": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/joycon/-/joycon-3.1.1.tgz", + "integrity": "sha512-34wB/Y7MW7bzjKRjUKTa46I2Z7eV62Rkhva+KkopW7Qvv/OSWBqvkSY7vusOPrNuZcUG3tApvdVgNB8POj3SPw==", "license": "MIT", - "dependencies": { - "@jest/pattern": "30.0.1", - "@jest/schemas": "30.0.5", - "@types/istanbul-lib-coverage": "^2.0.6", - "@types/istanbul-reports": "^3.0.4", - "@types/node": "*", - "@types/yargs": "^17.0.33", - "chalk": "^4.1.2" - }, "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + "node": ">=10" } }, - "node_modules/jest/node_modules/@sinclair/typebox": { - "version": "0.34.41", - "dev": true, - "license": "MIT" + "node_modules/js-base64": { + "version": "3.7.8", + "resolved": "https://registry.npmjs.org/js-base64/-/js-base64-3.7.8.tgz", + "integrity": "sha512-hNngCeKxIUQiEUN3GPJOkz4wF/YvdUdbNL9hsBcMQTkKzboD7T/q3OYOuuPZLUE6dBxSGpwhk5mwuDud7JVAow==", + "license": "BSD-3-Clause" }, "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": { @@ -17651,11 +15065,15 @@ }, "node_modules/js-tokens": { "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", "dev": true, "license": "MIT" }, "node_modules/js-yaml": { - "version": "4.1.0", + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.1.tgz", + "integrity": "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==", "license": "MIT", "dependencies": { "argparse": "^2.0.1" @@ -17666,6 +15084,8 @@ }, "node_modules/jsdom": { "version": "26.1.0", + "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-26.1.0.tgz", + "integrity": "sha512-Cvc9WUhxSMEo4McES3P7oK3QaXldCfNWp7pl2NNeiIFlCoLr3kfq9kb1fxftiwk1FLV7CvpvDfonxtzUDeSOPg==", "dev": true, "license": "MIT", "dependencies": { @@ -17704,6 +15124,8 @@ }, "node_modules/jsep": { "version": "1.4.0", + "resolved": "https://registry.npmjs.org/jsep/-/jsep-1.4.0.tgz", + "integrity": "sha512-B7qPcEVE3NVkmSJbaYxvv4cHkVW7DQsZz13pUMrfS8z8Q/BuShN+gcTXrUlPiGqM2/t/EEaI030bpxMqY8gMlw==", "dev": true, "license": "MIT", "engines": { @@ -17712,6 +15134,8 @@ }, "node_modules/jsesc": { "version": "3.1.0", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz", + "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==", "dev": true, "license": "MIT", "bin": { @@ -17723,33 +15147,60 @@ }, "node_modules/json-buffer": { "version": "3.0.1", + "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", + "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", "dev": true, "license": "MIT" }, "node_modules/json-parse-even-better-errors": { "version": "2.3.1", + "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", + "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", "dev": true, "license": "MIT" }, "node_modules/json-pointer": { "version": "0.6.2", + "resolved": "https://registry.npmjs.org/json-pointer/-/json-pointer-0.6.2.tgz", + "integrity": "sha512-vLWcKbOaXlO+jvRy4qNd+TI1QUPZzfJj1tpJ3vAXDych5XJf93ftpUKe5pKCrzyIIwgBJcOcCVRUfqQP25afBw==", "dev": true, "license": "MIT", "dependencies": { "foreach": "^2.0.4" } }, + "node_modules/json-schema-to-ts": { + "version": "2.7.2", + "resolved": "https://registry.npmjs.org/json-schema-to-ts/-/json-schema-to-ts-2.7.2.tgz", + "integrity": "sha512-R1JfqKqbBR4qE8UyBR56Ms30LL62/nlhoz+1UkfI/VE7p54Awu919FZ6ZUPG8zIa3XB65usPJgr1ONVncUGSaQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.18.3", + "@types/json-schema": "^7.0.9", + "ts-algebra": "^1.2.0" + }, + "engines": { + "node": ">=16" + } + }, "node_modules/json-schema-traverse": { "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", "license": "MIT" }, "node_modules/json-stable-stringify-without-jsonify": { "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", + "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", "dev": true, "license": "MIT" }, "node_modules/json5": { "version": "2.2.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", "dev": true, "license": "MIT", "bin": { @@ -17761,11 +15212,15 @@ }, "node_modules/jsonc-parser": { "version": "3.3.1", + "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.3.1.tgz", + "integrity": "sha512-HUgH65KyejrUFPvHFPbqOY0rsFip3Bo5wb4ngvdi1EpCYWUQDC5V+Y7mZws+DLkr4M//zQJoanu1SP+87Dv1oQ==", "dev": true, "license": "MIT" }, "node_modules/jsonfile": { "version": "6.2.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.2.0.tgz", + "integrity": "sha512-FGuPw30AdOIUTRMC2OMRtQV+jkVj2cfPqSeWXv1NEAJ1qZ5zb1X6z1mFhbfOB/iy3ssJCD+3KuZ8r8C3uVFlAg==", "dev": true, "license": "MIT", "dependencies": { @@ -17777,6 +15232,8 @@ }, "node_modules/jsonpath-plus": { "version": "10.3.0", + "resolved": "https://registry.npmjs.org/jsonpath-plus/-/jsonpath-plus-10.3.0.tgz", + "integrity": "sha512-8TNmfeTCk2Le33A3vRRwtuworG/L5RrgMvdjhKZxvyShO+mBu2fP50OWUjRLNtvw344DdDarFh9buFAZs5ujeA==", "dev": true, "license": "MIT", "dependencies": { @@ -17792,8 +15249,20 @@ "node": ">=18.0.0" } }, + "node_modules/jsonpath-rfc9535": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/jsonpath-rfc9535/-/jsonpath-rfc9535-1.3.0.tgz", + "integrity": "sha512-3jFHya7oZ45aDxIIdx+/zQARahHXxFSMWBkcBUldfXpLS9VCXDJyTKt35kQfEXLqh0K3Ixw/9xFnvcDStaxh7Q==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=20" + } + }, "node_modules/jsonpointer": { "version": "5.0.1", + "resolved": "https://registry.npmjs.org/jsonpointer/-/jsonpointer-5.0.1.tgz", + "integrity": "sha512-p/nXbhSEcu3pZRdkW1OfJhpsVtW1gd4Wa1fnQc9YLiTfAjn0312eMKimbdIQzuZl9aa9xUGaRlP9T/CJE/ditQ==", "dev": true, "license": "MIT", "engines": { @@ -17802,6 +15271,8 @@ }, "node_modules/jsx-ast-utils": { "version": "3.3.5", + "resolved": "https://registry.npmjs.org/jsx-ast-utils/-/jsx-ast-utils-3.3.5.tgz", + "integrity": "sha512-ZZow9HBI5O6EPgSJLUb8n2NKgmVWTwCvHGwFuJlMjvLFqlGG6pjirPhtdsseaLZjSibD8eegzmYpUZwoIlj2cQ==", "dev": true, "license": "MIT", "dependencies": { @@ -17814,29 +15285,37 @@ "node": ">=4.0" } }, - "node_modules/keyv": { - "version": "4.5.4", + "node_modules/jsx-ast-utils-x": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/jsx-ast-utils-x/-/jsx-ast-utils-x-0.1.0.tgz", + "integrity": "sha512-eQQBjBnsVtGacsG9uJNB8qOr3yA8rga4wAaGG1qRcBzSIvfhERLrWxMAM1hp5fcS6Abo8M4+bUBTekYR0qTPQw==", "dev": true, "license": "MIT", - "dependencies": { - "json-buffer": "3.0.1" + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" } }, - "node_modules/kleur": { - "version": "3.0.3", + "node_modules/keyv": { + "version": "4.5.4", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", + "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", "dev": true, "license": "MIT", - "engines": { - "node": ">=6" + "dependencies": { + "json-buffer": "3.0.1" } }, "node_modules/language-subtag-registry": { "version": "0.3.23", + "resolved": "https://registry.npmjs.org/language-subtag-registry/-/language-subtag-registry-0.3.23.tgz", + "integrity": "sha512-0K65Lea881pHotoGEa5gDlMxt3pctLi2RplBb7Ezh4rRdLEOtgi7n4EwK9lamnUCkKBqaeKRVebTq6BAxSkpXQ==", "dev": true, "license": "CC0-1.0" }, "node_modules/language-tags": { "version": "1.0.9", + "resolved": "https://registry.npmjs.org/language-tags/-/language-tags-1.0.9.tgz", + "integrity": "sha512-MbjN408fEndfiQXbFQ1vnd+1NoLDsnQW41410oQBXiyXDMYH5z505juWa4KUE1LqxRC7DgOgZDbKLxHIwm27hA==", "dev": true, "license": "MIT", "dependencies": { @@ -17848,6 +15327,8 @@ }, "node_modules/lazystream": { "version": "1.0.1", + "resolved": "https://registry.npmjs.org/lazystream/-/lazystream-1.0.1.tgz", + "integrity": "sha512-b94GiNHQNy6JNTrt5w6zNyffMrNkXZb3KTkCZJb2V1xaEGCk093vkZ2jk3tpaeP33/OiXC+WvK9AxUebnf5nbw==", "dev": true, "license": "MIT", "dependencies": { @@ -17857,13 +15338,10 @@ "node": ">= 0.6.3" } }, - "node_modules/lazystream/node_modules/isarray": { - "version": "1.0.0", - "dev": true, - "license": "MIT" - }, "node_modules/lazystream/node_modules/readable-stream": { "version": "2.3.8", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", + "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", "dev": true, "license": "MIT", "dependencies": { @@ -17876,8 +15354,17 @@ "util-deprecate": "~1.0.1" } }, + "node_modules/lazystream/node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "dev": true, + "license": "MIT" + }, "node_modules/lazystream/node_modules/string_decoder": { "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", "dev": true, "license": "MIT", "dependencies": { @@ -17886,6 +15373,8 @@ }, "node_modules/lcov-result-merger": { "version": "5.0.1", + "resolved": "https://registry.npmjs.org/lcov-result-merger/-/lcov-result-merger-5.0.1.tgz", + "integrity": "sha512-i53RjTYfqbHgerqGtuJjDfARDU340zNxXrJudQZU3o8ak9rrx8FDQUKf38Cjm6MtbqonqiDFmoKuUe++uZbvOg==", "dev": true, "license": "MIT", "dependencies": { @@ -17899,8 +15388,40 @@ "node": ">=14" } }, + "node_modules/lcov-result-merger/node_modules/cliui": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", + "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^7.0.0" + } + }, + "node_modules/lcov-result-merger/node_modules/wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, "node_modules/lcov-result-merger/node_modules/yargs": { "version": "16.2.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", + "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", "dev": true, "license": "MIT", "dependencies": { @@ -17918,6 +15439,8 @@ }, "node_modules/lcov-result-merger/node_modules/yargs-parser": { "version": "20.2.9", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz", + "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==", "dev": true, "license": "ISC", "engines": { @@ -17926,6 +15449,8 @@ }, "node_modules/leven": { "version": "3.1.0", + "resolved": "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz", + "integrity": "sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==", "dev": true, "license": "MIT", "engines": { @@ -17934,6 +15459,8 @@ }, "node_modules/levn": { "version": "0.4.1", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", + "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", "dev": true, "license": "MIT", "dependencies": { @@ -17946,11 +15473,15 @@ }, "node_modules/lines-and-columns": { "version": "1.2.4", + "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", + "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", "dev": true, "license": "MIT" }, "node_modules/load-esm": { - "version": "1.0.2", + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/load-esm/-/load-esm-1.0.3.tgz", + "integrity": "sha512-v5xlu8eHD1+6r8EHTg6hfmO97LN8ugKtiXcy5e6oN72iD2r6u0RPfLl6fxM+7Wnh2ZRq15o0russMst44WauPA==", "dev": true, "funding": [ { @@ -17969,6 +15500,8 @@ }, "node_modules/locate-path": { "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", "dev": true, "license": "MIT", "dependencies": { @@ -17983,26 +15516,42 @@ }, "node_modules/lodash": { "version": "4.17.21", - "dev": true, + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", "license": "MIT" }, "node_modules/lodash.camelcase": { "version": "4.3.0", + "resolved": "https://registry.npmjs.org/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz", + "integrity": "sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA==", "dev": true, "license": "MIT" }, "node_modules/lodash.memoize": { "version": "4.1.2", + "resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz", + "integrity": "sha512-t7j+NzmgnQzTAYXcsHYLgimltOV1MXHtlOWf6GjL9Kj8GK5FInw5JotxvbOs+IvV1/Dzo04/fCGfLVs7aXb4Ag==", "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", + "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/lodash.topath": { + "version": "4.5.2", + "resolved": "https://registry.npmjs.org/lodash.topath/-/lodash.topath-4.5.2.tgz", + "integrity": "sha512-1/W4dM+35DwvE/iEd1M9ekewOSTlpFekhw9mhAtrwjVqUr83/ilQiyAvmg4tVX7Unkcfl1KC+i9WdaT4B6aQcg==", "dev": true, "license": "MIT" }, "node_modules/log-symbols": { "version": "4.1.0", + "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", + "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==", "dev": true, "license": "MIT", "dependencies": { @@ -18018,11 +15567,15 @@ }, "node_modules/long": { "version": "5.3.2", + "resolved": "https://registry.npmjs.org/long/-/long-5.3.2.tgz", + "integrity": "sha512-mNAgZ1GmyNhD7AuqnTG3/VQ26o760+ZYBPKjPvugO8+nLbYfX6TVpJPseBvopbdY+qpZ/lKUnmEc1LeZYS3QAA==", "dev": true, "license": "Apache-2.0" }, "node_modules/loose-envify": { "version": "1.4.0", + "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", + "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", "dev": true, "license": "MIT", "dependencies": { @@ -18034,6 +15587,8 @@ }, "node_modules/lru-cache": { "version": "5.1.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", + "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", "dev": true, "license": "ISC", "dependencies": { @@ -18042,11 +15597,25 @@ }, "node_modules/lunr": { "version": "2.3.9", + "resolved": "https://registry.npmjs.org/lunr/-/lunr-2.3.9.tgz", + "integrity": "sha512-zTU3DaZaF3Rt9rhN3uBMGQD3dD2/vFQqnvZCDv4dl5iOzq2IZQqTxu90r4E5J+nP70J3ilqVCrbho2eWaeW8Ow==", "dev": true, "license": "MIT" }, + "node_modules/magic-string": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.25.9.tgz", + "integrity": "sha512-RmF0AsMzgt25qzqqLc1+MbHmhdx0ojF2Fvs4XnOqz2ZOBXzzkEwc/dJQZCYHAn7v1jbVOjAZfK8msRn4BxO4VQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "sourcemap-codec": "^1.4.8" + } + }, "node_modules/make-dir": { "version": "4.0.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-4.0.0.tgz", + "integrity": "sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==", "dev": true, "license": "MIT", "dependencies": { @@ -18061,11 +15630,15 @@ }, "node_modules/make-error": { "version": "1.3.6", + "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", + "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", "dev": true, "license": "ISC" }, "node_modules/makeerror": { "version": "1.0.12", + "resolved": "https://registry.npmjs.org/makeerror/-/makeerror-1.0.12.tgz", + "integrity": "sha512-JmqCvUhmt43madlpFzG4BQzG2Z3m6tvQDNKdClZnO3VbIudJYmxsT0FNJMeiB2+JTSlTQTSbU8QdesVmwJcmLg==", "dev": true, "license": "BSD-3-Clause", "dependencies": { @@ -18074,11 +15647,22 @@ }, "node_modules/mark.js": { "version": "8.11.1", + "resolved": "https://registry.npmjs.org/mark.js/-/mark.js-8.11.1.tgz", + "integrity": "sha512-1I+1qpDt4idfgLQG+BNWmrqku+7/2bi5nLf4YwF8y8zXvmfiTBY3PV3ZibfrjBueCByROpuBjLLFCajqkgYoLQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/markdown-escape": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/markdown-escape/-/markdown-escape-2.0.0.tgz", + "integrity": "sha512-Trz4v0+XWlwy68LJIyw3bLbsJiC8XAbRCKF9DbEtZjyndKOGVx6n+wNB0VfoRmY2LKboQLeniap3xrb6LGSJ8A==", "dev": true, "license": "MIT" }, "node_modules/marked": { "version": "4.3.0", + "resolved": "https://registry.npmjs.org/marked/-/marked-4.3.0.tgz", + "integrity": "sha512-PRsaiG84bK+AMvxziE/lCFss8juXjNaWzVbN5tXAm4XjeaS9NAHhop+PjQxz2A9h8Q4M/xGmzP8vqNwy6JeK0A==", "dev": true, "license": "MIT", "bin": { @@ -18090,18 +15674,55 @@ }, "node_modules/math-intrinsics": { "version": "1.1.0", - "dev": true, + "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", + "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", "license": "MIT", "engines": { "node": ">= 0.4" } }, + "node_modules/md5": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/md5/-/md5-2.3.0.tgz", + "integrity": "sha512-T1GITYmFaKuO91vxyoQMFETst+O71VUPEU3ze5GNzDm0OWdP8v1ziTaAEPUr/3kLsY3Sftgz242A1SetQiDL7g==", + "license": "BSD-3-Clause", + "dependencies": { + "charenc": "0.0.2", + "crypt": "0.0.2", + "is-buffer": "~1.1.6" + } + }, + "node_modules/media-typer": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-1.1.0.tgz", + "integrity": "sha512-aisnrDP4GNe06UcKFnV5bfMNPBUw4jsLGaWwWfnH3v02GnBuXX2MCVn5RbrWo0j3pczUilYblq7fQ7Nw2t5XKw==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/merge-descriptors": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-2.0.0.tgz", + "integrity": "sha512-Snk314V5ayFLhp3fkUREub6WtjBfPdCPY1Ln8/8munuLuiYhsABgBVWsozAG+MWMbVEvcdcpbi9R7ww22l9Q3g==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/merge-stream": { "version": "2.0.0", + "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", + "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", "license": "MIT" }, "node_modules/merge2": { "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", "dev": true, "license": "MIT", "engines": { @@ -18110,6 +15731,8 @@ }, "node_modules/micromatch": { "version": "4.0.8", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", + "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", "dev": true, "license": "MIT", "dependencies": { @@ -18120,44 +15743,73 @@ "node": ">=8.6" } }, + "node_modules/micromatch/node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, "node_modules/mime-db": { - "version": "1.52.0", + "version": "1.54.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.54.0.tgz", + "integrity": "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==", "license": "MIT", "engines": { "node": ">= 0.6" } }, "node_modules/mime-types": { - "version": "2.1.35", - "dev": true, + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-3.0.2.tgz", + "integrity": "sha512-Lbgzdk0h4juoQ9fCKXW4by0UJqj+nOOrI9MJ1sSj4nI8aI2eo1qmvQEie4VD1glsS250n15LsWsYtCugiStS5A==", "license": "MIT", "dependencies": { - "mime-db": "1.52.0" + "mime-db": "^1.54.0" }, "engines": { - "node": ">= 0.6" + "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" } }, "node_modules/mimic-fn": { "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", + "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", "license": "MIT", "engines": { "node": ">=6" } }, "node_modules/minimatch": { - "version": "5.1.6", + "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": ">=10" + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, "node_modules/minimist": { "version": "1.2.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", + "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", "license": "MIT", "funding": { "url": "https://github.com/sponsors/ljharb" @@ -18165,6 +15817,8 @@ }, "node_modules/minipass": { "version": "7.1.2", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", + "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", "dev": true, "license": "ISC", "engines": { @@ -18173,6 +15827,8 @@ }, "node_modules/mkdirp": { "version": "1.0.4", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", + "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", "dev": true, "license": "MIT", "bin": { @@ -18184,18 +15840,24 @@ }, "node_modules/mkdirp-classic": { "version": "0.5.3", + "resolved": "https://registry.npmjs.org/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz", + "integrity": "sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==", "dev": true, "license": "MIT" }, "node_modules/mnemonist": { "version": "0.38.3", + "resolved": "https://registry.npmjs.org/mnemonist/-/mnemonist-0.38.3.tgz", + "integrity": "sha512-2K9QYubXx/NAjv4VLq1d1Ly8pWNC5L3BrixtdkyTegXWJIqY+zLNDhhX/A+ZwWt70tB1S8H4BE8FLYEFyNoOBw==", "license": "MIT", "dependencies": { "obliterator": "^1.6.1" } }, "node_modules/mobx": { - "version": "6.13.7", + "version": "6.15.0", + "resolved": "https://registry.npmjs.org/mobx/-/mobx-6.15.0.tgz", + "integrity": "sha512-UczzB+0nnwGotYSgllfARAqWCJ5e/skuV2K/l+Zyck/H6pJIhLXuBnz+6vn2i211o7DtbE78HQtsYEKICHGI+g==", "dev": true, "license": "MIT", "funding": { @@ -18205,6 +15867,8 @@ }, "node_modules/mobx-react": { "version": "9.2.0", + "resolved": "https://registry.npmjs.org/mobx-react/-/mobx-react-9.2.0.tgz", + "integrity": "sha512-dkGWCx+S0/1mfiuFfHRH8D9cplmwhxOV5CkXMp38u6rQGG2Pv3FWYztS0M7ncR6TyPRQKaTG/pnitInoYE9Vrw==", "dev": true, "license": "MIT", "dependencies": { @@ -18228,7 +15892,9 @@ } }, "node_modules/mobx-react-lite": { - "version": "4.1.0", + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/mobx-react-lite/-/mobx-react-lite-4.1.1.tgz", + "integrity": "sha512-iUxiMpsvNraCKXU+yPotsOncNNmyeS2B5DKL+TL6Tar/xm+wwNJAubJmtRSeAoYawdZqwv8Z/+5nPRHeQxTiXg==", "dev": true, "license": "MIT", "dependencies": { @@ -18253,16 +15919,29 @@ }, "node_modules/ms": { "version": "2.1.3", - "dev": true, + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", "license": "MIT" }, "node_modules/mute-stream": { "version": "0.0.8", + "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.8.tgz", + "integrity": "sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA==", "dev": true, "license": "ISC" }, + "node_modules/nan": { + "version": "2.24.0", + "resolved": "https://registry.npmjs.org/nan/-/nan-2.24.0.tgz", + "integrity": "sha512-Vpf9qnVW1RaDkoNKFUvfxqAbtI8ncb8OJlqZ9wwpXzWPEsvsB1nvdUi6oYrHIkQ1Y/tMDnr1h4nczS0VB9Xykg==", + "dev": true, + "license": "MIT", + "optional": true + }, "node_modules/nanoid": { "version": "3.3.11", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz", + "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==", "dev": true, "funding": [ { @@ -18279,7 +15958,9 @@ } }, "node_modules/napi-postinstall": { - "version": "0.3.3", + "version": "0.3.4", + "resolved": "https://registry.npmjs.org/napi-postinstall/-/napi-postinstall-0.3.4.tgz", + "integrity": "sha512-PHI5f1O0EP5xJ9gQmFGMS6IZcrVvTjpXjz7Na41gTE7eE2hK11lg04CECCYEEjdc17EV4DO+fkGEtt7TpTaTiQ==", "dev": true, "license": "MIT", "bin": { @@ -18294,16 +15975,22 @@ }, "node_modules/natural-compare": { "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", "dev": true, "license": "MIT" }, "node_modules/natural-compare-lite": { "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare-lite/-/natural-compare-lite-1.4.0.tgz", + "integrity": "sha512-Tj+HTDSJJKaZnfiuw+iaF9skdPpTo2GtEly5JHnWV/hfv2Qj/9RKsGISQtLh2ox3l5EAGw487hnBee0sIJ6v2g==", "dev": true, "license": "MIT" }, "node_modules/negotiator": { - "version": "0.6.4", + "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" @@ -18311,11 +15998,15 @@ }, "node_modules/neo-async": { "version": "2.6.2", + "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz", + "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==", "dev": true, "license": "MIT" }, "node_modules/netmask": { "version": "2.0.2", + "resolved": "https://registry.npmjs.org/netmask/-/netmask-2.0.2.tgz", + "integrity": "sha512-dBpDMdxv9Irdq66304OLfEmQ9tbNRFnFTuZiLo+bD+r332bBmMJ8GBLXklIXXgxd3+v9+KUnZaUR5PJMa75Gsg==", "dev": true, "license": "MIT", "engines": { @@ -18326,27 +16017,67 @@ "resolved": "docs", "link": true }, - "node_modules/nhs-notify-supplier-api-data-generator": { - "resolved": "scripts/test-data", - "link": true - }, "node_modules/nhs-notify-supplier-api-handler": { "resolved": "lambdas/api-handler", "link": true }, + "node_modules/nhs-notify-supplier-api-letter-test-data-utility": { + "resolved": "scripts/utilities/letter-test-data", + "link": true + }, + "node_modules/nhs-notify-supplier-api-letter-updates-transformer": { + "resolved": "lambdas/letter-updates-transformer", + "link": true + }, + "node_modules/nhs-notify-supplier-api-suppliers-data-utility": { + "resolved": "scripts/utilities/supplier-data", + "link": true + }, + "node_modules/nhs-notify-supplier-api-tests": { + "resolved": "tests", + "link": true + }, + "node_modules/nhs-notify-supplier-api-upsert-letter": { + "resolved": "lambdas/upsert-letter", + "link": true + }, "node_modules/nhs-notify-supplier-authorizer": { "resolved": "lambdas/authorizer", "link": true }, "node_modules/nhsuk-frontend": { - "version": "8.3.0", + "version": "10.2.2", + "resolved": "https://registry.npmjs.org/nhsuk-frontend/-/nhsuk-frontend-10.2.2.tgz", + "integrity": "sha512-l9iD+6lsB9aqAZgiY3VyvBxiqwzcoKDr65KhD0KVlgxC0wXFhY4hQP9/Fhud2N5a9TDv0aJkwTsxHIerPGKnLg==", "license": "MIT", "engines": { - "node": ">=20.0.0" + "node": "^20.9.0 || ^22.11.0 || ^24.11.0" + } + }, + "node_modules/nimma": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/nimma/-/nimma-0.2.3.tgz", + "integrity": "sha512-1ZOI8J+1PKKGceo/5CT5GfQOG6H8I2BencSK06YarZ2wXwH37BSSUWldqJmMJYA5JfqDqffxDXynt6f11AyKcA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@jsep-plugin/regex": "^1.0.1", + "@jsep-plugin/ternary": "^1.0.2", + "astring": "^1.8.1", + "jsep": "^1.2.0" + }, + "engines": { + "node": "^12.20 || >=14.13" + }, + "optionalDependencies": { + "jsonpath-plus": "^6.0.1 || ^10.1.0", + "lodash.topath": "^4.5.2" } }, "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": { @@ -18366,6 +16097,8 @@ }, "node_modules/node-fetch-h2": { "version": "2.3.0", + "resolved": "https://registry.npmjs.org/node-fetch-h2/-/node-fetch-h2-2.3.0.tgz", + "integrity": "sha512-ofRW94Ab0T4AOh5Fk8t0h8OBWrmjb0SSB20xh1H8YnPV9EJ+f5AMoYSUQ2zgJ4Iq2HAK0I2l5/Nequ8YzFS3Hg==", "dev": true, "license": "MIT", "dependencies": { @@ -18377,16 +16110,22 @@ }, "node_modules/node-fetch/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/node-fetch/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/node-fetch/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": { @@ -18394,13 +16133,28 @@ "webidl-conversions": "^3.0.0" } }, + "node_modules/node-gyp-build": { + "version": "4.8.4", + "resolved": "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-4.8.4.tgz", + "integrity": "sha512-LA4ZjwlnUblHVgq0oBF3Jl/6h/Nvs5fzBLwdEF4nuxnFdsfajde4WfxtJr3CaiH+F6ewcIB/q4jQ4UzPyid+CQ==", + "license": "MIT", + "bin": { + "node-gyp-build": "bin.js", + "node-gyp-build-optional": "optional.js", + "node-gyp-build-test": "build-test.js" + } + }, "node_modules/node-int64": { "version": "0.4.0", + "resolved": "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz", + "integrity": "sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw==", "dev": true, "license": "MIT" }, "node_modules/node-readfiles": { "version": "0.2.0", + "resolved": "https://registry.npmjs.org/node-readfiles/-/node-readfiles-0.2.0.tgz", + "integrity": "sha512-SU00ZarexNlE4Rjdm83vglt5Y9yiQ+XI1XpflWlb7q7UTN1JUItm69xMeiQCTxtTfnzt+83T8Cx+vI2ED++VDA==", "dev": true, "license": "MIT", "dependencies": { @@ -18408,12 +16162,45 @@ } }, "node_modules/node-releases": { - "version": "2.0.26", + "version": "2.0.27", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.27.tgz", + "integrity": "sha512-nmh3lCkYZ3grZvqcCH+fjmQ7X+H0OeZgP40OierEaAptX4XofMh5kwNbWh7lBduUzCcV/8kZ+NDLCwm2iorIlA==", "dev": true, "license": "MIT" }, + "node_modules/node-sarif-builder": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/node-sarif-builder/-/node-sarif-builder-2.0.3.tgz", + "integrity": "sha512-Pzr3rol8fvhG/oJjIq2NTVB0vmdNNlz22FENhhPojYRZ4/ee08CfK4YuKmuL54V9MLhI1kpzxfOJ/63LzmZzDg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/sarif": "^2.1.4", + "fs-extra": "^10.0.0" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/node-sarif-builder/node_modules/fs-extra": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz", + "integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=12" + } + }, "node_modules/normalize-path": { "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", "dev": true, "license": "MIT", "engines": { @@ -18422,6 +16209,8 @@ }, "node_modules/npm-run-path": { "version": "4.0.1", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", + "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", "license": "MIT", "dependencies": { "path-key": "^3.0.0" @@ -18431,12 +16220,16 @@ } }, "node_modules/nwsapi": { - "version": "2.2.21", + "version": "2.2.23", + "resolved": "https://registry.npmjs.org/nwsapi/-/nwsapi-2.2.23.tgz", + "integrity": "sha512-7wfH4sLbt4M0gCDzGE6vzQBo0bfTKjU7Sfpqy/7gs1qBfYz2vEJH6vXcBKpO3+6Yu1telwd0t9HpyOoLEQQbIQ==", "dev": true, "license": "MIT" }, "node_modules/oas-kit-common": { "version": "1.0.8", + "resolved": "https://registry.npmjs.org/oas-kit-common/-/oas-kit-common-1.0.8.tgz", + "integrity": "sha512-pJTS2+T0oGIwgjGpw7sIRU8RQMcUoKCDWFLdBqKB2BNmGpbBMH2sdqAaOXUg8OzonZHU0L7vfJu1mJFEiYDWOQ==", "dev": true, "license": "BSD-3-Clause", "dependencies": { @@ -18445,6 +16238,8 @@ }, "node_modules/oas-linter": { "version": "3.2.2", + "resolved": "https://registry.npmjs.org/oas-linter/-/oas-linter-3.2.2.tgz", + "integrity": "sha512-KEGjPDVoU5K6swgo9hJVA/qYGlwfbFx+Kg2QB/kd7rzV5N8N5Mg6PlsoCMohVnQmo+pzJap/F610qTodKzecGQ==", "dev": true, "license": "BSD-3-Clause", "dependencies": { @@ -18456,8 +16251,20 @@ "url": "https://github.com/Mermade/oas-kit?sponsor=1" } }, + "node_modules/oas-linter/node_modules/yaml": { + "version": "1.10.2", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz", + "integrity": "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">= 6" + } + }, "node_modules/oas-resolver": { "version": "2.5.6", + "resolved": "https://registry.npmjs.org/oas-resolver/-/oas-resolver-2.5.6.tgz", + "integrity": "sha512-Yx5PWQNZomfEhPPOphFbZKi9W93CocQj18NlD2Pa4GWZzdZpSJvYwoiuurRI7m3SpcChrnO08hkuQDL3FGsVFQ==", "dev": true, "license": "BSD-3-Clause", "dependencies": { @@ -18474,8 +16281,20 @@ "url": "https://github.com/Mermade/oas-kit?sponsor=1" } }, + "node_modules/oas-resolver/node_modules/yaml": { + "version": "1.10.2", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz", + "integrity": "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">= 6" + } + }, "node_modules/oas-schema-walker": { "version": "1.1.5", + "resolved": "https://registry.npmjs.org/oas-schema-walker/-/oas-schema-walker-1.1.5.tgz", + "integrity": "sha512-2yucenq1a9YPmeNExoUa9Qwrt9RFkjqaMAA1X+U7sbb0AqBeTIdMHky9SQQ6iN94bO5NW0W4TRYXerG+BdAvAQ==", "dev": true, "license": "BSD-3-Clause", "funding": { @@ -18484,6 +16303,8 @@ }, "node_modules/oas-validator": { "version": "5.0.8", + "resolved": "https://registry.npmjs.org/oas-validator/-/oas-validator-5.0.8.tgz", + "integrity": "sha512-cu20/HE5N5HKqVygs3dt94eYJfBi0TsZvPVXDhbXQHiEityDN+RROTleefoKRKKJ9dFAF2JBkDHgvWj0sjKGmw==", "dev": true, "license": "BSD-3-Clause", "dependencies": { @@ -18500,8 +16321,20 @@ "url": "https://github.com/Mermade/oas-kit?sponsor=1" } }, + "node_modules/oas-validator/node_modules/yaml": { + "version": "1.10.2", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz", + "integrity": "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">= 6" + } + }, "node_modules/object-assign": { "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", "dev": true, "license": "MIT", "engines": { @@ -18510,7 +16343,8 @@ }, "node_modules/object-inspect": { "version": "1.13.4", - "dev": true, + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz", + "integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==", "license": "MIT", "engines": { "node": ">= 0.4" @@ -18521,6 +16355,8 @@ }, "node_modules/object-keys": { "version": "1.1.1", + "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", + "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", "dev": true, "license": "MIT", "engines": { @@ -18529,6 +16365,8 @@ }, "node_modules/object.assign": { "version": "4.1.7", + "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.7.tgz", + "integrity": "sha512-nK28WOo+QIjBkDduTINE4JkF/UJJKyf2EJxvJKfblDpyg0Q+pkOHNTL0Qwy6NP6FhE/EnzV73BxxqcJaXY9anw==", "dev": true, "license": "MIT", "dependencies": { @@ -18548,6 +16386,8 @@ }, "node_modules/object.entries": { "version": "1.1.9", + "resolved": "https://registry.npmjs.org/object.entries/-/object.entries-1.1.9.tgz", + "integrity": "sha512-8u/hfXFRBD1O0hPUjioLhoWFHRmt6tKA4/vZPyckBr18l1KE9uHrFaFaUi8MDRTpi4uak2goyPTSNJLXX2k2Hw==", "dev": true, "license": "MIT", "dependencies": { @@ -18562,6 +16402,8 @@ }, "node_modules/object.fromentries": { "version": "2.0.8", + "resolved": "https://registry.npmjs.org/object.fromentries/-/object.fromentries-2.0.8.tgz", + "integrity": "sha512-k6E21FzySsSK5a21KRADBd/NGneRegFO5pLHfdQLpRDETUNJueLXs3WCzyQ3tFRDYgbq3KHGXfTbi2bs8WQ6rQ==", "dev": true, "license": "MIT", "dependencies": { @@ -18579,6 +16421,8 @@ }, "node_modules/object.groupby": { "version": "1.0.3", + "resolved": "https://registry.npmjs.org/object.groupby/-/object.groupby-1.0.3.tgz", + "integrity": "sha512-+Lhy3TQTuzXI5hevh8sBGqbmurHbbIjAi0Z4S63nthVLmLxfbj4T54a4CfZrXIrt9iP4mVAPYMo/v99taj3wjQ==", "dev": true, "license": "MIT", "dependencies": { @@ -18592,6 +16436,8 @@ }, "node_modules/object.values": { "version": "1.2.1", + "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.2.1.tgz", + "integrity": "sha512-gXah6aZrcUxjWg2zR2MwouP2eHlCBzdV4pygudehaKXSGW4v2AsRQUK+lwwXhii6KFZcunEnmSUoYp5CXibxtA==", "dev": true, "license": "MIT", "dependencies": { @@ -18609,17 +16455,35 @@ }, "node_modules/obliterator": { "version": "1.6.1", + "resolved": "https://registry.npmjs.org/obliterator/-/obliterator-1.6.1.tgz", + "integrity": "sha512-9WXswnqINnnhOG/5SLimUlzuU1hFJUc8zkwyD59Sd+dPOMf05PmnYG/d6Q7HZ+KmgkZJa1PxRso6QdM3sTNHig==", "license": "MIT" }, "node_modules/on-exit-leak-free": { "version": "2.1.2", + "resolved": "https://registry.npmjs.org/on-exit-leak-free/-/on-exit-leak-free-2.1.2.tgz", + "integrity": "sha512-0eJJY6hXLGf1udHwfNftBqH+g73EU4B504nZeKpz1sYRKafAghwxEJunB2O7rDZkL4PGfsMVnTXZ2EjibbqcsA==", "license": "MIT", "engines": { "node": ">=14.0.0" } }, + "node_modules/on-finished": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", + "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", + "license": "MIT", + "dependencies": { + "ee-first": "1.1.1" + }, + "engines": { + "node": ">= 0.8" + } + }, "node_modules/on-headers": { "version": "1.1.0", + "resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.1.0.tgz", + "integrity": "sha512-737ZY3yNnXy37FHkQxPzt4UZ2UWPWiCZWLvFZ4fu5cueciegX0zGPnrlY6bwRg4FdQOe9YU8MkmJwGhoMybl8A==", "license": "MIT", "engines": { "node": ">= 0.8" @@ -18627,7 +16491,8 @@ }, "node_modules/once": { "version": "1.4.0", - "dev": true, + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", "license": "ISC", "dependencies": { "wrappy": "1" @@ -18635,6 +16500,8 @@ }, "node_modules/onetime": { "version": "5.1.2", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", + "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", "license": "MIT", "dependencies": { "mimic-fn": "^2.1.0" @@ -18646,25 +16513,10 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/open": { - "version": "10.2.0", - "dev": true, - "license": "MIT", - "dependencies": { - "default-browser": "^5.2.1", - "define-lazy-prop": "^3.0.0", - "is-inside-container": "^1.0.0", - "wsl-utils": "^0.1.0" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/openapi-response-validator": { "version": "12.1.3", + "resolved": "https://registry.npmjs.org/openapi-response-validator/-/openapi-response-validator-12.1.3.tgz", + "integrity": "sha512-beZNb6r1SXAg1835S30h9XwjE596BYzXQFAEZlYAoO2imfxAu5S7TvNFws5k/MMKMCOFTzBXSjapqEvAzlblrQ==", "license": "MIT", "dependencies": { "ajv": "^8.4.0", @@ -18672,21 +16524,59 @@ } }, "node_modules/openapi-sampler": { - "version": "1.6.1", + "version": "1.6.2", + "resolved": "https://registry.npmjs.org/openapi-sampler/-/openapi-sampler-1.6.2.tgz", + "integrity": "sha512-NyKGiFKfSWAZr4srD/5WDhInOWDhfml32h/FKUqLpEwKJt0kG0LGUU0MdyNkKrVGuJnw6DuPWq/sHCwAMpiRxg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/json-schema": "^7.0.7", + "fast-xml-parser": "^4.5.0", + "json-pointer": "0.6.2" + } + }, + "node_modules/openapi-sampler/node_modules/fast-xml-parser": { + "version": "4.5.3", + "resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-4.5.3.tgz", + "integrity": "sha512-RKihhV+SHsIUGXObeVy9AXiBbFwkVk7Syp8XgwN5U3JV416+Gwp/GO9i0JYKmikykgz/UHRrrV4ROuZEo/T0ig==", "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/NaturalIntelligence" + } + ], "license": "MIT", "dependencies": { - "@types/json-schema": "^7.0.7", - "fast-xml-parser": "^4.5.0", - "json-pointer": "0.6.2" + "strnum": "^1.1.1" + }, + "bin": { + "fxparser": "src/cli/cli.js" } }, + "node_modules/openapi-sampler/node_modules/strnum": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/strnum/-/strnum-1.1.2.tgz", + "integrity": "sha512-vrN+B7DBIoTTZjnPNewwhx6cBA/H+IS7rfW68n7XxC1y7uoiGQBxaKzqucGUgavX15dJgiGztLJ8vxuEzwqBdA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/NaturalIntelligence" + } + ], + "license": "MIT" + }, "node_modules/openapi-types": { "version": "12.1.3", + "resolved": "https://registry.npmjs.org/openapi-types/-/openapi-types-12.1.3.tgz", + "integrity": "sha512-N4YtSYJqghVu4iek2ZUvcN/0aqH1kRDuNqzcycDxhOUpg7GdvLa2F3DgS6yBNhInhv2r/6I0Flkn7CqL8+nIcw==", "license": "MIT" }, "node_modules/openapi-typescript": { - "version": "7.9.1", + "version": "7.10.1", + "resolved": "https://registry.npmjs.org/openapi-typescript/-/openapi-typescript-7.10.1.tgz", + "integrity": "sha512-rBcU8bjKGGZQT4K2ekSTY2Q5veOQbVG/lTKZ49DeCyT9z62hM2Vj/LLHjDHC9W7LJG8YMHcdXpRZDqC1ojB/lw==", "dev": true, "license": "MIT", "dependencies": { @@ -18694,7 +16584,7 @@ "ansi-colors": "^4.1.3", "change-case": "^5.4.4", "parse-json": "^8.3.0", - "supports-color": "^10.1.0", + "supports-color": "^10.2.2", "yargs-parser": "^21.1.1" }, "bin": { @@ -18704,8 +16594,52 @@ "typescript": "^5.x" } }, + "node_modules/openapi-typescript/node_modules/@redocly/config": { + "version": "0.22.2", + "resolved": "https://registry.npmjs.org/@redocly/config/-/config-0.22.2.tgz", + "integrity": "sha512-roRDai8/zr2S9YfmzUfNhKjOF0NdcOIqF7bhf4MVC5UxpjIysDjyudvlAiVbpPHp3eDRWbdzUgtkK1a7YiDNyQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/openapi-typescript/node_modules/@redocly/openapi-core": { + "version": "1.34.6", + "resolved": "https://registry.npmjs.org/@redocly/openapi-core/-/openapi-core-1.34.6.tgz", + "integrity": "sha512-2+O+riuIUgVSuLl3Lyh5AplWZyVMNuG2F98/o6NrutKJfW4/GTZdPpZlIphS0HGgcOHgmWcCSHj+dWFlZaGSHw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@redocly/ajv": "^8.11.2", + "@redocly/config": "^0.22.0", + "colorette": "^1.2.0", + "https-proxy-agent": "^7.0.5", + "js-levenshtein": "^1.1.6", + "js-yaml": "^4.1.0", + "minimatch": "^5.0.1", + "pluralize": "^8.0.0", + "yaml-ast-parser": "0.0.43" + }, + "engines": { + "node": ">=18.17.0", + "npm": ">=9.5.0" + } + }, + "node_modules/openapi-typescript/node_modules/minimatch": { + "version": "5.1.6", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz", + "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/openapi-typescript/node_modules/parse-json": { "version": "8.3.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-8.3.0.tgz", + "integrity": "sha512-ybiGyvspI+fAoRQbIPRddCcSTV9/LsJbf0e/S85VLowVGzRmokfneg2kwVW/KU5rOXrPSbF1qAKPMgNTqqROQQ==", "dev": true, "license": "MIT", "dependencies": { @@ -18721,7 +16655,9 @@ } }, "node_modules/openapi-typescript/node_modules/supports-color": { - "version": "10.1.0", + "version": "10.2.2", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-10.2.2.tgz", + "integrity": "sha512-SS+jx45GF1QjgEXQx4NJZV9ImqmO2NPz5FNsIHrsDjh2YsHnawpan7SNQ1o8NuhrbHZy9AZhIoCUiCeaW/C80g==", "dev": true, "license": "MIT", "engines": { @@ -18733,6 +16669,8 @@ }, "node_modules/openapi-typescript/node_modules/type-fest": { "version": "4.41.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-4.41.0.tgz", + "integrity": "sha512-TeTSQ6H5YHvpqVwBRcnLDCBnDOHWYu7IvGbHT6N8AOymcr9PJGjc1GTtiWZTYg0NCgYwvnYWEkVChQAr9bjfwA==", "dev": true, "license": "(MIT OR CC0-1.0)", "engines": { @@ -18744,6 +16682,8 @@ }, "node_modules/optionator": { "version": "0.9.4", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", + "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==", "dev": true, "license": "MIT", "dependencies": { @@ -18760,6 +16700,8 @@ }, "node_modules/ora": { "version": "5.4.1", + "resolved": "https://registry.npmjs.org/ora/-/ora-5.4.1.tgz", + "integrity": "sha512-5b6Y85tPxZZ7QytO+BQzysW31HJku27cRIlkbAXaNx+BdcVi+LlRFmVXzeF6a7JCwJpyw5c4b+YSVImQIrBpuQ==", "dev": true, "license": "MIT", "dependencies": { @@ -18782,11 +16724,15 @@ }, "node_modules/outdent": { "version": "0.8.0", + "resolved": "https://registry.npmjs.org/outdent/-/outdent-0.8.0.tgz", + "integrity": "sha512-KiOAIsdpUTcAXuykya5fnVVT+/5uS0Q1mrkRHcF89tpieSmY33O/tmc54CqwA+bfhbtEfZUNLHaPUiB9X3jt1A==", "dev": true, "license": "MIT" }, "node_modules/own-keys": { "version": "1.0.1", + "resolved": "https://registry.npmjs.org/own-keys/-/own-keys-1.0.1.tgz", + "integrity": "sha512-qFOyK5PjiWZd+QQIh+1jhdb9LpxTF0qs7Pm8o5QHYZ0M3vKqSqzsZaEB6oWlxZ+q2sJBMI/Ktgd2N5ZwQoRHfg==", "dev": true, "license": "MIT", "dependencies": { @@ -18803,6 +16749,8 @@ }, "node_modules/p-limit": { "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", "dev": true, "license": "MIT", "dependencies": { @@ -18817,6 +16765,8 @@ }, "node_modules/p-locate": { "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", "dev": true, "license": "MIT", "dependencies": { @@ -18831,6 +16781,8 @@ }, "node_modules/p-try": { "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", + "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", "dev": true, "license": "MIT", "engines": { @@ -18839,6 +16791,8 @@ }, "node_modules/pac-proxy-agent": { "version": "7.2.0", + "resolved": "https://registry.npmjs.org/pac-proxy-agent/-/pac-proxy-agent-7.2.0.tgz", + "integrity": "sha512-TEB8ESquiLMc0lV8vcd5Ql/JAKAoyzHFXaStwjkzpOpC5Yv+pIzLfHvjTSdf3vpa2bMiUQrg9i6276yn8666aA==", "dev": true, "license": "MIT", "dependencies": { @@ -18857,6 +16811,8 @@ }, "node_modules/pac-resolver": { "version": "7.0.1", + "resolved": "https://registry.npmjs.org/pac-resolver/-/pac-resolver-7.0.1.tgz", + "integrity": "sha512-5NPgf87AT2STgwa2ntRMr45jTKrYBGkVU36yT0ig/n/GMAa3oPqhZfIQ2kMEimReg0+t9kZViDVZ83qfVUlckg==", "dev": true, "license": "MIT", "dependencies": { @@ -18869,11 +16825,15 @@ }, "node_modules/package-json-from-dist": { "version": "1.0.1", + "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz", + "integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==", "dev": true, "license": "BlueOak-1.0.0" }, "node_modules/parent-module": { "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", + "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", "dev": true, "license": "MIT", "dependencies": { @@ -18885,6 +16845,8 @@ }, "node_modules/parse-json": { "version": "5.2.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", + "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", "dev": true, "license": "MIT", "dependencies": { @@ -18902,6 +16864,8 @@ }, "node_modules/parse5": { "version": "7.3.0", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-7.3.0.tgz", + "integrity": "sha512-IInvU7fabl34qmi9gY8XOVxhYyMyuH2xUNpb2q8/Y+7552KlejkRvqvD19nMoUW/uQGGbqNpA6Tufu5FL5BZgw==", "dev": true, "license": "MIT", "dependencies": { @@ -18911,13 +16875,26 @@ "url": "https://github.com/inikulin/parse5?sponsor=1" } }, + "node_modules/parseurl": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", + "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, "node_modules/path-browserify": { "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-browserify/-/path-browserify-1.0.1.tgz", + "integrity": "sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g==", "dev": true, "license": "MIT" }, "node_modules/path-exists": { "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", "dev": true, "license": "MIT", "engines": { @@ -18926,6 +16903,8 @@ }, "node_modules/path-is-absolute": { "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", "dev": true, "license": "MIT", "engines": { @@ -18934,10 +16913,14 @@ }, "node_modules/path-is-inside": { "version": "1.0.2", + "resolved": "https://registry.npmjs.org/path-is-inside/-/path-is-inside-1.0.2.tgz", + "integrity": "sha512-DUWJr3+ULp4zXmol/SZkFf3JGsS9/SIv+Y3Rt93/UjPpDpklB5f1er4O3POIbUuUJ3FXgqte2Q7SrU6zAqwk8w==", "license": "(WTFPL OR MIT)" }, "node_modules/path-key": { "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", "license": "MIT", "engines": { "node": ">=8" @@ -18945,11 +16928,15 @@ }, "node_modules/path-parse": { "version": "1.0.7", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", "dev": true, "license": "MIT" }, "node_modules/path-scurry": { - "version": "2.0.0", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-2.0.1.tgz", + "integrity": "sha512-oWyT4gICAu+kaA7QWk/jvCHWarMKNs6pXOGWKDTr7cw4IGcUbW+PeTfbaQiLGheFRpjo6O9J0PmyMfQPjH71oA==", "dev": true, "license": "BlueOak-1.0.0", "dependencies": { @@ -18964,48 +16951,60 @@ } }, "node_modules/path-scurry/node_modules/lru-cache": { - "version": "11.1.0", + "version": "11.2.4", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.2.4.tgz", + "integrity": "sha512-B5Y16Jr9LB9dHVkh6ZevG+vAbOsNOYCX+sXvFWFu7B3Iz5mijW3zdbMyhsh8ANd2mSWBYdJgnqi+mL7/LrOPYg==", "dev": true, - "license": "ISC", + "license": "BlueOak-1.0.0", "engines": { "node": "20 || >=22" } }, "node_modules/path-to-regexp": { - "version": "8.2.0", - "dev": true, + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-8.3.0.tgz", + "integrity": "sha512-7jdwVIRtsP8MYpdXSwOS0YdD0Du+qOoF/AEPIt88PcCFrZCzx41oxku1jD88hZBwbNUIEfpqvuhjFaMAqMTWnA==", "license": "MIT", - "engines": { - "node": ">=16" + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" } }, "node_modules/perfect-scrollbar": { "version": "1.5.6", + "resolved": "https://registry.npmjs.org/perfect-scrollbar/-/perfect-scrollbar-1.5.6.tgz", + "integrity": "sha512-rixgxw3SxyJbCaSpo1n35A/fwI1r2rdwMKOTCg/AcG+xOEyZcE8UHVjpZMFCVImzsFoCZeJTT+M/rdEIQYO2nw==", "dev": true, "license": "MIT" }, "node_modules/picocolors": { "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", "dev": true, "license": "ISC" }, "node_modules/picomatch": { - "version": "2.3.1", + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", + "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", "dev": true, "license": "MIT", "engines": { - "node": ">=8.6" + "node": ">=12" }, "funding": { "url": "https://github.com/sponsors/jonschlinkert" } }, "node_modules/pino": { - "version": "9.9.0", + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/pino/-/pino-10.1.0.tgz", + "integrity": "sha512-0zZC2ygfdqvqK8zJIr1e+wT1T/L+LF6qvqvbzEQ6tiMAoTqEVK9a1K3YRu8HEUvGEvNqZyPJTtb2sNIoTkB83w==", "license": "MIT", "dependencies": { + "@pinojs/redact": "^0.4.0", "atomic-sleep": "^1.0.0", - "fast-redact": "^3.1.1", "on-exit-leak-free": "^2.1.0", "pino-abstract-transport": "^2.0.0", "pino-std-serializers": "^7.0.0", @@ -19022,17 +17021,83 @@ }, "node_modules/pino-abstract-transport": { "version": "2.0.0", + "resolved": "https://registry.npmjs.org/pino-abstract-transport/-/pino-abstract-transport-2.0.0.tgz", + "integrity": "sha512-F63x5tizV6WCh4R6RHyi2Ml+M70DNRXt/+HANowMflpgGFMAym/VKm6G7ZOQRjqN7XbGxK1Lg9t6ZrtzOaivMw==", + "license": "MIT", + "dependencies": { + "split2": "^4.0.0" + } + }, + "node_modules/pino-pretty": { + "version": "13.1.3", + "resolved": "https://registry.npmjs.org/pino-pretty/-/pino-pretty-13.1.3.tgz", + "integrity": "sha512-ttXRkkOz6WWC95KeY9+xxWL6AtImwbyMHrL1mSwqwW9u+vLp/WIElvHvCSDg0xO/Dzrggz1zv3rN5ovTRVowKg==", + "license": "MIT", + "dependencies": { + "colorette": "^2.0.7", + "dateformat": "^4.6.3", + "fast-copy": "^4.0.0", + "fast-safe-stringify": "^2.1.1", + "help-me": "^5.0.0", + "joycon": "^3.1.1", + "minimist": "^1.2.6", + "on-exit-leak-free": "^2.1.0", + "pino-abstract-transport": "^3.0.0", + "pump": "^3.0.0", + "secure-json-parse": "^4.0.0", + "sonic-boom": "^4.0.1", + "strip-json-comments": "^5.0.2" + }, + "bin": { + "pino-pretty": "bin.js" + } + }, + "node_modules/pino-pretty/node_modules/colorette": { + "version": "2.0.20", + "resolved": "https://registry.npmjs.org/colorette/-/colorette-2.0.20.tgz", + "integrity": "sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==", + "license": "MIT" + }, + "node_modules/pino-pretty/node_modules/dateformat": { + "version": "4.6.3", + "resolved": "https://registry.npmjs.org/dateformat/-/dateformat-4.6.3.tgz", + "integrity": "sha512-2P0p0pFGzHS5EMnhdxQi7aJN+iMheud0UhG4dlE1DLAlvL8JHjJJTX/CSm4JXwV0Ka5nGk3zC5mcb5bUQUxxMA==", + "license": "MIT", + "engines": { + "node": "*" + } + }, + "node_modules/pino-pretty/node_modules/pino-abstract-transport": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pino-abstract-transport/-/pino-abstract-transport-3.0.0.tgz", + "integrity": "sha512-wlfUczU+n7Hy/Ha5j9a/gZNy7We5+cXp8YL+X+PG8S0KXxw7n/JXA3c46Y0zQznIJ83URJiwy7Lh56WLokNuxg==", "license": "MIT", "dependencies": { "split2": "^4.0.0" } }, + "node_modules/pino-pretty/node_modules/strip-json-comments": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-5.0.3.tgz", + "integrity": "sha512-1tB5mhVo7U+ETBKNf92xT4hrQa3pm0MZ0PQvuDnWgAAGHDsfp4lPSpiS6psrSiet87wyGPh9ft6wmhOMQ0hDiw==", + "license": "MIT", + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/pino-std-serializers": { "version": "7.0.0", + "resolved": "https://registry.npmjs.org/pino-std-serializers/-/pino-std-serializers-7.0.0.tgz", + "integrity": "sha512-e906FRY0+tV27iq4juKzSYPbUj2do2X2JX4EzSca1631EB2QJQUqGbDuERal7LCtOpxl6x3+nvo9NPZcmjkiFA==", "license": "MIT" }, "node_modules/pirates": { "version": "4.0.7", + "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.7.tgz", + "integrity": "sha512-TfySrs/5nm8fQJDcBDuUng3VOUKsd7S+zqvbOTiGXHfxX4wK31ard+hoNuvkicM/2YFzlpDgABOevKSsB4G/FA==", "dev": true, "license": "MIT", "engines": { @@ -19041,6 +17106,8 @@ }, "node_modules/pkg-dir": { "version": "4.2.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", + "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", "dev": true, "license": "MIT", "dependencies": { @@ -19052,6 +17119,8 @@ }, "node_modules/pkg-dir/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==", "dev": true, "license": "MIT", "dependencies": { @@ -19064,6 +17133,8 @@ }, "node_modules/pkg-dir/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==", "dev": true, "license": "MIT", "dependencies": { @@ -19075,6 +17146,8 @@ }, "node_modules/pkg-dir/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==", "dev": true, "license": "MIT", "dependencies": { @@ -19089,6 +17162,8 @@ }, "node_modules/pkg-dir/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==", "dev": true, "license": "MIT", "dependencies": { @@ -19099,10 +17174,12 @@ } }, "node_modules/playwright": { - "version": "1.55.1", + "version": "1.57.0", + "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.57.0.tgz", + "integrity": "sha512-ilYQj1s8sr2ppEJ2YVadYBN0Mb3mdo9J0wQ+UuDhzYqURwSoW4n1Xs5vs7ORwgDGmyEh33tRMeS8KhdkMoLXQw==", "license": "Apache-2.0", "dependencies": { - "playwright-core": "1.55.1" + "playwright-core": "1.57.0" }, "bin": { "playwright": "cli.js" @@ -19115,7 +17192,9 @@ } }, "node_modules/playwright-core": { - "version": "1.55.1", + "version": "1.57.0", + "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.57.0.tgz", + "integrity": "sha512-agTcKlMw/mjBWOnD6kFZttAAGHgi/Nw0CZ2o6JqWSbMlI219lAFLZZCyqByTsvVAJq5XA5H8cA6PrvBRpBWEuQ==", "license": "Apache-2.0", "bin": { "playwright-core": "cli.js" @@ -19140,6 +17219,8 @@ }, "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": { @@ -19148,6 +17229,8 @@ }, "node_modules/polished": { "version": "4.3.1", + "resolved": "https://registry.npmjs.org/polished/-/polished-4.3.1.tgz", + "integrity": "sha512-OBatVyC/N7SCW/FaDHrSd+vn0o5cS855TOmYi4OkdWUMSJCET/xip//ch8xGUvtr3i44X9LVyWwQlRMTN3pwSA==", "dev": true, "license": "MIT", "dependencies": { @@ -19157,9 +17240,20 @@ "node": ">=10" } }, + "node_modules/pony-cause": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/pony-cause/-/pony-cause-1.1.1.tgz", + "integrity": "sha512-PxkIc/2ZpLiEzQXu5YRDOUgBlfGYBY8156HY5ZcRAwwonMk5W/MrJP2LLkG/hF7GEQzaHo2aS7ho6ZLCOvf+6g==", + "dev": true, + "license": "0BSD", + "engines": { + "node": ">=12.0.0" + } + }, "node_modules/possible-typed-array-names": { "version": "1.1.0", - "dev": true, + "resolved": "https://registry.npmjs.org/possible-typed-array-names/-/possible-typed-array-names-1.1.0.tgz", + "integrity": "sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg==", "license": "MIT", "engines": { "node": ">= 0.4" @@ -19167,6 +17261,8 @@ }, "node_modules/postcss": { "version": "8.4.49", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.49.tgz", + "integrity": "sha512-OCVPnIObs4N29kxTjzLfUryOkvZEq+pf8jTF0lg8E7uETuWHA+v7j3c/xJmiqpX450191LlmZfUKkXxkTry7nA==", "dev": true, "funding": [ { @@ -19194,11 +17290,15 @@ }, "node_modules/postcss-value-parser": { "version": "4.2.0", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", + "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==", "dev": true, "license": "MIT" }, "node_modules/prelude-ls": { "version": "1.2.1", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", + "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", "dev": true, "license": "MIT", "engines": { @@ -19206,7 +17306,9 @@ } }, "node_modules/prettier": { - "version": "3.6.2", + "version": "3.7.4", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.7.4.tgz", + "integrity": "sha512-v6UNi1+3hSlVvv8fSaoUbggEM5VErKmmpGA7Pl3HF8V6uKY7rvClBOJlH6yNwQtfTueNkGVpOv/mtWL9L4bgRA==", "dev": true, "license": "MIT", "peer": true, @@ -19222,6 +17324,8 @@ }, "node_modules/prettier-linter-helpers": { "version": "1.0.0", + "resolved": "https://registry.npmjs.org/prettier-linter-helpers/-/prettier-linter-helpers-1.0.0.tgz", + "integrity": "sha512-GbK2cP9nraSSUF9N2XwUwqfzlAFlMNYYl+ShE/V+H8a9uNl/oUqB1w2EL54Jh0OlyRSd8RfWYJ3coVS4TROP2w==", "dev": true, "license": "MIT", "dependencies": { @@ -19232,20 +17336,24 @@ } }, "node_modules/pretty-format": { - "version": "29.7.0", + "version": "30.2.0", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-30.2.0.tgz", + "integrity": "sha512-9uBdv/B4EefsuAL+pWqueZyZS2Ba+LxfFeQ9DN14HU4bN8bhaxKdkpjpB6fs9+pSjIBu+FXQHImEg8j/Lw0+vA==", "dev": true, "license": "MIT", "dependencies": { - "@jest/schemas": "^29.6.3", - "ansi-styles": "^5.0.0", - "react-is": "^18.0.0" + "@jest/schemas": "30.0.5", + "ansi-styles": "^5.2.0", + "react-is": "^18.3.1" }, "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" } }, "node_modules/pretty-format/node_modules/ansi-styles": { "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", "dev": true, "license": "MIT", "engines": { @@ -19255,8 +17363,17 @@ "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, + "node_modules/printable-characters": { + "version": "1.0.42", + "resolved": "https://registry.npmjs.org/printable-characters/-/printable-characters-1.0.42.tgz", + "integrity": "sha512-dKp+C4iXWK4vVYZmYSd0KBH5F/h1HoZRsbJ82AVKRO3PEo8L4lBS/vLwhVtpwwuYcoIsVY+1JYKR268yn480uQ==", + "dev": true, + "license": "Unlicense" + }, "node_modules/prismjs": { "version": "1.30.0", + "resolved": "https://registry.npmjs.org/prismjs/-/prismjs-1.30.0.tgz", + "integrity": "sha512-DEvV2ZF2r2/63V+tK8hQvrR2ZGn10srHbXviTlcv7Kpzw8jWiNTqbVgjO3IY8RxrrOUF8VPMQQFysYYYv0YZxw==", "dev": true, "license": "MIT", "engines": { @@ -19265,6 +17382,8 @@ }, "node_modules/process": { "version": "0.11.10", + "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz", + "integrity": "sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==", "dev": true, "license": "MIT", "engines": { @@ -19273,11 +17392,15 @@ }, "node_modules/process-nextick-args": { "version": "2.0.1", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", + "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", "dev": true, "license": "MIT" }, "node_modules/process-warning": { "version": "5.0.0", + "resolved": "https://registry.npmjs.org/process-warning/-/process-warning-5.0.0.tgz", + "integrity": "sha512-a39t9ApHNx2L4+HBnQKqxxHNs1r7KF+Intd8Q/g1bUh6q0WIp9voPXJ/x0j+ZL45KF1pJd9+q2jLIRMfvEshkA==", "funding": [ { "type": "github", @@ -19290,20 +17413,10 @@ ], "license": "MIT" }, - "node_modules/prompts": { - "version": "2.4.2", - "dev": true, - "license": "MIT", - "dependencies": { - "kleur": "^3.0.3", - "sisteransi": "^1.0.5" - }, - "engines": { - "node": ">= 6" - } - }, "node_modules/prop-types": { "version": "15.8.1", + "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz", + "integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==", "dev": true, "license": "MIT", "dependencies": { @@ -19314,11 +17427,15 @@ }, "node_modules/prop-types/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==", "dev": true, "license": "MIT" }, "node_modules/proper-lockfile": { "version": "4.1.2", + "resolved": "https://registry.npmjs.org/proper-lockfile/-/proper-lockfile-4.1.2.tgz", + "integrity": "sha512-TjNPblN4BwAWMXU8s9AEz4JmQxnD1NNL7bNOY/AKUzyamc379FWASUhc/K1pL2noVb+XmZKLL68cjzLsiOAMaA==", "dev": true, "license": "MIT", "dependencies": { @@ -19327,13 +17444,10 @@ "signal-exit": "^3.0.2" } }, - "node_modules/proper-lockfile/node_modules/signal-exit": { - "version": "3.0.7", - "dev": true, - "license": "ISC" - }, "node_modules/properties-reader": { "version": "2.3.0", + "resolved": "https://registry.npmjs.org/properties-reader/-/properties-reader-2.3.0.tgz", + "integrity": "sha512-z597WicA7nDZxK12kZqHr2TcvwNU1GCfA5UwfDY/HDp3hXPoPlb5rlEx9bwGTiJnc0OqbBTkU975jDToth8Gxw==", "dev": true, "license": "MIT", "dependencies": { @@ -19348,7 +17462,9 @@ } }, "node_modules/protobufjs": { - "version": "7.5.3", + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-7.5.4.tgz", + "integrity": "sha512-CvexbZtbov6jW2eXAvLukXjXUW1TzFaivC46BpWc/3BpcCysb5Vffu+B3XHMm8lVEuy2Mm4XGex8hBSg1yapPg==", "dev": true, "hasInstallScript": true, "license": "BSD-3-Clause", @@ -19370,8 +17486,23 @@ "node": ">=12.0.0" } }, + "node_modules/proxy-addr": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", + "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", + "license": "MIT", + "dependencies": { + "forwarded": "0.2.0", + "ipaddr.js": "1.9.1" + }, + "engines": { + "node": ">= 0.10" + } + }, "node_modules/proxy-agent": { "version": "6.5.0", + "resolved": "https://registry.npmjs.org/proxy-agent/-/proxy-agent-6.5.0.tgz", + "integrity": "sha512-TmatMXdr2KlRiA2CyDu8GqR8EjahTG3aY3nXjdzFyoZbmB8hrBsTyMezhULIXKnC0jpfjlmiZ3+EaCzoInSu/A==", "dev": true, "license": "MIT", "dependencies": { @@ -19390,6 +17521,8 @@ }, "node_modules/proxy-agent/node_modules/lru-cache": { "version": "7.18.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-7.18.3.tgz", + "integrity": "sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA==", "dev": true, "license": "ISC", "engines": { @@ -19398,12 +17531,14 @@ }, "node_modules/proxy-from-env": { "version": "1.1.0", - "dev": true, + "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", + "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==", "license": "MIT" }, "node_modules/pump": { "version": "3.0.3", - "dev": true, + "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.3.tgz", + "integrity": "sha512-todwxLMY7/heScKmntwQG8CXVkWUOdYxIvY2s0VWAAMh/nd8SoYiRaKjlr7+iCs984f2P8zvrfWcDDYVb73NfA==", "license": "MIT", "dependencies": { "end-of-stream": "^1.1.0", @@ -19411,14 +17546,15 @@ } }, "node_modules/punycode": { - "version": "2.3.1", - "license": "MIT", - "engines": { - "node": ">=6" - } + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.3.2.tgz", + "integrity": "sha512-RofWgt/7fL5wP1Y7fxE7/EmTLzQVnB0ycyibJ0OOHIlJqTNzglYFxVwETOcIoJqJmpDXJ9xImDv+Fq34F/d4Dw==", + "license": "MIT" }, "node_modules/pure-rand": { "version": "7.0.1", + "resolved": "https://registry.npmjs.org/pure-rand/-/pure-rand-7.0.1.tgz", + "integrity": "sha512-oTUZM/NAZS8p7ANR3SHh30kXB+zK2r2BPcEn/awJIbOvq82WoMN4p62AWWp3Hhw50G0xMsw1mhIBLqHw64EcNQ==", "dev": true, "funding": [ { @@ -19432,8 +17568,34 @@ ], "license": "MIT" }, + "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/querystring": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/querystring/-/querystring-0.2.0.tgz", + "integrity": "sha512-X/xY82scca2tau62i9mDyU9K+I+djTMUsvwf7xnUX5GLvVzgJybOJf4Y6o9Zx3oJK/LSXg5tTZBjwzqVPaPO2g==", + "deprecated": "The querystring API is considered Legacy. new code should use the URLSearchParams API instead.", + "engines": { + "node": ">=0.4.x" + } + }, "node_modules/queue-microtask": { "version": "1.2.3", + "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", "dev": true, "funding": [ { @@ -19453,10 +17615,37 @@ }, "node_modules/quick-format-unescaped": { "version": "4.0.4", + "resolved": "https://registry.npmjs.org/quick-format-unescaped/-/quick-format-unescaped-4.0.4.tgz", + "integrity": "sha512-tYC1Q1hgyRuHgloV/YXs2w15unPVh8qfu/qCTfhTYamaw7fyhumKa2yGpdSo87vY32rIclj+4fWYQXUMs9EHvg==", "license": "MIT" }, + "node_modules/ramda": { + "version": "0.32.0", + "resolved": "https://registry.npmjs.org/ramda/-/ramda-0.32.0.tgz", + "integrity": "sha512-GQWAHhxhxWBWA8oIBr1XahFVjQ9Fic6MK9ikijfd4TZHfE2+urfk+irVlR5VOn48uwMgM+loRRBJd6Yjsbc0zQ==", + "license": "MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/ramda" + } + }, + "node_modules/randexp": { + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/randexp/-/randexp-0.5.3.tgz", + "integrity": "sha512-U+5l2KrcMNOUPYvazA3h5ekF80FHTUG+87SEAmHZmolh1M+i/WyTCxVzmi+tidIa1tM4BSe8g2Y/D3loWDjj+w==", + "license": "MIT", + "dependencies": { + "drange": "^1.0.2", + "ret": "^0.2.0" + }, + "engines": { + "node": ">=4" + } + }, "node_modules/randombytes": { "version": "2.1.0", + "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", + "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", "dev": true, "license": "MIT", "dependencies": { @@ -19464,14 +17653,33 @@ } }, "node_modules/range-parser": { - "version": "1.2.0", + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", + "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", "license": "MIT", "engines": { "node": ">= 0.6" } }, + "node_modules/raw-body": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-3.0.2.tgz", + "integrity": "sha512-K5zQjDllxWkf7Z5xJdV0/B0WTNqx6vxG70zJE4N0kBs4LovmEYWJzQGxC9bS9RAKu3bgM40lrd5zoLJ12MQ5BA==", + "license": "MIT", + "dependencies": { + "bytes": "~3.1.2", + "http-errors": "~2.0.1", + "iconv-lite": "~0.7.0", + "unpipe": "~1.0.0" + }, + "engines": { + "node": ">= 0.10" + } + }, "node_modules/rc": { "version": "1.2.8", + "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz", + "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==", "license": "(BSD-2-Clause OR MIT OR Apache-2.0)", "dependencies": { "deep-extend": "^0.6.0", @@ -19485,13 +17693,17 @@ }, "node_modules/rc/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": "sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==", "license": "MIT", "engines": { "node": ">=0.10.0" } }, "node_modules/react": { - "version": "19.1.1", + "version": "19.2.3", + "resolved": "https://registry.npmjs.org/react/-/react-19.2.3.tgz", + "integrity": "sha512-Ku/hhYbVjOQnXDZFv2+RibmLFGwFdeeKHFcOTlrt7xplBnya5OGn/hIRDsqDiSUcfORsDC7MPxwork8jBwsIWA==", "dev": true, "license": "MIT", "engines": { @@ -19499,23 +17711,29 @@ } }, "node_modules/react-dom": { - "version": "19.1.1", + "version": "19.2.3", + "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.2.3.tgz", + "integrity": "sha512-yELu4WmLPw5Mr/lmeEpox5rw3RETacE++JgHqQzd2dg+YbJuat3jH4ingc+WPZhxaoFzdv9y33G+F7Nl5O0GBg==", "dev": true, "license": "MIT", "dependencies": { - "scheduler": "^0.26.0" + "scheduler": "^0.27.0" }, "peerDependencies": { - "react": "^19.1.1" + "react": "^19.2.3" } }, "node_modules/react-is": { "version": "19.0.0", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-19.0.0.tgz", + "integrity": "sha512-H91OHcwjZsbq3ClIDHMzBShc1rotbfACdWENsmEf0IFvZ3FgGPtdHMcsv45bQ1hAbgdfiA8SnxTKfDS+x/8m2g==", "dev": true, "license": "MIT" }, "node_modules/react-tabs": { "version": "6.1.0", + "resolved": "https://registry.npmjs.org/react-tabs/-/react-tabs-6.1.0.tgz", + "integrity": "sha512-6QtbTRDKM+jA/MZTTefvigNxo0zz+gnBTVFw2CFVvq+f2BuH0nF0vDLNClL045nuTAdOoK/IL1vTP0ZLX0DAyQ==", "dev": true, "license": "MIT", "dependencies": { @@ -19528,6 +17746,8 @@ }, "node_modules/readable-stream": { "version": "3.6.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", "dev": true, "license": "MIT", "dependencies": { @@ -19541,14 +17761,31 @@ }, "node_modules/readdir-glob": { "version": "1.1.3", + "resolved": "https://registry.npmjs.org/readdir-glob/-/readdir-glob-1.1.3.tgz", + "integrity": "sha512-v05I2k7xN8zXvPD9N+z/uhXPaj0sUFCe2rcWZIpBsqxfP7xXFQ0tipAd/wjj1YxWyWtUS5IDJpOG82JKt2EAVA==", "dev": true, "license": "Apache-2.0", "dependencies": { "minimatch": "^5.1.0" } }, + "node_modules/readdir-glob/node_modules/minimatch": { + "version": "5.1.6", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz", + "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/readdirp": { "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", "dev": true, "license": "MIT", "dependencies": { @@ -19558,15 +17795,32 @@ "node": ">=8.10.0" } }, + "node_modules/readdirp/node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, "node_modules/real-require": { "version": "0.2.0", + "resolved": "https://registry.npmjs.org/real-require/-/real-require-0.2.0.tgz", + "integrity": "sha512-57frrGM/OCTLqLOAh0mhVA9VBMHd+9U7Zb2THMGdBUoZVOtGbJzjxsYGDJ3A9AYYCP4hn6y1TVbaOfzWtm5GFg==", "license": "MIT", "engines": { "node": ">= 12.13.0" } }, "node_modules/redoc": { - "version": "2.5.0", + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/redoc/-/redoc-2.5.1.tgz", + "integrity": "sha512-LmqA+4A3CmhTllGG197F0arUpmChukAj9klfSdxNRemT9Hr07xXr7OGKu4PHzBs359sgrJ+4JwmOlM7nxLPGMg==", "dev": true, "license": "MIT", "dependencies": { @@ -19579,7 +17833,7 @@ "lunr": "^2.3.9", "mark.js": "^8.11.1", "marked": "^4.3.0", - "mobx-react": "^9.1.1", + "mobx-react": "9.2.0", "openapi-sampler": "^1.5.0", "path-browserify": "^1.0.1", "perfect-scrollbar": "^1.5.5", @@ -19604,8 +17858,59 @@ "styled-components": "^4.1.1 || ^5.1.1 || ^6.0.5" } }, + "node_modules/redoc/node_modules/@redocly/config": { + "version": "0.22.2", + "resolved": "https://registry.npmjs.org/@redocly/config/-/config-0.22.2.tgz", + "integrity": "sha512-roRDai8/zr2S9YfmzUfNhKjOF0NdcOIqF7bhf4MVC5UxpjIysDjyudvlAiVbpPHp3eDRWbdzUgtkK1a7YiDNyQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/redoc/node_modules/@redocly/openapi-core": { + "version": "1.34.6", + "resolved": "https://registry.npmjs.org/@redocly/openapi-core/-/openapi-core-1.34.6.tgz", + "integrity": "sha512-2+O+riuIUgVSuLl3Lyh5AplWZyVMNuG2F98/o6NrutKJfW4/GTZdPpZlIphS0HGgcOHgmWcCSHj+dWFlZaGSHw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@redocly/ajv": "^8.11.2", + "@redocly/config": "^0.22.0", + "colorette": "^1.2.0", + "https-proxy-agent": "^7.0.5", + "js-levenshtein": "^1.1.6", + "js-yaml": "^4.1.0", + "minimatch": "^5.0.1", + "pluralize": "^8.0.0", + "yaml-ast-parser": "0.0.43" + }, + "engines": { + "node": ">=18.17.0", + "npm": ">=9.5.0" + } + }, + "node_modules/redoc/node_modules/eventemitter3": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-5.0.1.tgz", + "integrity": "sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA==", + "dev": true, + "license": "MIT" + }, + "node_modules/redoc/node_modules/minimatch": { + "version": "5.1.6", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz", + "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/refa": { "version": "0.12.1", + "resolved": "https://registry.npmjs.org/refa/-/refa-0.12.1.tgz", + "integrity": "sha512-J8rn6v4DBb2nnFqkqwy6/NnTYMcgLA+sLr0iIO41qpv0n+ngb7ksag2tMRl0inb1bbO/esUwzW1vbJi7K0sI0g==", "dev": true, "license": "MIT", "dependencies": { @@ -19617,11 +17922,15 @@ }, "node_modules/reflect-metadata": { "version": "0.2.2", + "resolved": "https://registry.npmjs.org/reflect-metadata/-/reflect-metadata-0.2.2.tgz", + "integrity": "sha512-urBwgfrvVP/eAyXx4hluJivBKzuEbSQs9rKWCrCkbSxNv8mxPcUZKeuoF3Uy4mJl3Lwprp6yy5/39VWigZ4K6Q==", "dev": true, "license": "Apache-2.0" }, "node_modules/reflect.getprototypeof": { "version": "1.0.10", + "resolved": "https://registry.npmjs.org/reflect.getprototypeof/-/reflect.getprototypeof-1.0.10.tgz", + "integrity": "sha512-00o4I+DVrefhv+nX0ulyi3biSHCPDe+yLv5o/p6d/UVlirijB8E16FtfwSAi4g3tcqrQ4lRAqQSoFEZJehYEcw==", "dev": true, "license": "MIT", "dependencies": { @@ -19643,6 +17952,8 @@ }, "node_modules/reftools": { "version": "1.1.9", + "resolved": "https://registry.npmjs.org/reftools/-/reftools-1.1.9.tgz", + "integrity": "sha512-OVede/NQE13xBQ+ob5CKd5KyeJYU2YInb1bmV4nRoOfquZPkAkxuOXicSe1PvqIuZZ4kD13sPKBbR7UFDmli6w==", "dev": true, "license": "BSD-3-Clause", "funding": { @@ -19651,6 +17962,8 @@ }, "node_modules/regexp-ast-analysis": { "version": "0.7.1", + "resolved": "https://registry.npmjs.org/regexp-ast-analysis/-/regexp-ast-analysis-0.7.1.tgz", + "integrity": "sha512-sZuz1dYW/ZsfG17WSAG7eS85r5a0dDsvg+7BiiYR5o6lKCAtUrEwdmRmaGF6rwVj3LcmAeYkOWKEPlbPzN3Y3A==", "dev": true, "license": "MIT", "dependencies": { @@ -19663,6 +17976,8 @@ }, "node_modules/regexp-tree": { "version": "0.1.27", + "resolved": "https://registry.npmjs.org/regexp-tree/-/regexp-tree-0.1.27.tgz", + "integrity": "sha512-iETxpjK6YoRWJG5o6hXLwvjYAoW+FEZn9os0PD/b6AP6xQwsa/Y7lCVgIixBbUPMfhu+i2LtdeAqVTgGlQarfA==", "dev": true, "license": "MIT", "bin": { @@ -19671,6 +17986,8 @@ }, "node_modules/regexp.prototype.flags": { "version": "1.5.4", + "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.4.tgz", + "integrity": "sha512-dYqgNSZbDwkaJ2ceRd9ojCGjBq+mOm9LmtXnAnEGyHhN/5R7iDW2TRw3h+o/jCFxus3P2LfWIIiwowAjANm7IA==", "dev": true, "license": "MIT", "dependencies": { @@ -19690,6 +18007,8 @@ }, "node_modules/registry-auth-token": { "version": "3.3.2", + "resolved": "https://registry.npmjs.org/registry-auth-token/-/registry-auth-token-3.3.2.tgz", + "integrity": "sha512-JL39c60XlzCVgNrO+qq68FoNb56w/m7JYvGR2jT5iR1xBrUA3Mfx5Twk5rqTThPmQKMWydGmq8oFtDlxfrmxnQ==", "license": "MIT", "dependencies": { "rc": "^1.1.6", @@ -19698,6 +18017,8 @@ }, "node_modules/registry-url": { "version": "3.1.0", + "resolved": "https://registry.npmjs.org/registry-url/-/registry-url-3.1.0.tgz", + "integrity": "sha512-ZbgR5aZEdf4UKZVBPYIgaglBmSF2Hi94s2PcIHhRGFjKYu+chjJdYfHn4rt3hB6eCKLJ8giVIIfgMa1ehDfZKA==", "license": "MIT", "dependencies": { "rc": "^1.0.1" @@ -19708,6 +18029,8 @@ }, "node_modules/regjsparser": { "version": "0.13.0", + "resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.13.0.tgz", + "integrity": "sha512-NZQZdC5wOE/H3UT28fVGL+ikOZcEzfMGk/c3iN9UGxzWHMa1op7274oyiUVrAG4B2EuFhus8SvkaYnhvW92p9Q==", "dev": true, "license": "BSD-2-Clause", "dependencies": { @@ -19719,24 +18042,45 @@ }, "node_modules/require-directory": { "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/require-from-string": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", + "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", "license": "MIT", "engines": { "node": ">=0.10.0" } }, - "node_modules/require-from-string": { - "version": "2.0.2", - "license": "MIT", + "node_modules/requires-port": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz", + "integrity": "sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==", + "license": "MIT" + }, + "node_modules/reserved": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/reserved/-/reserved-0.1.2.tgz", + "integrity": "sha512-/qO54MWj5L8WCBP9/UNe2iefJc+L9yETbH32xO/ft/EYPOTCR5k+azvDUgdCOKwZH8hXwPd0b8XBL78Nn2U69g==", + "dev": true, "engines": { - "node": ">=0.10.0" + "node": ">=0.8" } }, "node_modules/resolve": { - "version": "1.22.10", + "version": "1.22.11", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.11.tgz", + "integrity": "sha512-RfqAvLnMl313r7c9oclB1HhUEAezcpLjz95wFH4LVuhk9JF/r22qmVP9AMmOU4vMX7Q8pN8jwNg/CSpdFnMjTQ==", "dev": true, "license": "MIT", "dependencies": { - "is-core-module": "^2.16.0", + "is-core-module": "^2.16.1", "path-parse": "^1.0.7", "supports-preserve-symlinks-flag": "^1.0.0" }, @@ -19752,6 +18096,8 @@ }, "node_modules/resolve-cwd": { "version": "3.0.0", + "resolved": "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-3.0.0.tgz", + "integrity": "sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg==", "dev": true, "license": "MIT", "dependencies": { @@ -19763,6 +18109,8 @@ }, "node_modules/resolve-cwd/node_modules/resolve-from": { "version": "5.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", + "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", "dev": true, "license": "MIT", "engines": { @@ -19771,6 +18119,8 @@ }, "node_modules/resolve-from": { "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", "dev": true, "license": "MIT", "engines": { @@ -19779,22 +18129,18 @@ }, "node_modules/resolve-pkg-maps": { "version": "1.0.0", + "resolved": "https://registry.npmjs.org/resolve-pkg-maps/-/resolve-pkg-maps-1.0.0.tgz", + "integrity": "sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==", "dev": true, "license": "MIT", "funding": { "url": "https://github.com/privatenumber/resolve-pkg-maps?sponsor=1" } }, - "node_modules/resolve.exports": { - "version": "2.0.3", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10" - } - }, "node_modules/restore-cursor": { "version": "3.1.0", + "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz", + "integrity": "sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==", "dev": true, "license": "MIT", "dependencies": { @@ -19805,13 +18151,19 @@ "node": ">=8" } }, - "node_modules/restore-cursor/node_modules/signal-exit": { - "version": "3.0.7", - "dev": true, - "license": "ISC" + "node_modules/ret": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/ret/-/ret-0.2.2.tgz", + "integrity": "sha512-M0b3YWQs7R3Z917WRQy1HHA7Ba7D8hvZg6UE5mLykJxQVE2ju0IXbGlaHPPlkY+WN7wFP+wUMXmBFA0aV6vYGQ==", + "license": "MIT", + "engines": { + "node": ">=4" + } }, "node_modules/retry": { "version": "0.12.0", + "resolved": "https://registry.npmjs.org/retry/-/retry-0.12.0.tgz", + "integrity": "sha512-9LkiTwjUh6rT555DtE9rTX+BKByPfrMzEAtnlEtdEwr3Nkffwiihqe2bWADg+OQRjt9gl6ICdmB/ZFDCGAtSow==", "dev": true, "license": "MIT", "engines": { @@ -19820,6 +18172,8 @@ }, "node_modules/reusify": { "version": "1.1.0", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.1.0.tgz", + "integrity": "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==", "dev": true, "license": "MIT", "engines": { @@ -19827,24 +18181,49 @@ "node": ">=0.10.0" } }, - "node_modules/rrweb-cssom": { - "version": "0.8.0", - "dev": true, - "license": "MIT" - }, - "node_modules/run-applescript": { - "version": "7.0.0", + "node_modules/rollup": { + "version": "2.79.2", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-2.79.2.tgz", + "integrity": "sha512-fS6iqSPZDs3dr/y7Od6y5nha8dW1YnbgtsyotCVvoFGKbERG++CVRFv1meyGDE1SNItQA8BrnCw7ScdAhRJ3XQ==", "dev": true, "license": "MIT", + "bin": { + "rollup": "dist/bin/rollup" + }, "engines": { - "node": ">=18" + "node": ">=10.0.0" }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "optionalDependencies": { + "fsevents": "~2.3.2" + } + }, + "node_modules/router": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/router/-/router-2.2.0.tgz", + "integrity": "sha512-nLTrUKm2UyiL7rlhapu/Zl45FwNgkZGaCpZbIHajDYgwlJCOzLSk+cIPAnsEqV955GjILJnKbdQC1nVPz+gAYQ==", + "license": "MIT", + "dependencies": { + "debug": "^4.4.0", + "depd": "^2.0.0", + "is-promise": "^4.0.0", + "parseurl": "^1.3.3", + "path-to-regexp": "^8.0.0" + }, + "engines": { + "node": ">= 18" } }, + "node_modules/rrweb-cssom": { + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/rrweb-cssom/-/rrweb-cssom-0.8.0.tgz", + "integrity": "sha512-guoltQEx+9aMf2gDZ0s62EcV8lsXR+0w8915TC3ITdn2YueuNjdAYh/levpU9nFaoChh9RUS5ZdQMrKfVEN9tw==", + "dev": true, + "license": "MIT" + }, "node_modules/run-async": { "version": "2.4.1", + "resolved": "https://registry.npmjs.org/run-async/-/run-async-2.4.1.tgz", + "integrity": "sha512-tvVnVv01b8c1RrA6Ep7JkStj85Guv/YrMcwqYQnwjsAS2cTmmPGBBjAjpCW7RrSodNSoE2/qg9O4bceNvUuDgQ==", "dev": true, "license": "MIT", "engines": { @@ -19853,6 +18232,8 @@ }, "node_modules/run-parallel": { "version": "1.2.0", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", "dev": true, "funding": [ { @@ -19875,6 +18256,8 @@ }, "node_modules/rxjs": { "version": "7.8.2", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.2.tgz", + "integrity": "sha512-dhKf903U/PQZY6boNNtAGdWbG85WAbjT/1xYoZIC7FAY0yWapOBQVsVrDl58W86//e1VpMNBtRV4MaXfdMySFA==", "dev": true, "license": "Apache-2.0", "dependencies": { @@ -19883,6 +18266,8 @@ }, "node_modules/safe-array-concat": { "version": "1.1.3", + "resolved": "https://registry.npmjs.org/safe-array-concat/-/safe-array-concat-1.1.3.tgz", + "integrity": "sha512-AURm5f0jYEOydBj7VQlVvDrjeFgthDdEF5H1dP+6mNpoXOMo1quQqJ4wvJDyRZ9+pO3kGWoOdmV08cSv2aJV6Q==", "dev": true, "license": "MIT", "dependencies": { @@ -19899,12 +18284,37 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/safe-array-concat/node_modules/isarray": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz", + "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==", + "dev": true, + "license": "MIT" + }, "node_modules/safe-buffer": { - "version": "5.1.2", + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], "license": "MIT" }, "node_modules/safe-push-apply": { "version": "1.0.0", + "resolved": "https://registry.npmjs.org/safe-push-apply/-/safe-push-apply-1.0.0.tgz", + "integrity": "sha512-iKE9w/Z7xCzUMIZqdBsp6pEQvwuEebH4vdpjcDWnyzaI6yl6O9FHvVpmGelvEHNsoY6wGblkxR6Zty/h00WiSA==", "dev": true, "license": "MIT", "dependencies": { @@ -19918,8 +18328,17 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/safe-push-apply/node_modules/isarray": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz", + "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==", + "dev": true, + "license": "MIT" + }, "node_modules/safe-regex": { "version": "2.1.1", + "resolved": "https://registry.npmjs.org/safe-regex/-/safe-regex-2.1.1.tgz", + "integrity": "sha512-rx+x8AMzKb5Q5lQ95Zoi6ZbJqwCLkqi3XuJXp5P3rT8OEc6sZCJG5AE5dU3lsgRr/F4Bs31jSlVN+j5KrsGu9A==", "dev": true, "license": "MIT", "dependencies": { @@ -19928,7 +18347,8 @@ }, "node_modules/safe-regex-test": { "version": "1.1.0", - "dev": true, + "resolved": "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.1.0.tgz", + "integrity": "sha512-x/+Cz4YrimQxQccJf5mKEbIa1NzeCRNI5Ecl/ekmlYaampdNLPalVyIcCZNNH3MvmqBugV5TMYZXv0ljslUlaw==", "license": "MIT", "dependencies": { "call-bound": "^1.0.2", @@ -19944,6 +18364,8 @@ }, "node_modules/safe-stable-stringify": { "version": "2.5.0", + "resolved": "https://registry.npmjs.org/safe-stable-stringify/-/safe-stable-stringify-2.5.0.tgz", + "integrity": "sha512-b3rppTKm9T+PsVCBEOUR46GWI7fdOs00VKZ1+9c1EWDaDMvjQc6tUwuFyIprgGgTcWoVHSKrU8H31ZHA2e0RHA==", "license": "MIT", "engines": { "node": ">=10" @@ -19951,11 +18373,20 @@ }, "node_modules/safer-buffer": { "version": "2.1.2", - "dev": true, + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", "license": "MIT" }, + "node_modules/sax": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.1.tgz", + "integrity": "sha512-8I2a3LovHTOpm7NV5yOyO8IHqgVsfK4+UuySrXU8YXkSRX7k6hCV9b3HrkKCr3nMpgj+0bmocaJJWpvp1oc7ZA==", + "license": "ISC" + }, "node_modules/saxes": { "version": "6.0.0", + "resolved": "https://registry.npmjs.org/saxes/-/saxes-6.0.0.tgz", + "integrity": "sha512-xAg7SOnEhrm5zI3puOOKyy1OMcMlIJZYNJY7xLBwSze0UjhPLnWfj2GF2EpT0jmzaJKIWKHLsaSSajf35bcYnA==", "dev": true, "license": "ISC", "dependencies": { @@ -19966,12 +18397,16 @@ } }, "node_modules/scheduler": { - "version": "0.26.0", + "version": "0.27.0", + "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.27.0.tgz", + "integrity": "sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q==", "dev": true, "license": "MIT" }, "node_modules/scslre": { "version": "0.3.0", + "resolved": "https://registry.npmjs.org/scslre/-/scslre-0.3.0.tgz", + "integrity": "sha512-3A6sD0WYP7+QrjbfNA2FN3FsOaGGFoekCVgTyypy53gPxhbkCIjtO6YWgdrfM+n/8sI8JeXZOIxsHjMTNxQ4nQ==", "dev": true, "license": "MIT", "dependencies": { @@ -19983,8 +18418,26 @@ "node": "^14.0.0 || >=16.0.0" } }, + "node_modules/secure-json-parse": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/secure-json-parse/-/secure-json-parse-4.1.0.tgz", + "integrity": "sha512-l4KnYfEyqYJxDwlNVyRfO2E4NTHfMKAWdUuA8J0yve2Dz/E/PdBepY03RvyJpssIpRFwJoCD55wA+mEDs6ByWA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fastify" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fastify" + } + ], + "license": "BSD-3-Clause" + }, "node_modules/semver": { - "version": "7.7.2", + "version": "7.7.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.3.tgz", + "integrity": "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==", "dev": true, "license": "ISC", "bin": { @@ -19994,8 +18447,36 @@ "node": ">=10" } }, + "node_modules/send": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/send/-/send-1.2.1.tgz", + "integrity": "sha512-1gnZf7DFcoIcajTjTwjwuDjzuz4PPcY2StKPlsGAQ1+YH20IRVrBaXSWmdjowTJ6u8Rc01PoYOGHXfP1mYcZNQ==", + "license": "MIT", + "dependencies": { + "debug": "^4.4.3", + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "etag": "^1.8.1", + "fresh": "^2.0.0", + "http-errors": "^2.0.1", + "mime-types": "^3.0.2", + "ms": "^2.1.3", + "on-finished": "^2.4.1", + "range-parser": "^1.2.1", + "statuses": "^2.0.2" + }, + "engines": { + "node": ">= 18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, "node_modules/serve": { "version": "14.2.5", + "resolved": "https://registry.npmjs.org/serve/-/serve-14.2.5.tgz", + "integrity": "sha512-Qn/qMkzCcMFVPb60E/hQy+iRLpiU8PamOfOSYoAHmmF+fFFmpPpqa6Oci2iWYpTdOUM3VF+TINud7CfbQnsZbA==", "license": "MIT", "dependencies": { "@zeit/schemas": "2.36.0", @@ -20019,6 +18500,8 @@ }, "node_modules/serve-handler": { "version": "6.1.6", + "resolved": "https://registry.npmjs.org/serve-handler/-/serve-handler-6.1.6.tgz", + "integrity": "sha512-x5RL9Y2p5+Sh3D38Fh9i/iQ5ZK+e4xuXRd/pGbM4D13tgo/MGwbttUk8emytcr1YYzBYs+apnUngBDFYfpjPuQ==", "license": "MIT", "dependencies": { "bytes": "3.0.0", @@ -20032,6 +18515,8 @@ }, "node_modules/serve-handler/node_modules/brace-expansion": { "version": "1.1.12", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", + "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", "license": "MIT", "dependencies": { "balanced-match": "^1.0.0", @@ -20040,13 +18525,26 @@ }, "node_modules/serve-handler/node_modules/bytes": { "version": "3.0.0", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz", + "integrity": "sha512-pMhOfFDPiv9t5jjIXkHosWmkSyQbvsgEVNkz0ERHbuLh2T/7j4Mqqpz523Fe8MVY89KC6Sh/QfS2sM+SjgFDcw==", "license": "MIT", "engines": { "node": ">= 0.8" } }, + "node_modules/serve-handler/node_modules/content-disposition": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.2.tgz", + "integrity": "sha512-kRGRZw3bLlFISDBgwTSA1TMBFN6J6GWDeubmDE3AF+3+yXL8hTWv8r5rkLbqYXY4RjPk/EzHnClI3zQf1cFmHA==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, "node_modules/serve-handler/node_modules/mime-db": { "version": "1.33.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.33.0.tgz", + "integrity": "sha512-BHJ/EKruNIqJf/QahvxwQZXKygOQ256myeN/Ew+THcAa5q+PjyTTMMeNQC4DZw5AwfvelsUrA6B67NKMqXDbzQ==", "license": "MIT", "engines": { "node": ">= 0.6" @@ -20054,6 +18552,8 @@ }, "node_modules/serve-handler/node_modules/mime-types": { "version": "2.1.18", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.18.tgz", + "integrity": "sha512-lc/aahn+t4/SWV/qcmumYjymLsWfN3ELhpmVuUFjgsORruuZPVSwAQryq+HHGvO/SI2KVX26bx+En+zhM8g8hQ==", "license": "MIT", "dependencies": { "mime-db": "~1.33.0" @@ -20064,6 +18564,8 @@ }, "node_modules/serve-handler/node_modules/minimatch": { "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", "license": "ISC", "dependencies": { "brace-expansion": "^1.1.7" @@ -20074,10 +18576,42 @@ }, "node_modules/serve-handler/node_modules/path-to-regexp": { "version": "3.3.0", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-3.3.0.tgz", + "integrity": "sha512-qyCH421YQPS2WFDxDjftfc1ZR5WKQzVzqsp4n9M2kQhVOo/ByahFoUNJfl58kOcEGfQ//7weFTDhm+ss8Ecxgw==", "license": "MIT" }, + "node_modules/serve-handler/node_modules/range-parser": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.0.tgz", + "integrity": "sha512-kA5WQoNVo4t9lNx2kQNFCxKeBl5IbbSNBl1M/tLkw9WCn+hxNBAW5Qh8gdhs63CJnhjJ2zQWFoqPJP2sK1AV5A==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/serve-static": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-2.2.1.tgz", + "integrity": "sha512-xRXBn0pPqQTVQiC8wyQrKs2MOlX24zQ0POGaj0kultvoOCstBQM5yvOhAVSUwOMjQtTvsPWoNCHfPGwaaQJhTw==", + "license": "MIT", + "dependencies": { + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "parseurl": "^1.3.3", + "send": "^1.2.0" + }, + "engines": { + "node": ">= 18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, "node_modules/serve/node_modules/ajv": { "version": "8.12.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.12.0.tgz", + "integrity": "sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA==", "license": "MIT", "dependencies": { "fast-deep-equal": "^3.1.1", @@ -20092,6 +18626,8 @@ }, "node_modules/serve/node_modules/chalk": { "version": "5.0.1", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.0.1.tgz", + "integrity": "sha512-Fo07WOYGqMfCWHOzSXOt2CxDbC6skS/jO9ynEcmpANMoPrD+W1r1K6Vx7iNm+AQmETU1Xr2t+n8nzkV9t6xh3w==", "license": "MIT", "engines": { "node": "^12.17.0 || ^14.13 || >=16.0.0" @@ -20101,13 +18637,16 @@ } }, "node_modules/set-cookie-parser": { - "version": "2.7.1", + "version": "2.7.2", + "resolved": "https://registry.npmjs.org/set-cookie-parser/-/set-cookie-parser-2.7.2.tgz", + "integrity": "sha512-oeM1lpU/UvhTxw+g3cIfxXHyJRc/uidd3yK1P242gzHds0udQBYzs3y8j4gCCW+ZJ7ad0yctld8RYO+bdurlvw==", "dev": true, "license": "MIT" }, "node_modules/set-function-length": { "version": "1.2.2", - "dev": true, + "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz", + "integrity": "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==", "license": "MIT", "dependencies": { "define-data-property": "^1.1.4", @@ -20123,6 +18662,8 @@ }, "node_modules/set-function-name": { "version": "2.0.2", + "resolved": "https://registry.npmjs.org/set-function-name/-/set-function-name-2.0.2.tgz", + "integrity": "sha512-7PGFlmtwsEADb0WYyvCMa1t+yke6daIG4Wirafur5kcf+MhUnPms1UeR0CKQdTZD81yESwMHbtn+TR+dMviakQ==", "dev": true, "license": "MIT", "dependencies": { @@ -20137,6 +18678,8 @@ }, "node_modules/set-proto": { "version": "1.0.0", + "resolved": "https://registry.npmjs.org/set-proto/-/set-proto-1.0.0.tgz", + "integrity": "sha512-RJRdvCo6IAnPdsvP/7m6bsQqNnn1FCBX5ZNtFL98MmFF/4xAIJTIg1YbHW5DC2W5SKZanrC6i4HsJqlajw/dZw==", "dev": true, "license": "MIT", "dependencies": { @@ -20148,13 +18691,23 @@ "node": ">= 0.4" } }, + "node_modules/setprototypeof": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", + "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==", + "license": "ISC" + }, "node_modules/shallowequal": { "version": "1.1.0", + "resolved": "https://registry.npmjs.org/shallowequal/-/shallowequal-1.1.0.tgz", + "integrity": "sha512-y0m1JoUZSlPAjXVtPPW70aZWfIL/dSP7AFkRnniLCrK/8MDKog3TySTBmckD+RObVxH0v4Tox67+F14PdED2oQ==", "dev": true, "license": "MIT" }, "node_modules/shebang-command": { "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", "license": "MIT", "dependencies": { "shebang-regex": "^3.0.0" @@ -20165,6 +18718,8 @@ }, "node_modules/shebang-regex": { "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", "license": "MIT", "engines": { "node": ">=8" @@ -20172,6 +18727,8 @@ }, "node_modules/shell-quote": { "version": "1.8.3", + "resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.8.3.tgz", + "integrity": "sha512-ObmnIF4hXNg1BqhnHmgbDETF8dLPCggZWBjkQfhZpbszZnYur5DUljTcCHii5LC3J5E0yeO/1LIMyH+UvHQgyw==", "dev": true, "license": "MIT", "engines": { @@ -20183,6 +18740,8 @@ }, "node_modules/should": { "version": "13.2.3", + "resolved": "https://registry.npmjs.org/should/-/should-13.2.3.tgz", + "integrity": "sha512-ggLesLtu2xp+ZxI+ysJTmNjh2U0TsC+rQ/pfED9bUZZ4DKefP27D+7YJVVTvKsmjLpIi9jAa7itwDGkDDmt1GQ==", "dev": true, "license": "MIT", "dependencies": { @@ -20195,6 +18754,8 @@ }, "node_modules/should-equal": { "version": "2.0.0", + "resolved": "https://registry.npmjs.org/should-equal/-/should-equal-2.0.0.tgz", + "integrity": "sha512-ZP36TMrK9euEuWQYBig9W55WPC7uo37qzAEmbjHz4gfyuXrEUgF8cUvQVO+w+d3OMfPvSRQJ22lSm8MQJ43LTA==", "dev": true, "license": "MIT", "dependencies": { @@ -20203,6 +18764,8 @@ }, "node_modules/should-format": { "version": "3.0.3", + "resolved": "https://registry.npmjs.org/should-format/-/should-format-3.0.3.tgz", + "integrity": "sha512-hZ58adtulAk0gKtua7QxevgUaXTTXxIi8t41L3zo9AHvjXO1/7sdLECuHeIN2SRtYXpNkmhoUP2pdeWgricQ+Q==", "dev": true, "license": "MIT", "dependencies": { @@ -20212,11 +18775,15 @@ }, "node_modules/should-type": { "version": "1.4.0", + "resolved": "https://registry.npmjs.org/should-type/-/should-type-1.4.0.tgz", + "integrity": "sha512-MdAsTu3n25yDbIe1NeN69G4n6mUnJGtSJHygX3+oN0ZbO3DTiATnf7XnYJdGT42JCXurTb1JI0qOBR65shvhPQ==", "dev": true, "license": "MIT" }, "node_modules/should-type-adaptors": { "version": "1.1.0", + "resolved": "https://registry.npmjs.org/should-type-adaptors/-/should-type-adaptors-1.1.0.tgz", + "integrity": "sha512-JA4hdoLnN+kebEp2Vs8eBe9g7uy0zbRo+RMcU0EsNy+R+k049Ki+N5tT5Jagst2g7EAja+euFuoXFCa8vIklfA==", "dev": true, "license": "MIT", "dependencies": { @@ -20226,12 +18793,15 @@ }, "node_modules/should-util": { "version": "1.0.1", + "resolved": "https://registry.npmjs.org/should-util/-/should-util-1.0.1.tgz", + "integrity": "sha512-oXF8tfxx5cDk8r2kYqlkUJzZpDBqVY/II2WhvU0n9Y3XYvAYRmeaf1PvvIvTgPnv4KJ+ES5M0PyDq5Jp+Ygy2g==", "dev": true, "license": "MIT" }, "node_modules/side-channel": { "version": "1.1.0", - "dev": true, + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz", + "integrity": "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==", "license": "MIT", "dependencies": { "es-errors": "^1.3.0", @@ -20249,7 +18819,8 @@ }, "node_modules/side-channel-list": { "version": "1.0.0", - "dev": true, + "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.0.tgz", + "integrity": "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==", "license": "MIT", "dependencies": { "es-errors": "^1.3.0", @@ -20264,7 +18835,8 @@ }, "node_modules/side-channel-map": { "version": "1.0.1", - "dev": true, + "resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz", + "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==", "license": "MIT", "dependencies": { "call-bound": "^1.0.2", @@ -20281,7 +18853,8 @@ }, "node_modules/side-channel-weakmap": { "version": "1.0.2", - "dev": true, + "resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz", + "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==", "license": "MIT", "dependencies": { "call-bound": "^1.0.2", @@ -20298,18 +18871,28 @@ } }, "node_modules/signal-exit": { - "version": "4.1.0", + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", + "license": "ISC" + }, + "node_modules/simple-eval": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/simple-eval/-/simple-eval-1.0.1.tgz", + "integrity": "sha512-LH7FpTAkeD+y5xQC4fzS+tFtaNlvt3Ib1zKzvhjv/Y+cioV4zIuw4IZr2yhRLu67CWL7FR9/6KXKnjRoZTvGGQ==", "dev": true, - "license": "ISC", - "engines": { - "node": ">=14" + "license": "MIT", + "dependencies": { + "jsep": "^1.3.6" }, - "funding": { - "url": "https://github.com/sponsors/isaacs" + "engines": { + "node": ">=12" } }, "node_modules/simple-websocket": { "version": "9.1.0", + "resolved": "https://registry.npmjs.org/simple-websocket/-/simple-websocket-9.1.0.tgz", + "integrity": "sha512-8MJPnjRN6A8UCp1I+H/dSFyjwJhp6wta4hsVRhjf8w9qBHRzxYt14RaOcjvQnhD1N4yKOddEjflwMnQM4VtXjQ==", "dev": true, "funding": [ { @@ -20336,6 +18919,8 @@ }, "node_modules/simple-websocket/node_modules/ws": { "version": "7.5.10", + "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.10.tgz", + "integrity": "sha512-+dbF1tHwZpXcbOJdVOkzLDxZP1ailvSxM6ZweXTegylPny803bFhA+vqBYw4s31NSAk4S2Qz+AKXK9a4wkdjcQ==", "dev": true, "license": "MIT", "engines": { @@ -20354,13 +18939,10 @@ } } }, - "node_modules/sisteransi": { - "version": "1.0.5", - "dev": true, - "license": "MIT" - }, "node_modules/slash": { "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", "dev": true, "license": "MIT", "engines": { @@ -20369,6 +18951,8 @@ }, "node_modules/slugify": { "version": "1.4.7", + "resolved": "https://registry.npmjs.org/slugify/-/slugify-1.4.7.tgz", + "integrity": "sha512-tf+h5W1IrjNm/9rKKj0JU2MDMruiopx0jjVA5zCdBtcGjfp0+c5rHw/zADLC3IeKlGHtVbHtpfzvYA0OYT+HKg==", "dev": true, "license": "MIT", "engines": { @@ -20377,6 +18961,8 @@ }, "node_modules/smart-buffer": { "version": "4.2.0", + "resolved": "https://registry.npmjs.org/smart-buffer/-/smart-buffer-4.2.0.tgz", + "integrity": "sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==", "dev": true, "license": "MIT", "engines": { @@ -20386,6 +18972,8 @@ }, "node_modules/socks": { "version": "2.8.7", + "resolved": "https://registry.npmjs.org/socks/-/socks-2.8.7.tgz", + "integrity": "sha512-HLpt+uLy/pxB+bum/9DzAgiKS8CX1EvbWxI4zlmgGCExImLdiad2iCwXT5Z4c9c3Eq8rP2318mPW2c+QbtjK8A==", "dev": true, "license": "MIT", "dependencies": { @@ -20399,6 +18987,8 @@ }, "node_modules/socks-proxy-agent": { "version": "8.0.5", + "resolved": "https://registry.npmjs.org/socks-proxy-agent/-/socks-proxy-agent-8.0.5.tgz", + "integrity": "sha512-HehCEsotFqbPW9sJ8WVYB6UbmIMv7kUUORIF2Nncq4VQvBfNBLibW9YZR5dlYCSUhwcD628pRllm7n+E+YTzJw==", "dev": true, "license": "MIT", "dependencies": { @@ -20412,6 +19002,8 @@ }, "node_modules/sonic-boom": { "version": "4.2.0", + "resolved": "https://registry.npmjs.org/sonic-boom/-/sonic-boom-4.2.0.tgz", + "integrity": "sha512-INb7TM37/mAcsGmc9hyyI6+QR3rR1zVRu36B0NeGXKnOOLiZOfER5SA+N7X7k3yUYRzLWafduTDvJAfDswwEww==", "license": "MIT", "dependencies": { "atomic-sleep": "^1.0.0" @@ -20419,6 +19011,8 @@ }, "node_modules/source-map": { "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", "dev": true, "license": "BSD-3-Clause", "engines": { @@ -20427,6 +19021,8 @@ }, "node_modules/source-map-js": { "version": "1.2.1", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", + "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", "dev": true, "license": "BSD-3-Clause", "engines": { @@ -20435,6 +19031,8 @@ }, "node_modules/source-map-support": { "version": "0.5.13", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.13.tgz", + "integrity": "sha512-SHSKFHadjVA5oR4PPqhtAVdcBWwRYVd6g6cAXnIbRiIwc2EhPrTuKUBdSLvlEKyIP3GCf89fltvcZiP9MMFA1w==", "dev": true, "license": "MIT", "dependencies": { @@ -20442,13 +19040,25 @@ "source-map": "^0.6.0" } }, + "node_modules/sourcemap-codec": { + "version": "1.4.8", + "resolved": "https://registry.npmjs.org/sourcemap-codec/-/sourcemap-codec-1.4.8.tgz", + "integrity": "sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA==", + "deprecated": "Please use @jridgewell/sourcemap-codec instead", + "dev": true, + "license": "MIT" + }, "node_modules/split-ca": { "version": "1.0.1", + "resolved": "https://registry.npmjs.org/split-ca/-/split-ca-1.0.1.tgz", + "integrity": "sha512-Q5thBSxp5t8WPTTJQS59LrGqOZqOsrhDGDVm8azCqIBjSBd7nd9o2PM+mDulQQkh8h//4U6hFZnc/mul8t5pWQ==", "dev": true, "license": "ISC" }, "node_modules/split2": { "version": "4.2.0", + "resolved": "https://registry.npmjs.org/split2/-/split2-4.2.0.tgz", + "integrity": "sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==", "license": "ISC", "engines": { "node": ">= 10.x" @@ -20456,11 +19066,14 @@ }, "node_modules/sprintf-js": { "version": "1.0.3", - "dev": true, + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", + "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==", "license": "BSD-3-Clause" }, "node_modules/ssh-remote-port-forward": { "version": "1.0.4", + "resolved": "https://registry.npmjs.org/ssh-remote-port-forward/-/ssh-remote-port-forward-1.0.4.tgz", + "integrity": "sha512-x0LV1eVDwjf1gmG7TTnfqIzf+3VPRz7vrNIjX6oYLbeCrf/PeVY6hkT68Mg+q02qXxQhrLjB0jfgvhevoCRmLQ==", "dev": true, "license": "MIT", "dependencies": { @@ -20470,6 +19083,8 @@ }, "node_modules/ssh-remote-port-forward/node_modules/@types/ssh2": { "version": "0.5.52", + "resolved": "https://registry.npmjs.org/@types/ssh2/-/ssh2-0.5.52.tgz", + "integrity": "sha512-lbLLlXxdCZOSJMCInKH2+9V/77ET2J6NPQHpFI0kda61Dd1KglJs+fPQBchizmzYSOJBgdTajhPqBO1xxLywvg==", "dev": true, "license": "MIT", "dependencies": { @@ -20478,7 +19093,9 @@ } }, "node_modules/ssh2": { - "version": "1.16.0", + "version": "1.17.0", + "resolved": "https://registry.npmjs.org/ssh2/-/ssh2-1.17.0.tgz", + "integrity": "sha512-wPldCk3asibAjQ/kziWQQt1Wh3PgDFpC0XpwclzKcdT1vql6KeYxf5LIt4nlFkUeR8WuphYMKqUA56X4rjbfgQ==", "dev": true, "hasInstallScript": true, "dependencies": { @@ -20490,16 +19107,20 @@ }, "optionalDependencies": { "cpu-features": "~0.0.10", - "nan": "^2.20.0" + "nan": "^2.23.0" } }, "node_modules/stable-hash": { "version": "0.0.5", + "resolved": "https://registry.npmjs.org/stable-hash/-/stable-hash-0.0.5.tgz", + "integrity": "sha512-+L3ccpzibovGXFK+Ap/f8LOS0ahMrHTf3xu7mMLSpEGU0EO9ucaysSylKo9eRDFNhWve/y275iPmIZ4z39a9iA==", "dev": true, "license": "MIT" }, "node_modules/stable-hash-x": { "version": "0.2.0", + "resolved": "https://registry.npmjs.org/stable-hash-x/-/stable-hash-x-0.2.0.tgz", + "integrity": "sha512-o3yWv49B/o4QZk5ZcsALc6t0+eCelPc44zZsLtCQnZPDwFpDYSWcDnrv2TtMmMbQ7uKo3J0HTURCqckw23czNQ==", "dev": true, "license": "MIT", "engines": { @@ -20508,7 +19129,8 @@ }, "node_modules/stack-utils": { "version": "2.0.6", - "dev": true, + "resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-2.0.6.tgz", + "integrity": "sha512-XlkWvfIm6RmsWtNJx+uqtKLS8eqFbxUg0ZzLXqY0caEy9l7hruX8IpiDnjsLavoBgqCCR71TqWO8MaXYheJ3RQ==", "license": "MIT", "dependencies": { "escape-string-regexp": "^2.0.0" @@ -20519,18 +19141,43 @@ }, "node_modules/stack-utils/node_modules/escape-string-regexp": { "version": "2.0.0", - "dev": true, + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz", + "integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==", "license": "MIT", "engines": { "node": ">=8" } }, + "node_modules/stacktracey": { + "version": "2.1.8", + "resolved": "https://registry.npmjs.org/stacktracey/-/stacktracey-2.1.8.tgz", + "integrity": "sha512-Kpij9riA+UNg7TnphqjH7/CzctQ/owJGNbFkfEeve4Z4uxT5+JapVLFXcsurIfN34gnTWZNJ/f7NMG0E8JDzTw==", + "dev": true, + "license": "Unlicense", + "dependencies": { + "as-table": "^1.0.36", + "get-source": "^2.0.12" + } + }, + "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/stickyfill": { "version": "1.1.1", + "resolved": "https://registry.npmjs.org/stickyfill/-/stickyfill-1.1.1.tgz", + "integrity": "sha512-GCp7vHAfpao+Qh/3Flh9DXEJ/qSi0KJwJw6zYlZOtRYXWUIpMM6mC2rIep/dK8RQqwW0KxGJIllmjPIBOGN8AA==", "dev": true }, "node_modules/stop-iteration-iterator": { "version": "1.1.0", + "resolved": "https://registry.npmjs.org/stop-iteration-iterator/-/stop-iteration-iterator-1.1.0.tgz", + "integrity": "sha512-eLoXW/DHyl62zxY4SCaIgnRhuMr6ri4juEYARS8E6sCEqzKpOiE521Ucofdx+KnDZl5xmvGYaaKCk5FEOxJCoQ==", "dev": true, "license": "MIT", "dependencies": { @@ -20542,46 +19189,31 @@ } }, "node_modules/streamx": { - "version": "2.22.1", + "version": "2.23.0", + "resolved": "https://registry.npmjs.org/streamx/-/streamx-2.23.0.tgz", + "integrity": "sha512-kn+e44esVfn2Fa/O0CPFcex27fjIL6MkVae0Mm6q+E6f0hWv578YCERbv+4m02cjxvDsPKLnmxral/rR6lBMAg==", "dev": true, "license": "MIT", "dependencies": { + "events-universal": "^1.0.0", "fast-fifo": "^1.3.2", "text-decoder": "^1.1.0" - }, - "optionalDependencies": { - "bare-events": "^2.2.0" } }, "node_modules/string_decoder": { "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", "dev": true, "license": "MIT", "dependencies": { "safe-buffer": "~5.2.0" } }, - "node_modules/string_decoder/node_modules/safe-buffer": { - "version": "5.2.1", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "MIT" - }, "node_modules/string-length": { "version": "4.0.2", + "resolved": "https://registry.npmjs.org/string-length/-/string-length-4.0.2.tgz", + "integrity": "sha512-+l6rNN5fYHNhZZy41RXsYptCjA2Igmq4EG7kZAYFQI1E1VTXarr6ZPXBg6eq7Y6eK4FEhY6AJlyuFIb/v/S0VQ==", "dev": true, "license": "MIT", "dependencies": { @@ -20594,6 +19226,8 @@ }, "node_modules/string-width": { "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", "license": "MIT", "dependencies": { "emoji-regex": "^8.0.0", @@ -20607,6 +19241,8 @@ "node_modules/string-width-cjs": { "name": "string-width", "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", "dev": true, "license": "MIT", "dependencies": { @@ -20620,15 +19256,21 @@ }, "node_modules/string-width-cjs/node_modules/emoji-regex": { "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", "dev": true, "license": "MIT" }, "node_modules/string-width/node_modules/emoji-regex": { "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", "license": "MIT" }, "node_modules/string.prototype.includes": { "version": "2.0.1", + "resolved": "https://registry.npmjs.org/string.prototype.includes/-/string.prototype.includes-2.0.1.tgz", + "integrity": "sha512-o7+c9bW6zpAdJHTtujeePODAhkuicdAryFsfVKwA+wGw89wJ4GTY484WTucM9hLtDEOpOvI+aHnzqnC5lHp4Rg==", "dev": true, "license": "MIT", "dependencies": { @@ -20642,6 +19284,8 @@ }, "node_modules/string.prototype.matchall": { "version": "4.0.12", + "resolved": "https://registry.npmjs.org/string.prototype.matchall/-/string.prototype.matchall-4.0.12.tgz", + "integrity": "sha512-6CC9uyBL+/48dYizRf7H7VAYCMCNTBeM78x/VTUe9bFEaxBepPJDa1Ow99LqI/1yF7kuy7Q3cQsYMrcjGUcskA==", "dev": true, "license": "MIT", "dependencies": { @@ -20668,6 +19312,8 @@ }, "node_modules/string.prototype.repeat": { "version": "1.0.0", + "resolved": "https://registry.npmjs.org/string.prototype.repeat/-/string.prototype.repeat-1.0.0.tgz", + "integrity": "sha512-0u/TldDbKD8bFCQ/4f5+mNRrXwZ8hg2w7ZR8wa16e8z9XpePWl3eGEcUD0OXpEH/VJH/2G3gjUtR3ZOiBe2S/w==", "dev": true, "license": "MIT", "dependencies": { @@ -20677,6 +19323,8 @@ }, "node_modules/string.prototype.trim": { "version": "1.2.10", + "resolved": "https://registry.npmjs.org/string.prototype.trim/-/string.prototype.trim-1.2.10.tgz", + "integrity": "sha512-Rs66F0P/1kedk5lyYyH9uBzuiI/kNRmwJAR9quK6VOtIpZ2G+hMZd+HQbbv25MgCA6gEffoMZYxlTod4WcdrKA==", "dev": true, "license": "MIT", "dependencies": { @@ -20697,6 +19345,8 @@ }, "node_modules/string.prototype.trimend": { "version": "1.0.9", + "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.9.tgz", + "integrity": "sha512-G7Ok5C6E/j4SGfyLCloXTrngQIQU3PWtXGst3yM7Bea9FRURf1S42ZHlZZtsNque2FN2PoUhfZXYLNWwEr4dLQ==", "dev": true, "license": "MIT", "dependencies": { @@ -20714,6 +19364,8 @@ }, "node_modules/string.prototype.trimstart": { "version": "1.0.8", + "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.8.tgz", + "integrity": "sha512-UXSH262CSZY1tfu3G3Secr6uGLCFVPMhIqHjlgCUtCCcgihYc/xKs9djMTMUOb2j1mVSeU8EU6NWc/iQKU6Gfg==", "dev": true, "license": "MIT", "dependencies": { @@ -20730,6 +19382,8 @@ }, "node_modules/strip-ansi": { "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", "license": "MIT", "dependencies": { "ansi-regex": "^5.0.1" @@ -20741,6 +19395,8 @@ "node_modules/strip-ansi-cjs": { "name": "strip-ansi", "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", "dev": true, "license": "MIT", "dependencies": { @@ -20752,6 +19408,8 @@ }, "node_modules/strip-bom": { "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-4.0.0.tgz", + "integrity": "sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w==", "dev": true, "license": "MIT", "engines": { @@ -20760,6 +19418,8 @@ }, "node_modules/strip-final-newline": { "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", + "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==", "license": "MIT", "engines": { "node": ">=6" @@ -20767,6 +19427,8 @@ }, "node_modules/strip-indent": { "version": "4.1.1", + "resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-4.1.1.tgz", + "integrity": "sha512-SlyRoSkdh1dYP0PzclLE7r0M9sgbFKKMFXpFRUMNuKhQSbC6VQIGzq3E0qsfvGJaUFJPGv6Ws1NZ/haTAjfbMA==", "dev": true, "license": "MIT", "engines": { @@ -20778,6 +19440,8 @@ }, "node_modules/strip-json-comments": { "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", "dev": true, "license": "MIT", "engines": { @@ -20788,8 +19452,9 @@ } }, "node_modules/strnum": { - "version": "1.1.2", - "dev": true, + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/strnum/-/strnum-2.1.2.tgz", + "integrity": "sha512-l63NF9y/cLROq/yqKXSLtcMeeyOfnSQlfMSlzFt/K73oIaD8DGaQWd7Z34X9GPiKqP5rbSh84Hl4bOlLcjiSrQ==", "funding": [ { "type": "github", @@ -20800,6 +19465,8 @@ }, "node_modules/strtok3": { "version": "10.3.4", + "resolved": "https://registry.npmjs.org/strtok3/-/strtok3-10.3.4.tgz", + "integrity": "sha512-KIy5nylvC5le1OdaaoCJ07L+8iQzJHGH6pWDuzS+d07Cu7n1MZ2x26P8ZKIWfbK02+XIL8Mp4RkWeqdUCrDMfg==", "dev": true, "license": "MIT", "dependencies": { @@ -20815,6 +19482,8 @@ }, "node_modules/styled-components": { "version": "6.1.19", + "resolved": "https://registry.npmjs.org/styled-components/-/styled-components-6.1.19.tgz", + "integrity": "sha512-1v/e3Dl1BknC37cXMhwGomhO8AkYmN41CqyX9xhUDxry1ns3BFQy2lLDRQXJRdVVWB9OHemv/53xaStimvWyuA==", "dev": true, "license": "MIT", "dependencies": { @@ -20842,16 +19511,22 @@ }, "node_modules/styled-components/node_modules/tslib": { "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==", "dev": true, "license": "0BSD" }, "node_modules/stylis": { "version": "4.3.2", + "resolved": "https://registry.npmjs.org/stylis/-/stylis-4.3.2.tgz", + "integrity": "sha512-bhtUjWd/z6ltJiQwg0dUfxEJ+W+jdqQd8TbWLWyeIJHlnsqmGLRFFd8e5mA0AZi/zx90smXRlN66YMTcaSFifg==", "dev": true, "license": "MIT" }, "node_modules/supports-color": { "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", "license": "MIT", "dependencies": { "has-flag": "^4.0.0" @@ -20862,6 +19537,8 @@ }, "node_modules/supports-preserve-symlinks-flag": { "version": "1.0.0", + "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", + "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", "dev": true, "license": "MIT", "engines": { @@ -20873,6 +19550,8 @@ }, "node_modules/swagger2openapi": { "version": "7.0.8", + "resolved": "https://registry.npmjs.org/swagger2openapi/-/swagger2openapi-7.0.8.tgz", + "integrity": "sha512-upi/0ZGkYgEcLeGieoz8gT74oWHA0E7JivX7aN9mAf+Tc7BQoRBvnIGHoPDw+f9TXTW4s6kGYCZJtauP6OYp7g==", "dev": true, "license": "BSD-3-Clause", "dependencies": { @@ -20897,13 +19576,27 @@ "url": "https://github.com/Mermade/oas-kit?sponsor=1" } }, + "node_modules/swagger2openapi/node_modules/yaml": { + "version": "1.10.2", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz", + "integrity": "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">= 6" + } + }, "node_modules/symbol-tree": { "version": "3.2.4", + "resolved": "https://registry.npmjs.org/symbol-tree/-/symbol-tree-3.2.4.tgz", + "integrity": "sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==", "dev": true, "license": "MIT" }, "node_modules/synckit": { "version": "0.11.11", + "resolved": "https://registry.npmjs.org/synckit/-/synckit-0.11.11.tgz", + "integrity": "sha512-MeQTA1r0litLUf0Rp/iisCaL8761lKAZHaimlbGK4j0HysC4PLfqygQj9srcs0m2RdtDYnF8UuYyKpbjHYp7Jw==", "dev": true, "license": "MIT", "dependencies": { @@ -20918,6 +19611,8 @@ }, "node_modules/tar-fs": { "version": "3.1.1", + "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-3.1.1.tgz", + "integrity": "sha512-LZA0oaPOc2fVo82Txf3gw+AkEd38szODlptMYejQUhndHMLQ9M059uXR+AfS7DNo0NpINvSqDsvyaCrBVkptWg==", "dev": true, "license": "MIT", "dependencies": { @@ -20931,6 +19626,8 @@ }, "node_modules/tar-stream": { "version": "3.1.7", + "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-3.1.7.tgz", + "integrity": "sha512-qJj60CXt7IU1Ffyc3NJMjh6EkuCFej46zUqJ4J7pqYlThyd9bO0XBTmcOIhSzZJVWfsLks0+nle/j538YAW9RQ==", "dev": true, "license": "MIT", "dependencies": { @@ -20941,6 +19638,8 @@ }, "node_modules/test-exclude": { "version": "6.0.0", + "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-6.0.0.tgz", + "integrity": "sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==", "dev": true, "license": "ISC", "dependencies": { @@ -20954,6 +19653,8 @@ }, "node_modules/test-exclude/node_modules/brace-expansion": { "version": "1.1.12", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", + "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", "dev": true, "license": "MIT", "dependencies": { @@ -20963,6 +19664,9 @@ }, "node_modules/test-exclude/node_modules/glob": { "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "deprecated": "Glob versions prior to v9 are no longer supported", "dev": true, "license": "ISC", "dependencies": { @@ -20982,6 +19686,8 @@ }, "node_modules/test-exclude/node_modules/minimatch": { "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", "dev": true, "license": "ISC", "dependencies": { @@ -20992,29 +19698,33 @@ } }, "node_modules/testcontainers": { - "version": "11.5.1", + "version": "11.10.0", + "resolved": "https://registry.npmjs.org/testcontainers/-/testcontainers-11.10.0.tgz", + "integrity": "sha512-8hwK2EnrOZfrHPpDC7CPe03q7H8Vv8j3aXdcmFFyNV8dzpBzgZYmqyDtduJ8YQ5kbzj+A+jUXMQ6zI8B5U3z+g==", "dev": true, "license": "MIT", "dependencies": { "@balena/dockerignore": "^1.0.2", - "@types/dockerode": "^3.3.42", + "@types/dockerode": "^3.3.47", "archiver": "^7.0.1", "async-lock": "^1.4.1", "byline": "^5.0.0", - "debug": "^4.4.1", - "docker-compose": "^1.2.0", - "dockerode": "^4.0.7", + "debug": "^4.4.3", + "docker-compose": "^1.3.0", + "dockerode": "^4.0.9", "get-port": "^7.1.0", "proper-lockfile": "^4.1.2", "properties-reader": "^2.3.0", "ssh-remote-port-forward": "^1.0.4", - "tar-fs": "^3.1.0", - "tmp": "^0.2.4", - "undici": "^7.13.0" + "tar-fs": "^3.1.1", + "tmp": "^0.2.5", + "undici": "^7.16.0" } }, "node_modules/testcontainers/node_modules/undici": { - "version": "7.13.0", + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/undici/-/undici-7.16.0.tgz", + "integrity": "sha512-QEg3HPMll0o3t2ourKwOeUAZ159Kn9mx5pnzHRQO8+Wixmh88YdZRiIwat0iNzNNXn0yoEtXJqFpyW7eM8BV7g==", "dev": true, "license": "MIT", "engines": { @@ -21023,14 +19733,25 @@ }, "node_modules/text-decoder": { "version": "1.2.3", + "resolved": "https://registry.npmjs.org/text-decoder/-/text-decoder-1.2.3.tgz", + "integrity": "sha512-3/o9z3X0X0fTupwsYvR03pJ/DjWuqqrfwBgTQzdWDiQSm9KitAyz/9WqsT2JQW7KV2m+bC2ol/zqpW37NHxLaA==", "dev": true, "license": "Apache-2.0", "dependencies": { "b4a": "^1.6.4" } }, + "node_modules/text-table": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", + "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==", + "dev": true, + "license": "MIT" + }, "node_modules/thread-stream": { "version": "3.1.0", + "resolved": "https://registry.npmjs.org/thread-stream/-/thread-stream-3.1.0.tgz", + "integrity": "sha512-OqyPZ9u96VohAyMfJykzmivOrY2wfMSf3C5TtFJVgN+Hm6aj+voFhlK+kZEIv2FBh1X6Xp3DlnCOfEQ3B2J86A==", "license": "MIT", "dependencies": { "real-require": "^0.2.0" @@ -21038,16 +19759,20 @@ }, "node_modules/through": { "version": "2.3.8", + "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", + "integrity": "sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==", "dev": true, "license": "MIT" }, "node_modules/tinyglobby": { - "version": "0.2.14", + "version": "0.2.15", + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.15.tgz", + "integrity": "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==", "dev": true, "license": "MIT", "dependencies": { - "fdir": "^6.4.4", - "picomatch": "^4.0.2" + "fdir": "^6.5.0", + "picomatch": "^4.0.3" }, "engines": { "node": ">=12.0.0" @@ -21056,32 +19781,10 @@ "url": "https://github.com/sponsors/SuperchupuDev" } }, - "node_modules/tinyglobby/node_modules/fdir": { - "version": "6.4.6", - "dev": true, - "license": "MIT", - "peerDependencies": { - "picomatch": "^3 || ^4" - }, - "peerDependenciesMeta": { - "picomatch": { - "optional": true - } - } - }, - "node_modules/tinyglobby/node_modules/picomatch": { - "version": "4.0.3", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } - }, "node_modules/tldts": { "version": "6.1.86", + "resolved": "https://registry.npmjs.org/tldts/-/tldts-6.1.86.tgz", + "integrity": "sha512-WMi/OQ2axVTf/ykqCQgXiIct+mSQDFdH2fkwhPwgEwvJ1kSzZRiinb0zF2Xb8u4+OqPChmyI6MEu4EezNJz+FQ==", "dev": true, "license": "MIT", "dependencies": { @@ -21093,11 +19796,15 @@ }, "node_modules/tldts-core": { "version": "6.1.86", + "resolved": "https://registry.npmjs.org/tldts-core/-/tldts-core-6.1.86.tgz", + "integrity": "sha512-Je6p7pkk+KMzMv2XXKmAE3McmolOQFdxkKw0R8EYNr7sELW46JqnNeTX8ybPiQgvg1ymCoF8LXs5fzFaZvJPTA==", "dev": true, "license": "MIT" }, "node_modules/tmp": { "version": "0.2.5", + "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.5.tgz", + "integrity": "sha512-voyz6MApa1rQGUxT3E+BK7/ROe8itEx7vD8/HEvt4xwXucvQ5G5oeEiHkmHZJuBO21RpOf+YYm9MOivj709jow==", "dev": true, "license": "MIT", "engines": { @@ -21106,11 +19813,15 @@ }, "node_modules/tmpl": { "version": "1.0.5", + "resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.5.tgz", + "integrity": "sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw==", "dev": true, "license": "BSD-3-Clause" }, "node_modules/to-regex-range": { "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", "dev": true, "license": "MIT", "dependencies": { @@ -21120,8 +19831,19 @@ "node": ">=8.0" } }, + "node_modules/toidentifier": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", + "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", + "license": "MIT", + "engines": { + "node": ">=0.6" + } + }, "node_modules/token-types": { "version": "6.1.1", + "resolved": "https://registry.npmjs.org/token-types/-/token-types-6.1.1.tgz", + "integrity": "sha512-kh9LVIWH5CnL63Ipf0jhlBIy0UsrMj/NJDfpsy1SqOXlLKEVyXXYrnFxFT1yOOYVGBSApeVnjPw/sBz5BfEjAQ==", "dev": true, "license": "MIT", "dependencies": { @@ -21137,8 +19859,31 @@ "url": "https://github.com/sponsors/Borewit" } }, + "node_modules/token-types/node_modules/ieee754": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "BSD-3-Clause" + }, "node_modules/tough-cookie": { "version": "5.1.2", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-5.1.2.tgz", + "integrity": "sha512-FVDYdxtnj0G6Qm/DhNPSb8Ju59ULcup3tuJxkFb5K8Bv2pUXILbf0xZWU8PX8Ov19OXljbUyveOFwRMwkXzO+A==", "dev": true, "license": "BSD-3-Clause", "dependencies": { @@ -21150,25 +19895,48 @@ }, "node_modules/tr46": { "version": "5.1.1", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-5.1.1.tgz", + "integrity": "sha512-hdF5ZgjTqgAntKkklYw0R03MG2x/bSzTtkxmIRw/sTNV8YXsCJ1tfLAX23lhxhHJlEf3CRCOCGGWw3vI3GaSPw==", "dev": true, "license": "MIT", "dependencies": { "punycode": "^2.3.1" }, "engines": { - "node": ">=18" + "node": ">=18" + } + }, + "node_modules/tr46/node_modules/punycode": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" } }, "node_modules/tree-kill": { "version": "1.2.2", + "resolved": "https://registry.npmjs.org/tree-kill/-/tree-kill-1.2.2.tgz", + "integrity": "sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==", "dev": true, "license": "MIT", "bin": { "tree-kill": "cli.js" } }, + "node_modules/ts-algebra": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/ts-algebra/-/ts-algebra-1.2.2.tgz", + "integrity": "sha512-kloPhf1hq3JbCPOTYoOWDKxebWjNb2o/LKnNfkWhxVVisFFmMJPPdJeGoGmM+iRLyoXAR61e08Pb+vUXINg8aA==", + "dev": true, + "license": "MIT" + }, "node_modules/ts-api-utils": { "version": "2.1.0", + "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.1.0.tgz", + "integrity": "sha512-CUgTZL1irw8u29bzrOD/nH85jqyc74D6SshFgujOIA7osm2Rz7dYH77agkx7H4FBNxDq7Cjf+IjaX/8zwFW+ZQ==", "dev": true, "license": "MIT", "engines": { @@ -21180,6 +19948,8 @@ }, "node_modules/ts-essentials": { "version": "10.1.1", + "resolved": "https://registry.npmjs.org/ts-essentials/-/ts-essentials-10.1.1.tgz", + "integrity": "sha512-4aTB7KLHKmUvkjNj8V+EdnmuVTiECzn3K+zIbRthumvHu+j44x3w63xpfs0JL3NGIzGXqoQ7AV591xHO+XrOTw==", "dev": true, "license": "MIT", "peerDependencies": { @@ -21192,7 +19962,9 @@ } }, "node_modules/ts-jest": { - "version": "29.4.1", + "version": "29.4.6", + "resolved": "https://registry.npmjs.org/ts-jest/-/ts-jest-29.4.6.tgz", + "integrity": "sha512-fSpWtOO/1AjSNQguk43hb/JCo16oJDnMJf3CdEGNkqsEX3t0KX96xvyX1D7PfLCpVoKu4MfVrqUkFyblYoY4lA==", "dev": true, "license": "MIT", "dependencies": { @@ -21202,7 +19974,7 @@ "json5": "^2.2.3", "lodash.memoize": "^4.1.2", "make-error": "^1.3.6", - "semver": "^7.7.2", + "semver": "^7.7.3", "type-fest": "^4.41.0", "yargs-parser": "^21.1.1" }, @@ -21244,6 +20016,8 @@ }, "node_modules/ts-jest/node_modules/type-fest": { "version": "4.41.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-4.41.0.tgz", + "integrity": "sha512-TeTSQ6H5YHvpqVwBRcnLDCBnDOHWYu7IvGbHT6N8AOymcr9PJGjc1GTtiWZTYg0NCgYwvnYWEkVChQAr9bjfwA==", "dev": true, "license": "(MIT OR CC0-1.0)", "engines": { @@ -21255,6 +20029,8 @@ }, "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==", "dev": true, "license": "MIT", "dependencies": { @@ -21297,11 +20073,15 @@ }, "node_modules/ts-node/node_modules/arg": { "version": "4.1.3", + "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", + "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==", "dev": true, "license": "MIT" }, "node_modules/tsconfig-paths": { "version": "3.15.0", + "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.15.0.tgz", + "integrity": "sha512-2Ac2RgzDe/cn48GvOe3M+o82pEFewD3UPbyoUHHdKasHwJKjds4fLXWf/Ux5kATBKN20oaFGu+jbElp1pos0mg==", "dev": true, "license": "MIT", "dependencies": { @@ -21313,6 +20093,8 @@ }, "node_modules/tsconfig-paths/node_modules/json5": { "version": "1.0.2", + "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.2.tgz", + "integrity": "sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==", "dev": true, "license": "MIT", "dependencies": { @@ -21324,6 +20106,8 @@ }, "node_modules/tsconfig-paths/node_modules/strip-bom": { "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", + "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==", "dev": true, "license": "MIT", "engines": { @@ -21332,16 +20116,18 @@ }, "node_modules/tslib": { "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", "license": "0BSD" }, "node_modules/tsx": { - "version": "4.20.6", - "resolved": "https://registry.npmjs.org/tsx/-/tsx-4.20.6.tgz", - "integrity": "sha512-ytQKuwgmrrkDTFP4LjR0ToE2nqgy886GpvRSpU0JAnrdBYppuY5rLkRUYPU1yCryb24SsKBTL/hlDQAEFVwtZg==", + "version": "4.21.0", + "resolved": "https://registry.npmjs.org/tsx/-/tsx-4.21.0.tgz", + "integrity": "sha512-5C1sg4USs1lfG0GFb2RLXsdpXqBSEhAaA/0kPL01wxzpMqLILNxIxIOKiILz+cdg/pLnOUxFYOR5yhHU666wbw==", "dev": true, "license": "MIT", "dependencies": { - "esbuild": "~0.25.0", + "esbuild": "~0.27.0", "get-tsconfig": "^4.7.5" }, "bin": { @@ -21354,520 +20140,574 @@ "fsevents": "~2.3.3" } }, - "node_modules/tweetnacl": { - "version": "0.14.5", - "dev": true, - "license": "Unlicense" - }, - "node_modules/type-check": { - "version": "0.4.0", - "dev": true, - "license": "MIT", - "dependencies": { - "prelude-ls": "^1.2.1" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/type-detect": { - "version": "4.0.8", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=4" - } - }, - "node_modules/type-fest": { - "version": "0.21.3", - "dev": true, - "license": "(MIT OR CC0-1.0)", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/typed-array-buffer": { - "version": "1.0.3", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bound": "^1.0.3", - "es-errors": "^1.3.0", - "is-typed-array": "^1.1.14" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/typed-array-byte-length": { - "version": "1.0.3", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bind": "^1.0.8", - "for-each": "^0.3.3", - "gopd": "^1.2.0", - "has-proto": "^1.2.0", - "is-typed-array": "^1.1.14" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/typed-array-byte-offset": { - "version": "1.0.4", - "dev": true, - "license": "MIT", - "dependencies": { - "available-typed-arrays": "^1.0.7", - "call-bind": "^1.0.8", - "for-each": "^0.3.3", - "gopd": "^1.2.0", - "has-proto": "^1.2.0", - "is-typed-array": "^1.1.15", - "reflect.getprototypeof": "^1.0.9" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/typed-array-length": { - "version": "1.0.7", + "node_modules/tsx/node_modules/@esbuild/aix-ppc64": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.27.2.tgz", + "integrity": "sha512-GZMB+a0mOMZs4MpDbj8RJp4cw+w1WV5NYD6xzgvzUJ5Ek2jerwfO2eADyI6ExDSUED+1X8aMbegahsJi+8mgpw==", + "cpu": [ + "ppc64" + ], "dev": true, "license": "MIT", - "dependencies": { - "call-bind": "^1.0.7", - "for-each": "^0.3.3", - "gopd": "^1.0.1", - "is-typed-array": "^1.1.13", - "possible-typed-array-names": "^1.0.0", - "reflect.getprototypeof": "^1.0.6" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/typedarray": { - "version": "0.0.6", - "dev": true, - "license": "MIT" - }, - "node_modules/typescript": { - "version": "5.9.2", - "dev": true, - "license": "Apache-2.0", - "bin": { - "tsc": "bin/tsc", - "tsserver": "bin/tsserver" - }, - "engines": { - "node": ">=14.17" - } - }, - "node_modules/uglify-js": { - "version": "3.19.3", - "dev": true, - "license": "BSD-2-Clause", "optional": true, - "bin": { - "uglifyjs": "bin/uglifyjs" - }, - "engines": { - "node": ">=0.8.0" - } - }, - "node_modules/uid": { - "version": "2.0.2", - "dev": true, - "license": "MIT", - "dependencies": { - "@lukeed/csprng": "^1.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/uint8array-extras": { - "version": "1.4.1", - "dev": true, - "license": "MIT", + "os": [ + "aix" + ], "engines": { "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/unbox-primitive": { - "version": "1.1.0", + "node_modules/tsx/node_modules/@esbuild/android-arm": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.27.2.tgz", + "integrity": "sha512-DVNI8jlPa7Ujbr1yjU2PfUSRtAUZPG9I1RwW4F4xFB1Imiu2on0ADiI/c3td+KmDtVKNbi+nffGDQMfcIMkwIA==", + "cpu": [ + "arm" + ], "dev": true, "license": "MIT", - "dependencies": { - "call-bound": "^1.0.3", - "has-bigints": "^1.0.2", - "has-symbols": "^1.1.0", - "which-boxed-primitive": "^1.1.1" - }, + "optional": true, + "os": [ + "android" + ], "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": ">=18" } }, - "node_modules/undici": { - "version": "6.21.3", + "node_modules/tsx/node_modules/@esbuild/android-arm64": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.27.2.tgz", + "integrity": "sha512-pvz8ZZ7ot/RBphf8fv60ljmaoydPU12VuXHImtAs0XhLLw+EXBi2BLe3OYSBslR4rryHvweW5gmkKFwTiFy6KA==", + "cpu": [ + "arm64" + ], "dev": true, "license": "MIT", + "optional": true, + "os": [ + "android" + ], "engines": { - "node": ">=18.17" + "node": ">=18" } }, - "node_modules/undici-types": { - "version": "7.10.0", - "dev": true, - "license": "MIT" - }, - "node_modules/universalify": { - "version": "2.0.1", + "node_modules/tsx/node_modules/@esbuild/android-x64": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.27.2.tgz", + "integrity": "sha512-z8Ank4Byh4TJJOh4wpz8g2vDy75zFL0TlZlkUkEwYXuPSgX8yzep596n6mT7905kA9uHZsf/o2OJZubl2l3M7A==", + "cpu": [ + "x64" + ], "dev": true, "license": "MIT", + "optional": true, + "os": [ + "android" + ], "engines": { - "node": ">= 10.0.0" - } - }, - "node_modules/unrs-resolver": { - "version": "1.11.1", - "dev": true, - "hasInstallScript": true, - "license": "MIT", - "dependencies": { - "napi-postinstall": "^0.3.0" - }, - "funding": { - "url": "https://opencollective.com/unrs-resolver" - }, - "optionalDependencies": { - "@unrs/resolver-binding-android-arm-eabi": "1.11.1", - "@unrs/resolver-binding-android-arm64": "1.11.1", - "@unrs/resolver-binding-darwin-arm64": "1.11.1", - "@unrs/resolver-binding-darwin-x64": "1.11.1", - "@unrs/resolver-binding-freebsd-x64": "1.11.1", - "@unrs/resolver-binding-linux-arm-gnueabihf": "1.11.1", - "@unrs/resolver-binding-linux-arm-musleabihf": "1.11.1", - "@unrs/resolver-binding-linux-arm64-gnu": "1.11.1", - "@unrs/resolver-binding-linux-arm64-musl": "1.11.1", - "@unrs/resolver-binding-linux-ppc64-gnu": "1.11.1", - "@unrs/resolver-binding-linux-riscv64-gnu": "1.11.1", - "@unrs/resolver-binding-linux-riscv64-musl": "1.11.1", - "@unrs/resolver-binding-linux-s390x-gnu": "1.11.1", - "@unrs/resolver-binding-linux-x64-gnu": "1.11.1", - "@unrs/resolver-binding-linux-x64-musl": "1.11.1", - "@unrs/resolver-binding-wasm32-wasi": "1.11.1", - "@unrs/resolver-binding-win32-arm64-msvc": "1.11.1", - "@unrs/resolver-binding-win32-ia32-msvc": "1.11.1", - "@unrs/resolver-binding-win32-x64-msvc": "1.11.1" + "node": ">=18" } }, - "node_modules/update-browserslist-db": { - "version": "1.1.4", - "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/browserslist" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } + "node_modules/tsx/node_modules/@esbuild/darwin-arm64": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.27.2.tgz", + "integrity": "sha512-davCD2Zc80nzDVRwXTcQP/28fiJbcOwvdolL0sOiOsbwBa72kegmVU0Wrh1MYrbuCL98Omp5dVhQFWRKR2ZAlg==", + "cpu": [ + "arm64" ], + "dev": true, "license": "MIT", - "dependencies": { - "escalade": "^3.2.0", - "picocolors": "^1.1.1" - }, - "bin": { - "update-browserslist-db": "cli.js" - }, - "peerDependencies": { - "browserslist": ">= 4.21.0" + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" } }, - "node_modules/update-check": { - "version": "1.5.4", + "node_modules/tsx/node_modules/@esbuild/darwin-x64": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.27.2.tgz", + "integrity": "sha512-ZxtijOmlQCBWGwbVmwOF/UCzuGIbUkqB1faQRf5akQmxRJ1ujusWsb3CVfk/9iZKr2L5SMU5wPBi1UWbvL+VQA==", + "cpu": [ + "x64" + ], + "dev": true, "license": "MIT", - "dependencies": { - "registry-auth-token": "3.3.2", - "registry-url": "3.1.0" + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" } }, - "node_modules/uri-js": { - "version": "4.4.1", - "license": "BSD-2-Clause", - "dependencies": { - "punycode": "^2.1.0" + "node_modules/tsx/node_modules/@esbuild/freebsd-arm64": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.27.2.tgz", + "integrity": "sha512-lS/9CN+rgqQ9czogxlMcBMGd+l8Q3Nj1MFQwBZJyoEKI50XGxwuzznYdwcav6lpOGv5BqaZXqvBSiB/kJ5op+g==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" } }, - "node_modules/uri-js-replace": { - "version": "1.0.1", + "node_modules/tsx/node_modules/@esbuild/freebsd-x64": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.27.2.tgz", + "integrity": "sha512-tAfqtNYb4YgPnJlEFu4c212HYjQWSO/w/h/lQaBK7RbwGIkBOuNKQI9tqWzx7Wtp7bTPaGC6MJvWI608P3wXYA==", + "cpu": [ + "x64" + ], "dev": true, - "license": "MIT" + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } }, - "node_modules/url-template": { - "version": "2.0.8", + "node_modules/tsx/node_modules/@esbuild/linux-arm": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.27.2.tgz", + "integrity": "sha512-vWfq4GaIMP9AIe4yj1ZUW18RDhx6EPQKjwe7n8BbIecFtCQG4CfHGaHuh7fdfq+y3LIA2vGS/o9ZBGVxIDi9hw==", + "cpu": [ + "arm" + ], "dev": true, - "license": "BSD" + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } }, - "node_modules/use-sync-external-store": { - "version": "1.5.0", + "node_modules/tsx/node_modules/@esbuild/linux-arm64": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.27.2.tgz", + "integrity": "sha512-hYxN8pr66NsCCiRFkHUAsxylNOcAQaxSSkHMMjcpx0si13t1LHFphxJZUiGwojB1a/Hd5OiPIqDdXONia6bhTw==", + "cpu": [ + "arm64" + ], "dev": true, "license": "MIT", - "peerDependencies": { - "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" } }, - "node_modules/util-deprecate": { - "version": "1.0.2", + "node_modules/tsx/node_modules/@esbuild/linux-ia32": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.27.2.tgz", + "integrity": "sha512-MJt5BRRSScPDwG2hLelYhAAKh9imjHK5+NE/tvnRLbIqUWa+0E9N4WNMjmp/kXXPHZGqPLxggwVhz7QP8CTR8w==", + "cpu": [ + "ia32" + ], "dev": true, - "license": "MIT" + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } }, - "node_modules/uuid": { - "version": "9.0.1", - "funding": [ - "https://github.com/sponsors/broofa", - "https://github.com/sponsors/ctavan" + "node_modules/tsx/node_modules/@esbuild/linux-loong64": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.27.2.tgz", + "integrity": "sha512-lugyF1atnAT463aO6KPshVCJK5NgRnU4yb3FUumyVz+cGvZbontBgzeGFO1nF+dPueHD367a2ZXe1NtUkAjOtg==", + "cpu": [ + "loong64" ], + "dev": true, "license": "MIT", - "bin": { - "uuid": "dist/bin/uuid" + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" } }, - "node_modules/v8-compile-cache-lib": { - "version": "3.0.1", + "node_modules/tsx/node_modules/@esbuild/linux-mips64el": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.27.2.tgz", + "integrity": "sha512-nlP2I6ArEBewvJ2gjrrkESEZkB5mIoaTswuqNFRv/WYd+ATtUpe9Y09RnJvgvdag7he0OWgEZWhviS1OTOKixw==", + "cpu": [ + "mips64el" + ], "dev": true, - "license": "MIT" + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } }, - "node_modules/v8-to-istanbul": { - "version": "9.3.0", + "node_modules/tsx/node_modules/@esbuild/linux-ppc64": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.27.2.tgz", + "integrity": "sha512-C92gnpey7tUQONqg1n6dKVbx3vphKtTHJaNG2Ok9lGwbZil6DrfyecMsp9CrmXGQJmZ7iiVXvvZH6Ml5hL6XdQ==", + "cpu": [ + "ppc64" + ], "dev": true, - "license": "ISC", - "dependencies": { - "@jridgewell/trace-mapping": "^0.3.12", - "@types/istanbul-lib-coverage": "^2.0.1", - "convert-source-map": "^2.0.0" - }, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], "engines": { - "node": ">=10.12.0" + "node": ">=18" } }, - "node_modules/vary": { - "version": "1.1.2", + "node_modules/tsx/node_modules/@esbuild/linux-riscv64": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.27.2.tgz", + "integrity": "sha512-B5BOmojNtUyN8AXlK0QJyvjEZkWwy/FKvakkTDCziX95AowLZKR6aCDhG7LeF7uMCXEJqwa8Bejz5LTPYm8AvA==", + "cpu": [ + "riscv64" + ], + "dev": true, "license": "MIT", + "optional": true, + "os": [ + "linux" + ], "engines": { - "node": ">= 0.8" + "node": ">=18" } }, - "node_modules/vscode-json-languageservice": { - "version": "4.2.1", + "node_modules/tsx/node_modules/@esbuild/linux-s390x": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.27.2.tgz", + "integrity": "sha512-p4bm9+wsPwup5Z8f4EpfN63qNagQ47Ua2znaqGH6bqLlmJ4bx97Y9JdqxgGZ6Y8xVTixUnEkoKSHcpRlDnNr5w==", + "cpu": [ + "s390x" + ], "dev": true, "license": "MIT", - "dependencies": { - "jsonc-parser": "^3.0.0", - "vscode-languageserver-textdocument": "^1.0.3", - "vscode-languageserver-types": "^3.16.0", - "vscode-nls": "^5.0.0", - "vscode-uri": "^3.0.3" + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" } }, - "node_modules/vscode-languageserver-textdocument": { - "version": "1.0.12", + "node_modules/tsx/node_modules/@esbuild/linux-x64": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.27.2.tgz", + "integrity": "sha512-uwp2Tip5aPmH+NRUwTcfLb+W32WXjpFejTIOWZFw/v7/KnpCDKG66u4DLcurQpiYTiYwQ9B7KOeMJvLCu/OvbA==", + "cpu": [ + "x64" + ], "dev": true, - "license": "MIT" + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } }, - "node_modules/vscode-languageserver-types": { - "version": "3.17.5", + "node_modules/tsx/node_modules/@esbuild/netbsd-arm64": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.27.2.tgz", + "integrity": "sha512-Kj6DiBlwXrPsCRDeRvGAUb/LNrBASrfqAIok+xB0LxK8CHqxZ037viF13ugfsIpePH93mX7xfJp97cyDuTZ3cw==", + "cpu": [ + "arm64" + ], "dev": true, - "license": "MIT" + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } }, - "node_modules/vscode-nls": { - "version": "5.2.0", + "node_modules/tsx/node_modules/@esbuild/netbsd-x64": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.27.2.tgz", + "integrity": "sha512-HwGDZ0VLVBY3Y+Nw0JexZy9o/nUAWq9MlV7cahpaXKW6TOzfVno3y3/M8Ga8u8Yr7GldLOov27xiCnqRZf0tCA==", + "cpu": [ + "x64" + ], "dev": true, - "license": "MIT" + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } }, - "node_modules/vscode-uri": { - "version": "3.1.0", + "node_modules/tsx/node_modules/@esbuild/openbsd-arm64": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.27.2.tgz", + "integrity": "sha512-DNIHH2BPQ5551A7oSHD0CKbwIA/Ox7+78/AWkbS5QoRzaqlev2uFayfSxq68EkonB+IKjiuxBFoV8ESJy8bOHA==", + "cpu": [ + "arm64" + ], "dev": true, - "license": "MIT" + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } }, - "node_modules/w3c-xmlserializer": { - "version": "5.0.0", + "node_modules/tsx/node_modules/@esbuild/openbsd-x64": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.27.2.tgz", + "integrity": "sha512-/it7w9Nb7+0KFIzjalNJVR5bOzA9Vay+yIPLVHfIQYG/j+j9VTH84aNB8ExGKPU4AzfaEvN9/V4HV+F+vo8OEg==", + "cpu": [ + "x64" + ], "dev": true, "license": "MIT", - "dependencies": { - "xml-name-validator": "^5.0.0" - }, + "optional": true, + "os": [ + "openbsd" + ], "engines": { "node": ">=18" } }, - "node_modules/walker": { - "version": "1.0.8", + "node_modules/tsx/node_modules/@esbuild/openharmony-arm64": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.27.2.tgz", + "integrity": "sha512-LRBbCmiU51IXfeXk59csuX/aSaToeG7w48nMwA6049Y4J4+VbWALAuXcs+qcD04rHDuSCSRKdmY63sruDS5qag==", + "cpu": [ + "arm64" + ], "dev": true, - "license": "Apache-2.0", - "dependencies": { - "makeerror": "1.0.12" + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ], + "engines": { + "node": ">=18" } }, - "node_modules/wcwidth": { - "version": "1.0.1", + "node_modules/tsx/node_modules/@esbuild/sunos-x64": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.27.2.tgz", + "integrity": "sha512-kMtx1yqJHTmqaqHPAzKCAkDaKsffmXkPHThSfRwZGyuqyIeBvf08KSsYXl+abf5HDAPMJIPnbBfXvP2ZC2TfHg==", + "cpu": [ + "x64" + ], "dev": true, "license": "MIT", - "dependencies": { - "defaults": "^1.0.3" + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=18" } }, - "node_modules/webidl-conversions": { - "version": "7.0.0", + "node_modules/tsx/node_modules/@esbuild/win32-arm64": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.27.2.tgz", + "integrity": "sha512-Yaf78O/B3Kkh+nKABUF++bvJv5Ijoy9AN1ww904rOXZFLWVc5OLOfL56W+C8F9xn5JQZa3UX6m+IktJnIb1Jjg==", + "cpu": [ + "arm64" + ], "dev": true, - "license": "BSD-2-Clause", + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], "engines": { - "node": ">=12" + "node": ">=18" } }, - "node_modules/whatwg-encoding": { - "version": "3.1.1", + "node_modules/tsx/node_modules/@esbuild/win32-ia32": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.27.2.tgz", + "integrity": "sha512-Iuws0kxo4yusk7sw70Xa2E2imZU5HoixzxfGCdxwBdhiDgt9vX9VUCBhqcwY7/uh//78A1hMkkROMJq9l27oLQ==", + "cpu": [ + "ia32" + ], "dev": true, "license": "MIT", - "dependencies": { - "iconv-lite": "0.6.3" - }, + "optional": true, + "os": [ + "win32" + ], "engines": { "node": ">=18" } }, - "node_modules/whatwg-mimetype": { - "version": "4.0.0", + "node_modules/tsx/node_modules/@esbuild/win32-x64": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.27.2.tgz", + "integrity": "sha512-sRdU18mcKf7F+YgheI/zGf5alZatMUTKj/jNS6l744f9u3WFu4v7twcUI9vu4mknF4Y9aDlblIie0IM+5xxaqQ==", + "cpu": [ + "x64" + ], "dev": true, "license": "MIT", + "optional": true, + "os": [ + "win32" + ], "engines": { "node": ">=18" } }, - "node_modules/whatwg-url": { - "version": "14.2.0", + "node_modules/tsx/node_modules/esbuild": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.27.2.tgz", + "integrity": "sha512-HyNQImnsOC7X9PMNaCIeAm4ISCQXs5a5YasTXVliKv4uuBo1dKrG0A+uQS8M5eXjVMnLg3WgXaKvprHlFJQffw==", "dev": true, + "hasInstallScript": true, "license": "MIT", - "dependencies": { - "tr46": "^5.1.0", - "webidl-conversions": "^7.0.0" + "bin": { + "esbuild": "bin/esbuild" }, "engines": { "node": ">=18" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.27.2", + "@esbuild/android-arm": "0.27.2", + "@esbuild/android-arm64": "0.27.2", + "@esbuild/android-x64": "0.27.2", + "@esbuild/darwin-arm64": "0.27.2", + "@esbuild/darwin-x64": "0.27.2", + "@esbuild/freebsd-arm64": "0.27.2", + "@esbuild/freebsd-x64": "0.27.2", + "@esbuild/linux-arm": "0.27.2", + "@esbuild/linux-arm64": "0.27.2", + "@esbuild/linux-ia32": "0.27.2", + "@esbuild/linux-loong64": "0.27.2", + "@esbuild/linux-mips64el": "0.27.2", + "@esbuild/linux-ppc64": "0.27.2", + "@esbuild/linux-riscv64": "0.27.2", + "@esbuild/linux-s390x": "0.27.2", + "@esbuild/linux-x64": "0.27.2", + "@esbuild/netbsd-arm64": "0.27.2", + "@esbuild/netbsd-x64": "0.27.2", + "@esbuild/openbsd-arm64": "0.27.2", + "@esbuild/openbsd-x64": "0.27.2", + "@esbuild/openharmony-arm64": "0.27.2", + "@esbuild/sunos-x64": "0.27.2", + "@esbuild/win32-arm64": "0.27.2", + "@esbuild/win32-ia32": "0.27.2", + "@esbuild/win32-x64": "0.27.2" } }, - "node_modules/which": { - "version": "2.0.2", - "license": "ISC", + "node_modules/tweetnacl": { + "version": "0.14.5", + "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", + "integrity": "sha512-KXXFFdAbFXY4geFIwoyNK+f5Z1b7swfXABfL7HXCmoIWMKU3dmS26672A4EeQtDzLKy7SXmfBu51JolvEKwtGA==", + "dev": true, + "license": "Unlicense" + }, + "node_modules/type-check": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", + "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", + "dev": true, + "license": "MIT", "dependencies": { - "isexe": "^2.0.0" - }, - "bin": { - "node-which": "bin/node-which" + "prelude-ls": "^1.2.1" }, "engines": { - "node": ">= 8" + "node": ">= 0.8.0" } }, - "node_modules/which-boxed-primitive": { - "version": "1.1.1", + "node_modules/type-detect": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", + "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==", "dev": true, "license": "MIT", - "dependencies": { - "is-bigint": "^1.1.0", - "is-boolean-object": "^1.2.1", - "is-number-object": "^1.1.1", - "is-string": "^1.1.1", - "is-symbol": "^1.1.1" - }, "engines": { - "node": ">= 0.4" + "node": ">=4" + } + }, + "node_modules/type-fest": { + "version": "0.21.3", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz", + "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==", + "dev": true, + "license": "(MIT OR CC0-1.0)", + "engines": { + "node": ">=10" }, "funding": { - "url": "https://github.com/sponsors/ljharb" + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/which-builtin-type": { - "version": "1.2.1", - "dev": true, + "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": { - "call-bound": "^1.0.2", - "function.prototype.name": "^1.1.6", - "has-tostringtag": "^1.0.2", - "is-async-function": "^2.0.0", - "is-date-object": "^1.1.0", - "is-finalizationregistry": "^1.1.0", - "is-generator-function": "^1.0.10", - "is-regex": "^1.2.1", - "is-weakref": "^1.0.2", - "isarray": "^2.0.5", - "which-boxed-primitive": "^1.1.0", - "which-collection": "^1.0.2", - "which-typed-array": "^1.1.16" + "content-type": "^1.0.5", + "media-typer": "^1.1.0", + "mime-types": "^3.0.0" }, "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": ">= 0.6" } }, - "node_modules/which-collection": { - "version": "1.0.2", + "node_modules/typed-array-buffer": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/typed-array-buffer/-/typed-array-buffer-1.0.3.tgz", + "integrity": "sha512-nAYYwfY3qnzX30IkA6AQZjVbtK6duGontcQm1WSG1MD94YLqK0515GNApXkoxKOWMusVssAHWLh9SeaoefYFGw==", "dev": true, "license": "MIT", "dependencies": { - "is-map": "^2.0.3", - "is-set": "^2.0.3", - "is-weakmap": "^2.0.2", - "is-weakset": "^2.0.3" + "call-bound": "^1.0.3", + "es-errors": "^1.3.0", + "is-typed-array": "^1.1.14" }, "engines": { "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/which-typed-array": { - "version": "1.1.19", + "node_modules/typed-array-byte-length": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/typed-array-byte-length/-/typed-array-byte-length-1.0.3.tgz", + "integrity": "sha512-BaXgOuIxz8n8pIq3e7Atg/7s+DpiYrxn4vdot3w9KbnBhcRQq6o3xemQdIfynqSeXeDrF32x+WvfzmOjPiY9lg==", "dev": true, "license": "MIT", "dependencies": { - "available-typed-arrays": "^1.0.7", "call-bind": "^1.0.8", - "call-bound": "^1.0.4", - "for-each": "^0.3.5", - "get-proto": "^1.0.1", + "for-each": "^0.3.3", "gopd": "^1.2.0", - "has-tostringtag": "^1.0.2" + "has-proto": "^1.2.0", + "is-typed-array": "^1.1.14" }, "engines": { "node": ">= 0.4" @@ -21876,144 +20716,120 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/widest-line": { - "version": "4.0.1", - "license": "MIT", - "dependencies": { - "string-width": "^5.0.1" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/widest-line/node_modules/ansi-regex": { - "version": "6.1.0", - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/ansi-regex?sponsor=1" - } - }, - "node_modules/widest-line/node_modules/string-width": { - "version": "5.1.2", + "node_modules/typed-array-byte-offset": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/typed-array-byte-offset/-/typed-array-byte-offset-1.0.4.tgz", + "integrity": "sha512-bTlAFB/FBYMcuX81gbL4OcpH5PmlFHqlCCpAl8AlEzMz5k53oNDvN8p1PNOWLEmI2x4orp3raOFB51tv9X+MFQ==", + "dev": true, "license": "MIT", "dependencies": { - "eastasianwidth": "^0.2.0", - "emoji-regex": "^9.2.2", - "strip-ansi": "^7.0.1" + "available-typed-arrays": "^1.0.7", + "call-bind": "^1.0.8", + "for-each": "^0.3.3", + "gopd": "^1.2.0", + "has-proto": "^1.2.0", + "is-typed-array": "^1.1.15", + "reflect.getprototypeof": "^1.0.9" }, "engines": { - "node": ">=12" + "node": ">= 0.4" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/widest-line/node_modules/strip-ansi": { - "version": "7.1.0", + "node_modules/typed-array-length": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/typed-array-length/-/typed-array-length-1.0.7.tgz", + "integrity": "sha512-3KS2b+kL7fsuk/eJZ7EQdnEmQoaho/r6KUef7hxvltNA5DR8NAUM+8wJMbJyZ4G9/7i3v5zPBIMN5aybAh2/Jg==", + "dev": true, "license": "MIT", "dependencies": { - "ansi-regex": "^6.0.1" + "call-bind": "^1.0.7", + "for-each": "^0.3.3", + "gopd": "^1.0.1", + "is-typed-array": "^1.1.13", + "possible-typed-array-names": "^1.0.0", + "reflect.getprototypeof": "^1.0.6" }, "engines": { - "node": ">=12" + "node": ">= 0.4" }, "funding": { - "url": "https://github.com/chalk/strip-ansi?sponsor=1" - } - }, - "node_modules/word-wrap": { - "version": "1.2.5", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/wordwrap": { - "version": "1.0.0", - "dev": true, - "license": "MIT" - }, - "node_modules/wrap-ansi": { - "version": "6.2.0", + "node_modules/typescript": { + "version": "5.9.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", + "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", "dev": true, - "license": "MIT", - "dependencies": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" }, "engines": { - "node": ">=8" + "node": ">=14.17" } }, - "node_modules/wrap-ansi-cjs": { - "name": "wrap-ansi", - "version": "7.0.0", + "node_modules/typescript-eslint": { + "version": "8.50.0", + "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.50.0.tgz", + "integrity": "sha512-Q1/6yNUmCpH94fbgMUMg2/BSAr/6U7GBk61kZTv1/asghQOWOjTlp9K8mixS5NcJmm2creY+UFfGeW/+OcA64A==", "dev": true, "license": "MIT", "dependencies": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" + "@typescript-eslint/eslint-plugin": "8.50.0", + "@typescript-eslint/parser": "8.50.0", + "@typescript-eslint/typescript-estree": "8.50.0", + "@typescript-eslint/utils": "8.50.0" }, "engines": { - "node": ">=10" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" - } - }, - "node_modules/wrappy": { - "version": "1.0.2", - "dev": true, - "license": "ISC" + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0", + "typescript": ">=4.8.4 <6.0.0" + } }, - "node_modules/write-file-atomic": { - "version": "5.0.1", + "node_modules/uglify-js": { + "version": "3.19.3", + "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.19.3.tgz", + "integrity": "sha512-v3Xu+yuwBXisp6QYTcH4UbH+xYJXqnq2m/LtQVWKWzYc1iehYnLixoQDN9FH6/j9/oybfd6W9Ghwkl8+UMKTKQ==", "dev": true, - "license": "ISC", - "dependencies": { - "imurmurhash": "^0.1.4", - "signal-exit": "^4.0.1" + "license": "BSD-2-Clause", + "optional": true, + "bin": { + "uglifyjs": "bin/uglifyjs" }, "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + "node": ">=0.8.0" } }, - "node_modules/ws": { - "version": "8.18.3", + "node_modules/uid": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/uid/-/uid-2.0.2.tgz", + "integrity": "sha512-u3xV3X7uzvi5b1MncmZo3i2Aw222Zk1keqLA1YkHldREkAhAqi65wuPfe7lHx8H/Wzy+8CE7S7uS3jekIM5s8g==", "dev": true, "license": "MIT", - "engines": { - "node": ">=10.0.0" - }, - "peerDependencies": { - "bufferutil": "^4.0.1", - "utf-8-validate": ">=5.0.2" + "dependencies": { + "@lukeed/csprng": "^1.0.0" }, - "peerDependenciesMeta": { - "bufferutil": { - "optional": true - }, - "utf-8-validate": { - "optional": true - } + "engines": { + "node": ">=8" } }, - "node_modules/wsl-utils": { - "version": "0.1.0", + "node_modules/uint8array-extras": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/uint8array-extras/-/uint8array-extras-1.5.0.tgz", + "integrity": "sha512-rvKSBiC5zqCCiDZ9kAOszZcDvdAHwwIKJG33Ykj43OKcWsnmcBRL09YTU4nOeHZ8Y2a7l1MgTd08SBe9A8Qj6A==", "dev": true, "license": "MIT", - "dependencies": { - "is-wsl": "^3.1.0" - }, "engines": { "node": ">=18" }, @@ -22021,1089 +20837,1093 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/wsl-utils/node_modules/is-wsl": { - "version": "3.1.0", + "node_modules/ulid": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/ulid/-/ulid-3.0.2.tgz", + "integrity": "sha512-yu26mwteFYzBAot7KVMqFGCVpsF6g8wXfJzQUHvu1no3+rRRSFcSV2nKeYvNPLD2J4b08jYBDhHUjeH0ygIl9w==", + "dev": true, + "license": "MIT", + "bin": { + "ulid": "dist/cli.js" + } + }, + "node_modules/unbox-primitive": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.1.0.tgz", + "integrity": "sha512-nWJ91DjeOkej/TA8pXQ3myruKpKEYgqvpw9lz4OPHj/NWFNluYrjbz9j01CJ8yKQd2g4jFoOkINCTW2I5LEEyw==", "dev": true, "license": "MIT", "dependencies": { - "is-inside-container": "^1.0.0" + "call-bound": "^1.0.3", + "has-bigints": "^1.0.2", + "has-symbols": "^1.1.0", + "which-boxed-primitive": "^1.1.1" }, "engines": { - "node": ">=16" + "node": ">= 0.4" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/xml-name-validator": { - "version": "5.0.0", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": ">=18" - } + "node_modules/underscore": { + "version": "1.13.7", + "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.13.7.tgz", + "integrity": "sha512-GMXzWtsc57XAtguZgaQViUOzs0KTkk8ojr3/xAxXLITqf/3EMwxC0inyETfDFjH/Krbhuep0HNbbjI9i/q3F3g==", + "license": "MIT" }, - "node_modules/xmlbuilder": { - "version": "15.0.0", + "node_modules/undici": { + "version": "6.22.0", + "resolved": "https://registry.npmjs.org/undici/-/undici-6.22.0.tgz", + "integrity": "sha512-hU/10obOIu62MGYjdskASR3CUAiYaFTtC9Pa6vHyf//mAipSvSQg6od2CnJswq7fvzNS3zJhxoRkgNVaHurWKw==", "dev": true, "license": "MIT", "engines": { - "node": ">=8.0" + "node": ">=18.17" } }, - "node_modules/xmlchars": { - "version": "2.2.0", - "dev": true, + "node_modules/undici-types": { + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.16.0.tgz", + "integrity": "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw==", "license": "MIT" }, - "node_modules/y18n": { - "version": "5.0.8", - "license": "ISC", + "node_modules/universalify": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", + "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", + "dev": true, + "license": "MIT", "engines": { - "node": ">=10" + "node": ">= 10.0.0" } }, - "node_modules/yallist": { - "version": "3.1.1", - "dev": true, - "license": "ISC" - }, - "node_modules/yaml": { - "version": "1.10.2", - "dev": true, - "license": "ISC", + "node_modules/unpipe": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", + "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==", + "license": "MIT", "engines": { - "node": ">= 6" + "node": ">= 0.8" } }, - "node_modules/yaml-ast-parser": { - "version": "0.0.43", + "node_modules/unrs-resolver": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/unrs-resolver/-/unrs-resolver-1.11.1.tgz", + "integrity": "sha512-bSjt9pjaEBnNiGgc9rUiHGKv5l4/TGzDmYw3RhnkJGtLhbnnA/5qJj7x3dNDCRx/PJxu774LlH8lCOlB4hEfKg==", "dev": true, - "license": "Apache-2.0" + "hasInstallScript": true, + "license": "MIT", + "dependencies": { + "napi-postinstall": "^0.3.0" + }, + "funding": { + "url": "https://opencollective.com/unrs-resolver" + }, + "optionalDependencies": { + "@unrs/resolver-binding-android-arm-eabi": "1.11.1", + "@unrs/resolver-binding-android-arm64": "1.11.1", + "@unrs/resolver-binding-darwin-arm64": "1.11.1", + "@unrs/resolver-binding-darwin-x64": "1.11.1", + "@unrs/resolver-binding-freebsd-x64": "1.11.1", + "@unrs/resolver-binding-linux-arm-gnueabihf": "1.11.1", + "@unrs/resolver-binding-linux-arm-musleabihf": "1.11.1", + "@unrs/resolver-binding-linux-arm64-gnu": "1.11.1", + "@unrs/resolver-binding-linux-arm64-musl": "1.11.1", + "@unrs/resolver-binding-linux-ppc64-gnu": "1.11.1", + "@unrs/resolver-binding-linux-riscv64-gnu": "1.11.1", + "@unrs/resolver-binding-linux-riscv64-musl": "1.11.1", + "@unrs/resolver-binding-linux-s390x-gnu": "1.11.1", + "@unrs/resolver-binding-linux-x64-gnu": "1.11.1", + "@unrs/resolver-binding-linux-x64-musl": "1.11.1", + "@unrs/resolver-binding-wasm32-wasi": "1.11.1", + "@unrs/resolver-binding-win32-arm64-msvc": "1.11.1", + "@unrs/resolver-binding-win32-ia32-msvc": "1.11.1", + "@unrs/resolver-binding-win32-x64-msvc": "1.11.1" + } }, - "node_modules/yargs": { - "version": "17.0.1", + "node_modules/update-browserslist-db": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.2.3.tgz", + "integrity": "sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w==", "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], "license": "MIT", "dependencies": { - "cliui": "^7.0.2", - "escalade": "^3.1.1", - "get-caller-file": "^2.0.5", - "require-directory": "^2.1.1", - "string-width": "^4.2.0", - "y18n": "^5.0.5", - "yargs-parser": "^20.2.2" + "escalade": "^3.2.0", + "picocolors": "^1.1.1" }, - "engines": { - "node": ">=12" + "bin": { + "update-browserslist-db": "cli.js" + }, + "peerDependencies": { + "browserslist": ">= 4.21.0" } }, - "node_modules/yargs-parser": { - "version": "21.1.1", - "license": "ISC", - "engines": { - "node": ">=12" + "node_modules/update-check": { + "version": "1.5.4", + "resolved": "https://registry.npmjs.org/update-check/-/update-check-1.5.4.tgz", + "integrity": "sha512-5YHsflzHP4t1G+8WGPlvKbJEbAJGCgw+Em+dGR1KmBUbr1J36SJBqlHLjR7oob7sco5hWHGQVcr9B2poIVDDTQ==", + "license": "MIT", + "dependencies": { + "registry-auth-token": "3.3.2", + "registry-url": "3.1.0" } }, - "node_modules/yargs/node_modules/yargs-parser": { - "version": "20.2.9", - "dev": true, - "license": "ISC", - "engines": { - "node": ">=10" + "node_modules/uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "license": "BSD-2-Clause", + "dependencies": { + "punycode": "^2.1.0" } }, - "node_modules/yn": { - "version": "3.1.1", - "dev": true, + "node_modules/uri-js/node_modules/punycode": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", "license": "MIT", "engines": { "node": ">=6" } }, - "node_modules/yocto-queue": { - "version": "0.1.0", + "node_modules/urijs": { + "version": "1.19.11", + "resolved": "https://registry.npmjs.org/urijs/-/urijs-1.19.11.tgz", + "integrity": "sha512-HXgFDgDommxn5/bIv0cnQZsPhHDA90NPHD6+c/v21U5+Sx5hoP8+dP9IZXBU1gIfvdRfhG8cel9QNPeionfcCQ==", "dev": true, + "license": "MIT" + }, + "node_modules/url": { + "version": "0.10.3", + "resolved": "https://registry.npmjs.org/url/-/url-0.10.3.tgz", + "integrity": "sha512-hzSUW2q06EqL1gKM/a+obYHLIO6ct2hwPuviqTTOcfFVc61UbfJ2Q32+uGL/HCPxKqrdGB5QUwIe7UqlDgwsOQ==", "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "dependencies": { + "punycode": "1.3.2", + "querystring": "0.2.0" } }, - "node_modules/zip-stream": { - "version": "6.0.1", + "node_modules/url-template": { + "version": "2.0.8", + "resolved": "https://registry.npmjs.org/url-template/-/url-template-2.0.8.tgz", + "integrity": "sha512-XdVKMF4SJ0nP/O7XIPB0JwAEuT9lDIYnNsK8yGVe43y0AWoKeJNdv3ZNWh7ksJ6KqQFjOO6ox/VEitLnaVNufw==", + "dev": true, + "license": "BSD" + }, + "node_modules/use-sync-external-store": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.6.0.tgz", + "integrity": "sha512-Pp6GSwGP/NrPIrxVFAIkOQeyw8lFenOHijQWkUTrDvrF4ALqylP2C/KCkeS9dpUM3KvYRQhna5vt7IL95+ZQ9w==", "dev": true, "license": "MIT", + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" + } + }, + "node_modules/util": { + "version": "0.12.5", + "resolved": "https://registry.npmjs.org/util/-/util-0.12.5.tgz", + "integrity": "sha512-kZf/K6hEIrWHI6XqOFUiiMa+79wE/D8Q+NCNAWclkyg3b4d2k7s0QGepNjiABc+aR3N1PAyHL7p6UcLY6LmrnA==", + "license": "MIT", "dependencies": { - "archiver-utils": "^5.0.0", - "compress-commons": "^6.0.2", - "readable-stream": "^4.0.0" - }, - "engines": { - "node": ">= 14" + "inherits": "^2.0.3", + "is-arguments": "^1.0.4", + "is-generator-function": "^1.0.7", + "is-typed-array": "^1.1.3", + "which-typed-array": "^1.1.2" } }, - "node_modules/zip-stream/node_modules/buffer": { - "version": "6.0.3", + "node_modules/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", + "dev": true, + "license": "MIT" + }, + "node_modules/utility-types": { + "version": "3.11.0", + "resolved": "https://registry.npmjs.org/utility-types/-/utility-types-3.11.0.tgz", + "integrity": "sha512-6Z7Ma2aVEWisaL6TvBCy7P8rm2LQoPv6dJ7ecIaIixHcwfbJ0x7mWdbcwlIM5IGQxPZSFYeqRCqlOOeKoJYMkw==", "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], "license": "MIT", - "dependencies": { - "base64-js": "^1.3.1", - "ieee754": "^1.2.1" + "engines": { + "node": ">= 4" } }, - "node_modules/zip-stream/node_modules/readable-stream": { - "version": "4.7.0", + "node_modules/uuid": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.0.0.tgz", + "integrity": "sha512-jOXGuXZAWdsTH7eZLtyXMqUb9EcWMGZNbL9YcGBJl4MH4nrxHmZJhEHvyLFrkxo+28uLb/NYRcStH48fnD0Vzw==", + "license": "MIT", + "bin": { + "uuid": "dist/bin/uuid" + } + }, + "node_modules/v8-compile-cache-lib": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz", + "integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==", + "dev": true, + "license": "MIT" + }, + "node_modules/v8-to-istanbul": { + "version": "9.3.0", + "resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-9.3.0.tgz", + "integrity": "sha512-kiGUalWN+rgBJ/1OHZsBtU4rXZOfj/7rKQxULKlIzwzQSvMJUUNgPwJEEh7gU6xEVxC0ahoOBvN2YI8GH6FNgA==", "dev": true, - "license": "MIT", + "license": "ISC", "dependencies": { - "abort-controller": "^3.0.0", - "buffer": "^6.0.3", - "events": "^3.3.0", - "process": "^0.11.10", - "string_decoder": "^1.3.0" + "@jridgewell/trace-mapping": "^0.3.12", + "@types/istanbul-lib-coverage": "^2.0.1", + "convert-source-map": "^2.0.0" }, "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + "node": ">=10.12.0" } }, - "node_modules/zod": { - "version": "4.0.17", + "node_modules/validate-npm-package-name": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/validate-npm-package-name/-/validate-npm-package-name-3.0.0.tgz", + "integrity": "sha512-M6w37eVCMMouJ9V/sdPGnC5H4uDr73/+xdq0FBLO3TFFX1+7wiUY6Es328NN+y43tmY+doUdN9g9J21vqB7iLw==", "dev": true, - "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/colinhacks" + "license": "ISC", + "dependencies": { + "builtins": "^1.0.3" } }, - "node_modules/zod-mermaid": { - "version": "1.0.9", - "dev": true, + "node_modules/vary": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", + "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==", "license": "MIT", - "dependencies": { - "zod": "^4.0.5" - }, "engines": { - "node": ">=18.0.0" + "node": ">= 0.8" } }, - "scripts/test-data": { - "name": "nhs-notify-supplier-api-data-generator", - "version": "0.0.1", + "node_modules/vscode-json-languageservice": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/vscode-json-languageservice/-/vscode-json-languageservice-4.2.1.tgz", + "integrity": "sha512-xGmv9QIWs2H8obGbWg+sIPI/3/pFgj/5OWBhNzs00BkYQ9UaB2F6JJaGB/2/YOZJ3BvLXQTC4Q7muqU25QgAhA==", + "dev": true, + "license": "MIT", "dependencies": { - "@aws-sdk/client-s3": "^3.858.0", - "@internal/datastore": "*", - "esbuild": "^0.25.11", - "pino": "^9.7.0", - "yargs": "^17.7.2" - }, - "devDependencies": { - "@tsconfig/node22": "^22.0.2", - "@types/jest": "^30.0.0", - "jest": "^30.2.0", - "jest-mock-extended": "^4.0.0", - "typescript": "^5.8.3" + "jsonc-parser": "^3.0.0", + "vscode-languageserver-textdocument": "^1.0.3", + "vscode-languageserver-types": "^3.16.0", + "vscode-nls": "^5.0.0", + "vscode-uri": "^3.0.3" } }, - "scripts/test-data/node_modules/@jest/core": { - "version": "29.7.0", + "node_modules/vscode-languageserver-textdocument": { + "version": "1.0.12", + "resolved": "https://registry.npmjs.org/vscode-languageserver-textdocument/-/vscode-languageserver-textdocument-1.0.12.tgz", + "integrity": "sha512-cxWNPesCnQCcMPeenjKKsOCKQZ/L6Tv19DTRIGuLWe32lyzWhihGVJ/rcckZXJxfdKCFvRLS3fpBIsV/ZGX4zA==", + "dev": true, + "license": "MIT" + }, + "node_modules/vscode-languageserver-types": { + "version": "3.17.5", + "resolved": "https://registry.npmjs.org/vscode-languageserver-types/-/vscode-languageserver-types-3.17.5.tgz", + "integrity": "sha512-Ld1VelNuX9pdF39h2Hgaeb5hEZM2Z3jUrrMgWQAu82jMtZp7p3vJT3BzToKtZI7NgQssZje5o0zryOrhQvzQAg==", + "dev": true, + "license": "MIT" + }, + "node_modules/vscode-nls": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/vscode-nls/-/vscode-nls-5.2.0.tgz", + "integrity": "sha512-RAaHx7B14ZU04EU31pT+rKz2/zSl7xMsfIZuo8pd+KZO6PXtQmpevpq3vxvWNcrGbdmhM/rr5Uw5Mz+NBfhVng==", + "dev": true, + "license": "MIT" + }, + "node_modules/vscode-uri": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/vscode-uri/-/vscode-uri-3.1.0.tgz", + "integrity": "sha512-/BpdSx+yCQGnCvecbyXdxHDkuk55/G3xwnC0GqY4gmQ3j+A+g8kzzgB4Nk/SINjqn6+waqw3EgbVF2QKExkRxQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/w3c-xmlserializer": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/w3c-xmlserializer/-/w3c-xmlserializer-5.0.0.tgz", + "integrity": "sha512-o8qghlI8NZHU1lLPrpi2+Uq7abh4GGPpYANlalzWxyWteJOCsr/P+oPBA49TOLu5FTZO4d3F9MnWJfiMo4BkmA==", "dev": true, "license": "MIT", "dependencies": { - "@jest/console": "^29.7.0", - "@jest/reporters": "^29.7.0", - "@jest/test-result": "^29.7.0", - "@jest/transform": "^29.7.0", - "@jest/types": "^29.6.3", - "@types/node": "*", - "ansi-escapes": "^4.2.1", - "chalk": "^4.0.0", - "ci-info": "^3.2.0", - "exit": "^0.1.2", - "graceful-fs": "^4.2.9", - "jest-changed-files": "^29.7.0", - "jest-config": "^29.7.0", - "jest-haste-map": "^29.7.0", - "jest-message-util": "^29.7.0", - "jest-regex-util": "^29.6.3", - "jest-resolve": "^29.7.0", - "jest-resolve-dependencies": "^29.7.0", - "jest-runner": "^29.7.0", - "jest-runtime": "^29.7.0", - "jest-snapshot": "^29.7.0", - "jest-util": "^29.7.0", - "jest-validate": "^29.7.0", - "jest-watcher": "^29.7.0", - "micromatch": "^4.0.4", - "pretty-format": "^29.7.0", - "slash": "^3.0.0", - "strip-ansi": "^6.0.0" + "xml-name-validator": "^5.0.0" }, "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - }, - "peerDependencies": { - "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" - }, - "peerDependenciesMeta": { - "node-notifier": { - "optional": true - } + "node": ">=18" } }, - "scripts/test-data/node_modules/@jest/environment": { - "version": "29.7.0", + "node_modules/walker": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/walker/-/walker-1.0.8.tgz", + "integrity": "sha512-ts/8E8l5b7kY0vlWLewOkDXMmPdLcVV4GmOQLyxuSswIJsweeFZtAsMF7k1Nszz+TYBQrlYRmzOnr398y1JemQ==", "dev": true, + "license": "Apache-2.0", + "dependencies": { + "makeerror": "1.0.12" + } + }, + "node_modules/watchpack": { + "version": "2.4.4", + "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.4.4.tgz", + "integrity": "sha512-c5EGNOiyxxV5qmTtAB7rbiXxi1ooX1pQKMLX/MIabJjRA0SJBQOjKF+KSVfHkr9U1cADPon0mRiVe/riyaiDUA==", "license": "MIT", "dependencies": { - "@jest/fake-timers": "^29.7.0", - "@jest/types": "^29.6.3", - "@types/node": "*", - "jest-mock": "^29.7.0" + "glob-to-regexp": "^0.4.1", + "graceful-fs": "^4.1.2" }, "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + "node": ">=10.13.0" } }, - "scripts/test-data/node_modules/@jest/expect": { - "version": "29.7.0", + "node_modules/wcwidth": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/wcwidth/-/wcwidth-1.0.1.tgz", + "integrity": "sha512-XHPEwS0q6TaxcvG85+8EYkbiCux2XtWG2mkc47Ng2A77BQu9+DqIOJldST4HgPkuea7dvKSj5VgX3P1d4rW8Tg==", "dev": true, "license": "MIT", "dependencies": { - "expect": "^29.7.0", - "jest-snapshot": "^29.7.0" - }, + "defaults": "^1.0.3" + } + }, + "node_modules/webidl-conversions": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-7.0.0.tgz", + "integrity": "sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==", + "dev": true, + "license": "BSD-2-Clause", "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + "node": ">=12" } }, - "scripts/test-data/node_modules/@jest/fake-timers": { - "version": "29.7.0", + "node_modules/whatwg-encoding": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/whatwg-encoding/-/whatwg-encoding-3.1.1.tgz", + "integrity": "sha512-6qN4hJdMwfYBtE3YBTTHhoeuUrDBPZmbQaxWAqSALV/MeEnR5z1xd8UKud2RAkFoPkmB+hli1TZSnyi84xz1vQ==", "dev": true, "license": "MIT", "dependencies": { - "@jest/types": "^29.6.3", - "@sinonjs/fake-timers": "^10.0.2", - "@types/node": "*", - "jest-message-util": "^29.7.0", - "jest-mock": "^29.7.0", - "jest-util": "^29.7.0" + "iconv-lite": "0.6.3" }, "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + "node": ">=18" } }, - "scripts/test-data/node_modules/@jest/globals": { - "version": "29.7.0", + "node_modules/whatwg-encoding/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==", "dev": true, "license": "MIT", "dependencies": { - "@jest/environment": "^29.7.0", - "@jest/expect": "^29.7.0", - "@jest/types": "^29.6.3", - "jest-mock": "^29.7.0" + "safer-buffer": ">= 2.1.2 < 3.0.0" }, "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + "node": ">=0.10.0" } }, - "scripts/test-data/node_modules/@jest/reporters": { - "version": "29.7.0", + "node_modules/whatwg-mimetype": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-4.0.0.tgz", + "integrity": "sha512-QaKxh0eNIi2mE9p2vEdzfagOKHCcj1pJ56EEHGQOVxp8r9/iszLUUV7v89x9O1p/T+NlTM5W7jW6+cz4Fq1YVg==", "dev": true, "license": "MIT", - "dependencies": { - "@bcoe/v8-coverage": "^0.2.3", - "@jest/console": "^29.7.0", - "@jest/test-result": "^29.7.0", - "@jest/transform": "^29.7.0", - "@jest/types": "^29.6.3", - "@jridgewell/trace-mapping": "^0.3.18", - "@types/node": "*", - "chalk": "^4.0.0", - "collect-v8-coverage": "^1.0.0", - "exit": "^0.1.2", - "glob": "^7.1.3", - "graceful-fs": "^4.2.9", - "istanbul-lib-coverage": "^3.0.0", - "istanbul-lib-instrument": "^6.0.0", - "istanbul-lib-report": "^3.0.0", - "istanbul-lib-source-maps": "^4.0.0", - "istanbul-reports": "^3.1.3", - "jest-message-util": "^29.7.0", - "jest-util": "^29.7.0", - "jest-worker": "^29.7.0", - "slash": "^3.0.0", - "string-length": "^4.0.1", - "strip-ansi": "^6.0.0", - "v8-to-istanbul": "^9.0.1" - }, "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - }, - "peerDependencies": { - "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" - }, - "peerDependenciesMeta": { - "node-notifier": { - "optional": true - } + "node": ">=18" } }, - "scripts/test-data/node_modules/@jest/source-map": { - "version": "29.6.3", + "node_modules/whatwg-url": { + "version": "14.2.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-14.2.0.tgz", + "integrity": "sha512-De72GdQZzNTUBBChsXueQUnPKDkg/5A5zp7pFDuQAj5UFoENpiACU0wlCvzpAGnTkj++ihpKwKyYewn/XNUbKw==", "dev": true, "license": "MIT", "dependencies": { - "@jridgewell/trace-mapping": "^0.3.18", - "callsites": "^3.0.0", - "graceful-fs": "^4.2.9" + "tr46": "^5.1.0", + "webidl-conversions": "^7.0.0" }, "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + "node": ">=18" } }, - "scripts/test-data/node_modules/@jest/test-sequencer": { - "version": "29.7.0", - "dev": true, - "license": "MIT", + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "license": "ISC", "dependencies": { - "@jest/test-result": "^29.7.0", - "graceful-fs": "^4.2.9", - "jest-haste-map": "^29.7.0", - "slash": "^3.0.0" + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" }, "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + "node": ">= 8" } }, - "scripts/test-data/node_modules/@jest/transform": { - "version": "29.7.0", + "node_modules/which-boxed-primitive": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.1.1.tgz", + "integrity": "sha512-TbX3mj8n0odCBFVlY8AxkqcHASw3L60jIuF8jFP78az3C2YhmGvqbHBpAjTRH2/xqYunrJ9g1jSyjCjpoWzIAA==", "dev": true, "license": "MIT", "dependencies": { - "@babel/core": "^7.11.6", - "@jest/types": "^29.6.3", - "@jridgewell/trace-mapping": "^0.3.18", - "babel-plugin-istanbul": "^6.1.1", - "chalk": "^4.0.0", - "convert-source-map": "^2.0.0", - "fast-json-stable-stringify": "^2.1.0", - "graceful-fs": "^4.2.9", - "jest-haste-map": "^29.7.0", - "jest-regex-util": "^29.6.3", - "jest-util": "^29.7.0", - "micromatch": "^4.0.4", - "pirates": "^4.0.4", - "slash": "^3.0.0", - "write-file-atomic": "^4.0.2" + "is-bigint": "^1.1.0", + "is-boolean-object": "^1.2.1", + "is-number-object": "^1.1.1", + "is-string": "^1.1.1", + "is-symbol": "^1.1.1" }, "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "scripts/test-data/node_modules/@sinonjs/fake-timers": { - "version": "10.3.0", + "node_modules/which-builtin-type": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/which-builtin-type/-/which-builtin-type-1.2.1.tgz", + "integrity": "sha512-6iBczoX+kDQ7a3+YJBnh3T+KZRxM/iYNPXicqk66/Qfm1b93iu+yOImkg0zHbj5LNOcNv1TEADiZ0xa34B4q6Q==", "dev": true, - "license": "BSD-3-Clause", + "license": "MIT", "dependencies": { - "@sinonjs/commons": "^3.0.0" + "call-bound": "^1.0.2", + "function.prototype.name": "^1.1.6", + "has-tostringtag": "^1.0.2", + "is-async-function": "^2.0.0", + "is-date-object": "^1.1.0", + "is-finalizationregistry": "^1.1.0", + "is-generator-function": "^1.0.10", + "is-regex": "^1.2.1", + "is-weakref": "^1.0.2", + "isarray": "^2.0.5", + "which-boxed-primitive": "^1.1.0", + "which-collection": "^1.0.2", + "which-typed-array": "^1.1.16" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "scripts/test-data/node_modules/@types/jest": { - "version": "29.5.14", + "node_modules/which-builtin-type/node_modules/isarray": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz", + "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==", "dev": true, - "license": "MIT", - "dependencies": { - "expect": "^29.0.0", - "pretty-format": "^29.0.0" - } + "license": "MIT" }, - "scripts/test-data/node_modules/babel-jest": { - "version": "29.7.0", + "node_modules/which-collection": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/which-collection/-/which-collection-1.0.2.tgz", + "integrity": "sha512-K4jVyjnBdgvc86Y6BkaLZEN933SwYOuBFkdmBu9ZfkcAbdVbpITnDmjvZ/aQjRXQrv5EPkTnD1s39GiiqbngCw==", "dev": true, "license": "MIT", "dependencies": { - "@jest/transform": "^29.7.0", - "@types/babel__core": "^7.1.14", - "babel-plugin-istanbul": "^6.1.1", - "babel-preset-jest": "^29.6.3", - "chalk": "^4.0.0", - "graceful-fs": "^4.2.9", - "slash": "^3.0.0" + "is-map": "^2.0.3", + "is-set": "^2.0.3", + "is-weakmap": "^2.0.2", + "is-weakset": "^2.0.3" }, "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + "node": ">= 0.4" }, - "peerDependencies": { - "@babel/core": "^7.8.0" + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "scripts/test-data/node_modules/babel-plugin-istanbul": { - "version": "6.1.1", - "dev": true, - "license": "BSD-3-Clause", + "node_modules/which-typed-array": { + "version": "1.1.19", + "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.19.tgz", + "integrity": "sha512-rEvr90Bck4WZt9HHFC4DJMsjvu7x+r6bImz0/BrbWb7A2djJ8hnZMrWnHo9F8ssv0OMErasDhftrfROTyqSDrw==", + "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.0.0", - "@istanbuljs/load-nyc-config": "^1.0.0", - "@istanbuljs/schema": "^0.1.2", - "istanbul-lib-instrument": "^5.0.4", - "test-exclude": "^6.0.0" + "available-typed-arrays": "^1.0.7", + "call-bind": "^1.0.8", + "call-bound": "^1.0.4", + "for-each": "^0.3.5", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "has-tostringtag": "^1.0.2" }, "engines": { - "node": ">=8" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "scripts/test-data/node_modules/babel-plugin-istanbul/node_modules/istanbul-lib-instrument": { - "version": "5.2.1", - "dev": true, - "license": "BSD-3-Clause", + "node_modules/widest-line": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/widest-line/-/widest-line-4.0.1.tgz", + "integrity": "sha512-o0cyEG0e8GPzT4iGHphIOh0cJOV8fivsXxddQasHPHfoZf1ZexrfeA21w2NaEN1RHE+fXlfISmOE8R9N3u3Qig==", + "license": "MIT", "dependencies": { - "@babel/core": "^7.12.3", - "@babel/parser": "^7.14.7", - "@istanbuljs/schema": "^0.1.2", - "istanbul-lib-coverage": "^3.2.0", - "semver": "^6.3.0" + "string-width": "^5.0.1" }, "engines": { - "node": ">=8" + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "scripts/test-data/node_modules/babel-plugin-istanbul/node_modules/semver": { - "version": "6.3.1", - "dev": true, - "license": "ISC", - "bin": { - "semver": "bin/semver.js" + "node_modules/widest-line/node_modules/ansi-regex": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.2.tgz", + "integrity": "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==", + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" } }, - "scripts/test-data/node_modules/babel-plugin-jest-hoist": { - "version": "29.6.3", - "dev": true, + "node_modules/widest-line/node_modules/string-width": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", + "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", "license": "MIT", "dependencies": { - "@babel/template": "^7.3.3", - "@babel/types": "^7.3.3", - "@types/babel__core": "^7.1.14", - "@types/babel__traverse": "^7.0.6" + "eastasianwidth": "^0.2.0", + "emoji-regex": "^9.2.2", + "strip-ansi": "^7.0.1" }, "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "scripts/test-data/node_modules/babel-preset-jest": { - "version": "29.6.3", - "dev": true, + "node_modules/widest-line/node_modules/strip-ansi": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.2.tgz", + "integrity": "sha512-gmBGslpoQJtgnMAvOVqGZpEz9dyoKTCzy2nfz/n8aIFhN/jCE/rCmcxabB6jOOHV+0WNnylOxaxBQPSvcWklhA==", "license": "MIT", "dependencies": { - "babel-plugin-jest-hoist": "^29.6.3", - "babel-preset-current-node-syntax": "^1.0.0" + "ansi-regex": "^6.0.1" }, "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + "node": ">=12" }, - "peerDependencies": { - "@babel/core": "^7.0.0" + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" } }, - "scripts/test-data/node_modules/brace-expansion": { - "version": "1.1.12", + "node_modules/word-wrap": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", + "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==", "dev": true, "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" + "engines": { + "node": ">=0.10.0" } }, - "scripts/test-data/node_modules/camelcase": { - "version": "6.3.0", + "node_modules/wordwrap": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz", + "integrity": "sha512-gvVzJFlPycKc5dZN4yPkP8w7Dc37BtP1yczEneOb4uq34pXZcvrtRTmWV8W+Ume+XCxKgbjM+nevkyFPMybd4Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/wrap-ansi": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", + "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", "dev": true, "license": "MIT", - "engines": { - "node": ">=10" + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "engines": { + "node": ">=8" } }, - "scripts/test-data/node_modules/ci-info": { - "version": "3.9.0", + "node_modules/wrap-ansi-cjs": { + "name": "wrap-ansi", + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/sibiraj-s" - } - ], "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, "engines": { - "node": ">=8" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" } }, - "scripts/test-data/node_modules/cjs-module-lexer": { - "version": "1.4.3", - "dev": true, - "license": "MIT" + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", + "license": "ISC" }, - "scripts/test-data/node_modules/cliui": { - "version": "8.0.1", + "node_modules/write-file-atomic": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-5.0.1.tgz", + "integrity": "sha512-+QU2zd6OTD8XWIJCbffaiQeH9U73qIqafo1x6V1snCWYGJf6cVE0cDR4D8xRzcEnfI21IFrUPzPGtcPf8AC+Rw==", + "dev": true, "license": "ISC", "dependencies": { - "string-width": "^4.2.0", - "strip-ansi": "^6.0.1", - "wrap-ansi": "^7.0.0" + "imurmurhash": "^0.1.4", + "signal-exit": "^4.0.1" }, "engines": { - "node": ">=12" + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, - "scripts/test-data/node_modules/glob": { - "version": "7.2.3", + "node_modules/write-file-atomic/node_modules/signal-exit": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", "dev": true, "license": "ISC", - "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.1.1", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - }, "engines": { - "node": "*" + "node": ">=14" }, "funding": { "url": "https://github.com/sponsors/isaacs" } }, - "scripts/test-data/node_modules/istanbul-lib-source-maps": { - "version": "4.0.1", + "node_modules/write-file-atomic/node_modules/signal-exit": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", "dev": true, - "license": "BSD-3-Clause", - "dependencies": { - "debug": "^4.1.1", - "istanbul-lib-coverage": "^3.0.0", - "source-map": "^0.6.1" - }, + "license": "ISC", "engines": { - "node": ">=10" + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, - "scripts/test-data/node_modules/jest": { - "version": "29.7.0", + "node_modules/ws": { + "version": "8.18.3", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.3.tgz", + "integrity": "sha512-PEIGCY5tSlUt50cqyMXfCzX+oOPqN0vuGqWzbcJ2xvnkzkq46oOpz7dQaTDBdfICb4N14+GARUDw2XV2N4tvzg==", "dev": true, "license": "MIT", - "dependencies": { - "@jest/core": "^29.7.0", - "@jest/types": "^29.6.3", - "import-local": "^3.0.2", - "jest-cli": "^29.7.0" - }, - "bin": { - "jest": "bin/jest.js" - }, "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + "node": ">=10.0.0" }, "peerDependencies": { - "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" + "bufferutil": "^4.0.1", + "utf-8-validate": ">=5.0.2" }, "peerDependenciesMeta": { - "node-notifier": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { "optional": true } } }, - "scripts/test-data/node_modules/jest-changed-files": { - "version": "29.7.0", + "node_modules/xml-name-validator": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-5.0.0.tgz", + "integrity": "sha512-EvGK8EJ3DhaHfbRlETOWAS5pO9MZITeauHKJyb8wyajUfQUenkIg2MvLDTZ4T/TgIcm3HU0TFBgWWboAZ30UHg==", "dev": true, - "license": "MIT", - "dependencies": { - "execa": "^5.0.0", - "jest-util": "^29.7.0", - "p-limit": "^3.1.0" - }, + "license": "Apache-2.0", "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + "node": ">=18" } }, - "scripts/test-data/node_modules/jest-circus": { - "version": "29.7.0", - "dev": true, + "node_modules/xml2js": { + "version": "0.6.2", + "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.6.2.tgz", + "integrity": "sha512-T4rieHaC1EXcES0Kxxj4JWgaUQHDk+qwHcYOCFHfiwKz7tOVPLq7Hjq9dM1WCMhylqMEfP7hMcOIChvotiZegA==", "license": "MIT", "dependencies": { - "@jest/environment": "^29.7.0", - "@jest/expect": "^29.7.0", - "@jest/test-result": "^29.7.0", - "@jest/types": "^29.6.3", - "@types/node": "*", - "chalk": "^4.0.0", - "co": "^4.6.0", - "dedent": "^1.0.0", - "is-generator-fn": "^2.0.0", - "jest-each": "^29.7.0", - "jest-matcher-utils": "^29.7.0", - "jest-message-util": "^29.7.0", - "jest-runtime": "^29.7.0", - "jest-snapshot": "^29.7.0", - "jest-util": "^29.7.0", - "p-limit": "^3.1.0", - "pretty-format": "^29.7.0", - "pure-rand": "^6.0.0", - "slash": "^3.0.0", - "stack-utils": "^2.0.3" + "sax": ">=0.6.0", + "xmlbuilder": "~11.0.0" }, "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + "node": ">=4.0.0" } }, - "scripts/test-data/node_modules/jest-cli": { - "version": "29.7.0", - "dev": true, + "node_modules/xml2js/node_modules/xmlbuilder": { + "version": "11.0.1", + "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-11.0.1.tgz", + "integrity": "sha512-fDlsI/kFEx7gLvbecc0/ohLG50fugQp8ryHzMTuW9vSa1GJ0XYWKnhsUx7oie3G98+r56aTQIUB4kht42R3JvA==", "license": "MIT", - "dependencies": { - "@jest/core": "^29.7.0", - "@jest/test-result": "^29.7.0", - "@jest/types": "^29.6.3", - "chalk": "^4.0.0", - "create-jest": "^29.7.0", - "exit": "^0.1.2", - "import-local": "^3.0.2", - "jest-config": "^29.7.0", - "jest-util": "^29.7.0", - "jest-validate": "^29.7.0", - "yargs": "^17.3.1" - }, - "bin": { - "jest": "bin/jest.js" - }, "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - }, - "peerDependencies": { - "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" - }, - "peerDependenciesMeta": { - "node-notifier": { - "optional": true - } + "node": ">=4.0" } }, - "scripts/test-data/node_modules/jest-config": { - "version": "29.7.0", + "node_modules/xmlbuilder": { + "version": "15.0.0", + "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-15.0.0.tgz", + "integrity": "sha512-KLu/G0DoWhkncQ9eHSI6s0/w+T4TM7rQaLhtCaL6tORv8jFlJPlnGumsgTcGfYeS1qZ/IHqrvDG7zJZ4d7e+nw==", "dev": true, "license": "MIT", - "dependencies": { - "@babel/core": "^7.11.6", - "@jest/test-sequencer": "^29.7.0", - "@jest/types": "^29.6.3", - "babel-jest": "^29.7.0", - "chalk": "^4.0.0", - "ci-info": "^3.2.0", - "deepmerge": "^4.2.2", - "glob": "^7.1.3", - "graceful-fs": "^4.2.9", - "jest-circus": "^29.7.0", - "jest-environment-node": "^29.7.0", - "jest-get-type": "^29.6.3", - "jest-regex-util": "^29.6.3", - "jest-resolve": "^29.7.0", - "jest-runner": "^29.7.0", - "jest-util": "^29.7.0", - "jest-validate": "^29.7.0", - "micromatch": "^4.0.4", - "parse-json": "^5.2.0", - "pretty-format": "^29.7.0", - "slash": "^3.0.0", - "strip-json-comments": "^3.1.1" - }, "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - }, - "peerDependencies": { - "@types/node": "*", - "ts-node": ">=9.0.0" - }, - "peerDependenciesMeta": { - "@types/node": { - "optional": true - }, - "ts-node": { - "optional": true - } + "node": ">=8.0" } }, - "scripts/test-data/node_modules/jest-docblock": { - "version": "29.7.0", + "node_modules/xmlchars": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/xmlchars/-/xmlchars-2.2.0.tgz", + "integrity": "sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==", "dev": true, - "license": "MIT", - "dependencies": { - "detect-newline": "^3.0.0" - }, + "license": "MIT" + }, + "node_modules/y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "license": "ISC", "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + "node": ">=10" } }, - "scripts/test-data/node_modules/jest-each": { - "version": "29.7.0", + "node_modules/yallist": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", "dev": true, - "license": "MIT", - "dependencies": { - "@jest/types": "^29.6.3", - "chalk": "^4.0.0", - "jest-get-type": "^29.6.3", - "jest-util": "^29.7.0", - "pretty-format": "^29.7.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } + "license": "ISC" }, - "scripts/test-data/node_modules/jest-environment-node": { - "version": "29.7.0", + "node_modules/yaml": { + "version": "2.8.2", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.8.2.tgz", + "integrity": "sha512-mplynKqc1C2hTVYxd0PU2xQAc22TI1vShAYGksCCfxbn/dFwnHTNi1bvYsBTkhdUNtGIf5xNOg938rrSSYvS9A==", "dev": true, - "license": "MIT", - "dependencies": { - "@jest/environment": "^29.7.0", - "@jest/fake-timers": "^29.7.0", - "@jest/types": "^29.6.3", - "@types/node": "*", - "jest-mock": "^29.7.0", - "jest-util": "^29.7.0" + "license": "ISC", + "bin": { + "yaml": "bin.mjs" }, "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + "node": ">= 14.6" + }, + "funding": { + "url": "https://github.com/sponsors/eemeli" } }, - "scripts/test-data/node_modules/jest-haste-map": { - "version": "29.7.0", + "node_modules/yaml-ast-parser": { + "version": "0.0.43", + "resolved": "https://registry.npmjs.org/yaml-ast-parser/-/yaml-ast-parser-0.0.43.tgz", + "integrity": "sha512-2PTINUwsRqSd+s8XxKaJWQlUuEMHJQyEuh2edBbW8KNJz0SJPwUSD2zRWqezFEdN7IzAgeuYHFUCF7o8zRdZ0A==", "dev": true, + "license": "Apache-2.0" + }, + "node_modules/yargs": { + "version": "17.7.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", + "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", "license": "MIT", "dependencies": { - "@jest/types": "^29.6.3", - "@types/graceful-fs": "^4.1.3", - "@types/node": "*", - "anymatch": "^3.0.3", - "fb-watchman": "^2.0.0", - "graceful-fs": "^4.2.9", - "jest-regex-util": "^29.6.3", - "jest-util": "^29.7.0", - "jest-worker": "^29.7.0", - "micromatch": "^4.0.4", - "walker": "^1.0.8" + "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": "^14.15.0 || ^16.10.0 || >=18.0.0" - }, - "optionalDependencies": { - "fsevents": "^2.3.2" + "node": ">=12" } }, - "scripts/test-data/node_modules/jest-leak-detector": { - "version": "29.7.0", - "dev": true, - "license": "MIT", - "dependencies": { - "jest-get-type": "^29.6.3", - "pretty-format": "^29.7.0" - }, + "node_modules/yargs-parser": { + "version": "21.1.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", + "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", + "license": "ISC", "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + "node": ">=12" } }, - "scripts/test-data/node_modules/jest-mock": { - "version": "29.7.0", + "node_modules/yn": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", + "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==", "dev": true, "license": "MIT", - "dependencies": { - "@jest/types": "^29.6.3", - "@types/node": "*", - "jest-util": "^29.7.0" - }, "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + "node": ">=6" } }, - "scripts/test-data/node_modules/jest-regex-util": { - "version": "29.6.3", + "node_modules/yocto-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", "dev": true, "license": "MIT", "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "scripts/test-data/node_modules/jest-resolve": { - "version": "29.7.0", + "node_modules/zip-stream": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/zip-stream/-/zip-stream-6.0.1.tgz", + "integrity": "sha512-zK7YHHz4ZXpW89AHXUPbQVGKI7uvkd3hzusTdotCg1UxyaVtg0zFJSTfW/Dq5f7OBBVnq6cZIaC8Ti4hb6dtCA==", "dev": true, "license": "MIT", "dependencies": { - "chalk": "^4.0.0", - "graceful-fs": "^4.2.9", - "jest-haste-map": "^29.7.0", - "jest-pnp-resolver": "^1.2.2", - "jest-util": "^29.7.0", - "jest-validate": "^29.7.0", - "resolve": "^1.20.0", - "resolve.exports": "^2.0.0", - "slash": "^3.0.0" + "archiver-utils": "^5.0.0", + "compress-commons": "^6.0.2", + "readable-stream": "^4.0.0" }, "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + "node": ">= 14" } }, - "scripts/test-data/node_modules/jest-resolve-dependencies": { - "version": "29.7.0", + "node_modules/zip-stream/node_modules/buffer": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz", + "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==", "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], "license": "MIT", "dependencies": { - "jest-regex-util": "^29.6.3", - "jest-snapshot": "^29.7.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + "base64-js": "^1.3.1", + "ieee754": "^1.2.1" } }, - "scripts/test-data/node_modules/jest-runner": { - "version": "29.7.0", + "node_modules/zip-stream/node_modules/events": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", + "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==", "dev": true, "license": "MIT", - "dependencies": { - "@jest/console": "^29.7.0", - "@jest/environment": "^29.7.0", - "@jest/test-result": "^29.7.0", - "@jest/transform": "^29.7.0", - "@jest/types": "^29.6.3", - "@types/node": "*", - "chalk": "^4.0.0", - "emittery": "^0.13.1", - "graceful-fs": "^4.2.9", - "jest-docblock": "^29.7.0", - "jest-environment-node": "^29.7.0", - "jest-haste-map": "^29.7.0", - "jest-leak-detector": "^29.7.0", - "jest-message-util": "^29.7.0", - "jest-resolve": "^29.7.0", - "jest-runtime": "^29.7.0", - "jest-util": "^29.7.0", - "jest-watcher": "^29.7.0", - "jest-worker": "^29.7.0", - "p-limit": "^3.1.0", - "source-map-support": "0.5.13" - }, "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + "node": ">=0.8.x" } }, - "scripts/test-data/node_modules/jest-runtime": { - "version": "29.7.0", + "node_modules/zip-stream/node_modules/ieee754": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", "dev": true, - "license": "MIT", - "dependencies": { - "@jest/environment": "^29.7.0", - "@jest/fake-timers": "^29.7.0", - "@jest/globals": "^29.7.0", - "@jest/source-map": "^29.6.3", - "@jest/test-result": "^29.7.0", - "@jest/transform": "^29.7.0", - "@jest/types": "^29.6.3", - "@types/node": "*", - "chalk": "^4.0.0", - "cjs-module-lexer": "^1.0.0", - "collect-v8-coverage": "^1.0.0", - "glob": "^7.1.3", - "graceful-fs": "^4.2.9", - "jest-haste-map": "^29.7.0", - "jest-message-util": "^29.7.0", - "jest-mock": "^29.7.0", - "jest-regex-util": "^29.6.3", - "jest-resolve": "^29.7.0", - "jest-snapshot": "^29.7.0", - "jest-util": "^29.7.0", - "slash": "^3.0.0", - "strip-bom": "^4.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "BSD-3-Clause" }, - "scripts/test-data/node_modules/jest-snapshot": { - "version": "29.7.0", + "node_modules/zip-stream/node_modules/readable-stream": { + "version": "4.7.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-4.7.0.tgz", + "integrity": "sha512-oIGGmcpTLwPga8Bn6/Z75SVaH1z5dUut2ibSyAMVhmUggWpmDn2dapB0n7f8nwaSiRtepAsfJyfXIO5DCVAODg==", "dev": true, "license": "MIT", "dependencies": { - "@babel/core": "^7.11.6", - "@babel/generator": "^7.7.2", - "@babel/plugin-syntax-jsx": "^7.7.2", - "@babel/plugin-syntax-typescript": "^7.7.2", - "@babel/types": "^7.3.3", - "@jest/expect-utils": "^29.7.0", - "@jest/transform": "^29.7.0", - "@jest/types": "^29.6.3", - "babel-preset-current-node-syntax": "^1.0.0", - "chalk": "^4.0.0", - "expect": "^29.7.0", - "graceful-fs": "^4.2.9", - "jest-diff": "^29.7.0", - "jest-get-type": "^29.6.3", - "jest-matcher-utils": "^29.7.0", - "jest-message-util": "^29.7.0", - "jest-util": "^29.7.0", - "natural-compare": "^1.4.0", - "pretty-format": "^29.7.0", - "semver": "^7.5.3" + "abort-controller": "^3.0.0", + "buffer": "^6.0.3", + "events": "^3.3.0", + "process": "^0.11.10", + "string_decoder": "^1.3.0" }, "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" } }, - "scripts/test-data/node_modules/jest-validate": { - "version": "29.7.0", - "dev": true, + "node_modules/zod": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/zod/-/zod-4.2.1.tgz", + "integrity": "sha512-0wZ1IRqGGhMP76gLqz8EyfBXKk0J2qo2+H3fi4mcUP/KtTocoX08nmIAHl1Z2kJIZbZee8KOpBCSNPRgauucjw==", "license": "MIT", - "dependencies": { - "@jest/types": "^29.6.3", - "camelcase": "^6.2.0", - "chalk": "^4.0.0", - "jest-get-type": "^29.6.3", - "leven": "^3.1.0", - "pretty-format": "^29.7.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + "funding": { + "url": "https://github.com/sponsors/colinhacks" } }, - "scripts/test-data/node_modules/jest-watcher": { - "version": "29.7.0", - "dev": true, + "node_modules/zod-mermaid": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/zod-mermaid/-/zod-mermaid-1.1.0.tgz", + "integrity": "sha512-BV3mBaloFpujtSdFrs7Fgv3e/OIDGGuatMLCl83ANfR1sMtfuCxI8w3W9yy6OOcCv44L0Jxj8GvP9aJs6BYgjg==", "license": "MIT", "dependencies": { - "@jest/test-result": "^29.7.0", - "@jest/types": "^29.6.3", - "@types/node": "*", - "ansi-escapes": "^4.2.1", - "chalk": "^4.0.0", - "emittery": "^0.13.1", - "jest-util": "^29.7.0", - "string-length": "^4.0.1" + "zod": "^4.0.5" }, "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + "node": ">=18.0.0" } }, - "scripts/test-data/node_modules/jest-worker": { - "version": "29.7.0", - "dev": true, - "license": "MIT", + "pact-contracts": { + "name": "@nhsdigital/notify-supplier-api-consumer-contracts", + "version": "1.0.1", + "license": "MIT" + }, + "scripts/utilities/letter-test-data": { + "name": "nhs-notify-supplier-api-letter-test-data-utility", + "version": "0.0.1", "dependencies": { - "@types/node": "*", - "jest-util": "^29.7.0", - "merge-stream": "^2.0.0", - "supports-color": "^8.0.0" + "@aws-sdk/client-dynamodb": "^3.858.0", + "@aws-sdk/client-s3": "^3.858.0", + "@aws-sdk/lib-dynamodb": "^3.858.0", + "@internal/datastore": "*", + "esbuild": "^0.25.11", + "pino": "^9.7.0", + "yargs": "^17.7.2" }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + "devDependencies": { + "@tsconfig/node22": "^22.0.2", + "@types/jest": "^30.0.0", + "jest": "^30.2.0", + "jest-mock-extended": "^4.0.0", + "typescript": "^5.9.3" } }, - "scripts/test-data/node_modules/minimatch": { - "version": "3.1.2", - "dev": true, - "license": "ISC", + "scripts/utilities/letter-test-data/node_modules/pino": { + "version": "9.14.0", + "resolved": "https://registry.npmjs.org/pino/-/pino-9.14.0.tgz", + "integrity": "sha512-8OEwKp5juEvb/MjpIc4hjqfgCNysrS94RIOMXYvpYCdm/jglrKEiAYmiumbmGhCvs+IcInsphYDFwqrjr7398w==", + "license": "MIT", "dependencies": { - "brace-expansion": "^1.1.7" + "@pinojs/redact": "^0.4.0", + "atomic-sleep": "^1.0.0", + "on-exit-leak-free": "^2.1.0", + "pino-abstract-transport": "^2.0.0", + "pino-std-serializers": "^7.0.0", + "process-warning": "^5.0.0", + "quick-format-unescaped": "^4.0.3", + "real-require": "^0.2.0", + "safe-stable-stringify": "^2.3.1", + "sonic-boom": "^4.0.1", + "thread-stream": "^3.0.0" }, - "engines": { - "node": "*" + "bin": { + "pino": "bin.js" } }, - "scripts/test-data/node_modules/pure-rand": { - "version": "6.1.0", - "dev": true, - "funding": [ - { - "type": "individual", - "url": "https://github.com/sponsors/dubzzz" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/fast-check" - } - ], - "license": "MIT" - }, - "scripts/test-data/node_modules/signal-exit": { - "version": "3.0.7", - "dev": true, - "license": "ISC" - }, - "scripts/test-data/node_modules/supports-color": { - "version": "8.1.1", - "dev": true, - "license": "MIT", + "scripts/utilities/supplier-data": { + "name": "nhs-notify-supplier-api-suppliers-data-utility", + "version": "0.0.1", "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=10" + "@aws-sdk/client-dynamodb": "^3.858.0", + "@aws-sdk/lib-dynamodb": "^3.858.0", + "@internal/datastore": "*", + "esbuild": "^0.25.11", + "pino": "^9.7.0", + "yargs": "^17.7.2" }, - "funding": { - "url": "https://github.com/chalk/supports-color?sponsor=1" + "devDependencies": { + "@tsconfig/node22": "^22.0.2", + "@types/jest": "^30.0.0", + "jest": "^30.2.0", + "jest-mock-extended": "^4.0.0", + "typescript": "^5.8.3" } }, - "scripts/test-data/node_modules/wrap-ansi": { - "version": "7.0.0", + "scripts/utilities/supplier-data/node_modules/pino": { + "version": "9.14.0", + "resolved": "https://registry.npmjs.org/pino/-/pino-9.14.0.tgz", + "integrity": "sha512-8OEwKp5juEvb/MjpIc4hjqfgCNysrS94RIOMXYvpYCdm/jglrKEiAYmiumbmGhCvs+IcInsphYDFwqrjr7398w==", "license": "MIT", "dependencies": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": ">=10" + "@pinojs/redact": "^0.4.0", + "atomic-sleep": "^1.0.0", + "on-exit-leak-free": "^2.1.0", + "pino-abstract-transport": "^2.0.0", + "pino-std-serializers": "^7.0.0", + "process-warning": "^5.0.0", + "quick-format-unescaped": "^4.0.3", + "real-require": "^0.2.0", + "safe-stable-stringify": "^2.3.1", + "sonic-boom": "^4.0.1", + "thread-stream": "^3.0.0" }, - "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + "bin": { + "pino": "bin.js" } }, - "scripts/test-data/node_modules/write-file-atomic": { - "version": "4.0.2", - "dev": true, + "tests": { + "name": "nhs-notify-supplier-api-tests", + "version": "0.0.1", "license": "ISC", "dependencies": { - "imurmurhash": "^0.1.4", - "signal-exit": "^3.0.7" + "@aws-sdk/client-api-gateway": "^3.906.0", + "@aws-sdk/client-dynamodb": "^3.858.0", + "@aws-sdk/lib-dynamodb": "^3.858.0", + "@nhsdigital/nhs-notify-event-schemas-letter-rendering": "^2.0.1", + "@pact-foundation/pact": "^16.0.2", + "@playwright/test": "^1.57.0", + "allure-js-commons": "^3.3.3", + "dotenv": "^17.2.2", + "md5": "^2.3.0", + "openapi-response-validator": "^12.1.3", + "playwright": "^1.54.2", + "playwright-core": "^1.54.2", + "undici-types": "^7.10.0", + "zod": "^4.1.11" }, - "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + "devDependencies": { + "@types/node": "^24.3.1", + "allure-commandline": "^2.34.1", + "allure-playwright": "^3.4.3", + "jest": "^30.1.3", + "ts-jest": "^29.4.0", + "ts-node": "^10.9.2", + "typescript": "^5.9.3" } }, - "scripts/test-data/node_modules/yargs": { - "version": "17.7.2", + "tests/node_modules/@types/node": { + "version": "24.10.4", + "resolved": "https://registry.npmjs.org/@types/node/-/node-24.10.4.tgz", + "integrity": "sha512-vnDVpYPMzs4wunl27jHrfmwojOGKya0xyM3sH+UE5iv5uPS6vX7UIoh6m+vQc5LGBq52HBKPIn/zcSZVzeDEZg==", + "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" + "undici-types": "~7.16.0" } } } diff --git a/package.json b/package.json index 0453cc74..cfdcac88 100644 --- a/package.json +++ b/package.json @@ -1,19 +1,21 @@ { "dependencies": { "@aws-sdk/client-api-gateway": "^3.906.0", - "@playwright/test": "^1.55.1", + "@aws-sdk/client-kinesis": "^3.939.0", + "@aws-sdk/client-s3": "^3.925.0", + "@aws-sdk/client-sns": "^3.936.0", + "@playwright/test": "^1.57.0", "ajv": "^8.17.1", - "js-yaml": "^4.1.0", + "get-east-asian-width": "^1.4.0", "openapi-response-validator": "^12.1.3", "serve": "^14.2.4" }, "devDependencies": { "@openapitools/openapi-generator-cli": "^2.21.4", - "@redocly/cli": "^1.34.5", + "@redocly/cli": "^2.11.1", "@tsconfig/node22": "^22.0.2", "@types/jest": "^30.0.0", "@typescript-eslint/eslint-plugin": "^8.46.2", - "@types/js-yaml": "^4.0.9", "@typescript-eslint/parser": "^8.27.0", "esbuild": "^0.25.11", "eslint": "^9.27.0", @@ -42,7 +44,9 @@ "ts-jest": "^29.4.0", "ts-node": "^10.9.2", "tsx": "^4.20.6", - "typescript": "^5.8.3" + "typescript": "^5.9.3", + "typescript-eslint": "^8.27.0", + "yaml": "^2.6.1" }, "name": "nhs-notify-supplier-api", "overrides": { @@ -77,9 +81,11 @@ "deps:check": "npx syncpack list-mismatches" }, "workspaces": [ - "lambdas/*", + "docs", "internal/*", - "scripts/test-data", - "docs" + "lambdas/*", + "pact-contracts", + "scripts/utilities/*", + "tests" ] } diff --git a/pact-contracts/package.json b/pact-contracts/package.json new file mode 100644 index 00000000..c28d516d --- /dev/null +++ b/pact-contracts/package.json @@ -0,0 +1,25 @@ +{ + "name": "@nhsdigital/notify-supplier-api-consumer-contracts", + "version": "1.0.1", + "description": "NHS Notify Supplier API Pact contracts", + "license": "MIT", + "repository": { + "type": "git", + "url": "git+ssh://git@github.com/NHSDigital/nhs-notify-supplier-api.git" + }, + "exports": { + "./pacts/*.json": { + "default": "./pacts/*.json" + } + }, + "publishConfig": { + "access": "public", + "registry": "https://npm.pkg.github.com" + }, + "scripts": { + "lint": "echo Linting not required", + "test:unit": "echo Unit tests not required", + "test:unit:coverage": "echo Unit tests not required", + "typecheck": "echo Typecheck not required" + } +} diff --git a/postman/Sandbox.postman_collection.json b/postman/Sandbox.postman_collection.json index b4aabdb2..4279bb4a 100644 --- a/postman/Sandbox.postman_collection.json +++ b/postman/Sandbox.postman_collection.json @@ -754,7 +754,7 @@ "language": "json" } }, - "raw": "\n{\n \"data\": {\n \"attributes\": {\n \"reasonCode\": 100,\n \"reasonText\": \"failed validation\",\n \"status\": \"CANCELLED\"\n },\n \"id\": \"2WL5eYSWGzCHlGmzNxuqVusPxDg\",\n \"type\": \"Letter\"\n }\n}" + "raw": "\n{\n \"data\": {\n \"attributes\": {\n \"reasonCode\": 'R01',\n \"reasonText\": \"failed validation\",\n \"status\": \"CANCELLED\"\n },\n \"id\": \"2WL5eYSWGzCHlGmzNxuqVusPxDg\",\n \"type\": \"Letter\"\n }\n}" }, "header": [ { @@ -1014,7 +1014,7 @@ "language": "json" } }, - "raw": "{\n \"data\": {\n \"attributes\": {\n \"reasonCode\": 100,\n \"reasonText\": \"failed validation\",\n \"status\": \"FAILED\"\n },\n \"id\": \"2WL5eYSWGzCHlGmzNxuqVusPxDg\",\n \"type\": \"Letter\"\n }\n}" + "raw": "{\n \"data\": {\n \"attributes\": {\n \"reasonCode\": 'R01',\n \"reasonText\": \"failed validation\",\n \"status\": \"FAILED\"\n },\n \"id\": \"2WL5eYSWGzCHlGmzNxuqVusPxDg\",\n \"type\": \"Letter\"\n }\n}" }, "header": [ { @@ -1451,7 +1451,7 @@ "language": "json" } }, - "raw": "{\n \"data\": [\n {\n \"attributes\": {\n \"status\": \"PENDING\"\n },\n \"id\": \"2WL5eYSWGzCHlGmzNxuqVusPxDg\",\n \"type\": \"Letter\"\n },\n {\n \"attributes\": {\n \"status\": \"ACCEPTED\"\n },\n \"id\": \"2WL5eYSWGzCHlGmzNxuqVusPxDg\",\n \"type\": \"Letter\"\n },\n {\n \"attributes\": {\n \"status\": \"PRINTED\"\n },\n \"id\": \"2WL5eYSWGzCHlGmzNxuqVusPxDg\",\n \"type\": \"Letter\"\n },\n {\n \"attributes\": {\n \"status\": \"ENCLOSED\"\n },\n \"id\": \"2WL5eYSWGzCHlGmzNxuqVusPxDg\",\n \"type\": \"Letter\"\n },\n {\n \"attributes\": {\n \"status\": \"DISPATCHED\"\n },\n \"id\": \"2WL5eYSWGzCHlGmzNxuqVusPxDg\",\n \"type\": \"Letter\"\n },\n {\n \"attributes\": {\n \"status\": \"DELIVERED\"\n },\n \"id\": \"2WL5eYSWGzCHlGmzNxuqVusPxDg\",\n \"type\": \"Letter\"\n },\n {\n \"attributes\": {\n \"reasonCode\": 100,\n \"reasonText\": \"failed validation\",\n \"status\": \"RETURNED\"\n },\n \"id\": \"2WL5eYSWGzCHlGmzNxuqVusPxDg\",\n \"type\": \"Letter\"\n },\n {\n \"attributes\": {\n \"reasonCode\": 100,\n \"reasonText\": \"failed validation\",\n \"status\": \"CANCELLED\"\n },\n \"id\": \"2WL5eYSWGzCHlGmzNxuqVusPxDg\",\n \"type\": \"Letter\"\n },\n {\n \"attributes\": {\n \"reasonCode\": 100,\n \"reasonText\": \"failed validation\",\n \"status\": \"FAILED\"\n },\n \"id\": \"2WL5eYSWGzCHlGmzNxuqVusPxDg\",\n \"type\": \"Letter\"\n },\n {\n \"attributes\": {\n \"reasonCode\": 100,\n \"reasonText\": \"failed validation\",\n \"status\": \"RETURNED\"\n },\n \"id\": \"2WL5eYSWGzCHlGmzNxuqVusPxDg\",\n \"type\": \"Letter\"\n }\n ]\n}" + "raw": "{\n \"data\": [\n {\n \"attributes\": {\n \"status\": \"PENDING\"\n },\n \"id\": \"2WL5eYSWGzCHlGmzNxuqVusPxDg\",\n \"type\": \"Letter\"\n },\n {\n \"attributes\": {\n \"status\": \"ACCEPTED\"\n },\n \"id\": \"2WL5eYSWGzCHlGmzNxuqVusPxDg\",\n \"type\": \"Letter\"\n },\n {\n \"attributes\": {\n \"status\": \"PRINTED\"\n },\n \"id\": \"2WL5eYSWGzCHlGmzNxuqVusPxDg\",\n \"type\": \"Letter\"\n },\n {\n \"attributes\": {\n \"status\": \"ENCLOSED\"\n },\n \"id\": \"2WL5eYSWGzCHlGmzNxuqVusPxDg\",\n \"type\": \"Letter\"\n },\n {\n \"attributes\": {\n \"status\": \"DISPATCHED\"\n },\n \"id\": \"2WL5eYSWGzCHlGmzNxuqVusPxDg\",\n \"type\": \"Letter\"\n },\n {\n \"attributes\": {\n \"status\": \"DELIVERED\"\n },\n \"id\": \"2WL5eYSWGzCHlGmzNxuqVusPxDg\",\n \"type\": \"Letter\"\n },\n {\n \"attributes\": {\n \"reasonCode\": 'R01',\n \"reasonText\": \"failed validation\",\n \"status\": \"RETURNED\"\n },\n \"id\": \"2WL5eYSWGzCHlGmzNxuqVusPxDg\",\n \"type\": \"Letter\"\n },\n {\n \"attributes\": {\n \"reasonCode\": 'R01',\n \"reasonText\": \"failed validation\",\n \"status\": \"CANCELLED\"\n },\n \"id\": \"2WL5eYSWGzCHlGmzNxuqVusPxDg\",\n \"type\": \"Letter\"\n },\n {\n \"attributes\": {\n \"reasonCode\": 'R01',\n \"reasonText\": \"failed validation\",\n \"status\": \"FAILED\"\n },\n \"id\": \"2WL5eYSWGzCHlGmzNxuqVusPxDg\",\n \"type\": \"Letter\"\n },\n {\n \"attributes\": {\n \"reasonCode\": 'R01',\n \"reasonText\": \"failed validation\",\n \"status\": \"RETURNED\"\n },\n \"id\": \"2WL5eYSWGzCHlGmzNxuqVusPxDg\",\n \"type\": \"Letter\"\n }\n ]\n}" }, "header": [ { diff --git a/sandbox/.eslintrc.json b/sandbox/.eslintrc.json index ff2c4965..954d644b 100644 --- a/sandbox/.eslintrc.json +++ b/sandbox/.eslintrc.json @@ -1,5 +1,3 @@ -// Use this file as a starting point for your project's .eslintrc. -// Copy this file, and add rule overrides as needed. { "extends": "airbnb-base", "rules": { diff --git a/sandbox/HealthcheckEndpoint.json b/sandbox/HealthcheckEndpoint.json index c286173f..41f9393e 100644 --- a/sandbox/HealthcheckEndpoint.json +++ b/sandbox/HealthcheckEndpoint.json @@ -8,7 +8,7 @@ "description": "Service is healthy" } }, - "summary": "Health check endpoint" + "summary": "Health check" } } } diff --git a/sandbox/api/openapi.yaml b/sandbox/api/openapi.yaml index abc29494..0f80ee80 100644 --- a/sandbox/api/openapi.yaml +++ b/sandbox/api/openapi.yaml @@ -1,17 +1,133 @@ openapi: 3.0.3 info: description: | - API for communication suppliers to integrate with NHS Notify. + ## Overview + + API for letter suppliers to integrate with NHS Notify. + + This API lets you: + + * Get lists of letters allocated to you + * Download letter PDFs and metadata + * Update and manage letter statuses + * Submit and retrieve management information (MI) This specification represents the in-development 'next' version of the API schema - and should be treated as unstable. + and should be treated as unstable + + Use this API to retrieve letters to be printed + + ## Who can use this API + + The NHS Notify Supplier API is designed for approved print service suppliers who support the delivery of physical letters through the [NHS Notify](https://digital.nhs.uk/services/nhs-notify) platform. + + ## Related APIs + + The [NHS Notify API](https://digital.nhs.uk/developer/api-catalogue/nhs-notify) is used to send messages to citizens via NHS App, email, text message or letter. + + ## API status and roadmap + + This API is [in production, beta](https://digital.nhs.uk/developer/guides-and-documentation/reference-guide#statuses). We are onboarding partners to use it. + + We may make additive non-breaking changes to the API without notice, for example the addition of fields to a response or callback, or new optional fields to a request. + + ## Service Level + + This service is a [silver](https://digital.nhs.uk/services/reference-guide#service-levels) service, meaning it is available 24 hours a day, 365 days a year and supported from 8am to 6pm, Monday to Friday excluding bank holidays. + For more details, see [service levels](https://digital.nhs.uk/developer/guides-and-documentation/reference-guide#service-levels). + + ## Technology + + This API is a [REST-based](https://digital.nhs.uk/developer/guides-and-documentation/our-api-technologies#basic-rest) API. + + We follow the [JSON:API](https://jsonapi.org/) standard for our request and response schemas. + + ### Response content types + + This API can generate responses in the following format: + + * `application/vnd.api+json` - see [JSON:API specification](https://jsonapi.org/format/#introduction) + + ### Request content types + + This API will accept request payloads of the following types: + + * `application/vnd.api+json` - see [JSON:API specification](https://jsonapi.org/format/#introduction) + * `application/json` + + The `Content-Type` header may optionally include a `charset` attribute. If included, it **must** be set to `charset=utf-8` Any other `charset` value will result in a `406` error response. If omitted then `utf-8` is assumed. + + If you attempt to send a payload without the `Content-Type` header set to either of these values then the API will respond with a `415 Unsupported Media Type` response. + + ## Network access + + This API is available on the internet and, indirectly on the [Health and Social Care Network (HSCN)](https://digital.nhs.uk/services/health-and-social-care-network). + + For more details see [Network access for APIs](https://digital.nhs.uk/developer/guides-and-documentation/network-access-for-apis). + + ## Security and authorisation + + This API is [application-restricted](https://digital.nhs.uk/developer/guides-and-documentation/security-and-authorisation#application-restricted-apis), meaning we authenticate the calling application but not the end user. + + Authentication and authorisation of end users is the responsibility of your application. + + To access this API, use the following security pattern: + + * [Application-restricted RESTful API - signed JWT authentication](https://digital.nhs.uk/developer/guides-and-documentation/security-and-authorisation/application-restricted-restful-apis-signed-jwt-authentication) + + ## Environments and testing + + | Environment | Base URL | + |------------ | -------- | + | Sandbox | `https://sandbox.api.service.nhs.uk/nhs-notify-supplier` | + | Integration test | `https://int.api.service.nhs.uk/nhs-notify-supplier` | + | Production | `https://api.service.nhs.uk/nhs-notify-supplier` | + + ### Sandbox testing + + Our [sandbox environment](https://digital.nhs.uk/developer/guides-and-documentation/testing#sandbox-testing): + + * is for early developer testing + * only covers a limited set of scenarios + * is stateless, so does not actually persist any updates + * is open access, so does not allow you to test authorisation + + For details of sandbox test scenarios, or to try out sandbox using our 'Try this API' feature, see the documentation for each endpoint. + + Alternatively, you can try out the sandbox using our Postman collection + + You can find our postman collection source in our [public repository on github](https://github.com/NHSDigital/nhs-notify-supplier-api/tree/main/postman). + + ### Integration testing + + Our integration test environment: + + * is for formal integration sandbox-testing + * is stateful, so persists updates + * includes authorisation via [signed JWT authentication](https://digital.nhs.uk/developer/guides-and-documentation/security-and-authorisation/application-restricted-restful-apis-signed-jwt-authentication) + + You need to get your software approved by us before it can go live with this API. + + You will also need to follow our steps to - TBD + + ### Production smoke testing + + Before go-live, you must complete a smoke test in the NHS Notify production environment. + The smoke test confirms that your live credentials, connectivity, and print workflow operate correctly end-to-end. It will be carried out in coordination with the NHS Notify Supplier API team. + You will retrieve and print one or more live test letters through the production API, send the printed output to the address provided, and submit a Management Information (MI) update for verification. + The NHS Notify team will configure your production access, review your results, and confirm that your output meets NHS Notify print specifications. + + ### Onboarding + + You need to get your software approved by us before it can go live with this API. + You will also need to be an approved NHS letter supplier under the framework agreement (ADD link) and nominate your technical and operational contacts title: NHS Notify Supplier API version: next servers: +- description: Public sandbox + url: https://internal-dev-sandbox.api.service.nhs.uk/nhs-notify-supplier - description: Local development server url: http://127.0.0.1:9000 -- description: Public sandbox - url: https://sandbox-server.nhs.uk/nhs-notify-supplier-api tags: - description: "" name: letter @@ -22,8 +138,15 @@ tags: paths: /letters: get: - description: The key use of this endpoint is to query letters which are ready - to be printed + description: | + ## Overview + + Use this endpoint to poll letters which are ready to be printed. + + Returns letters whose `status` is **PENDING**. + Use `limit` to control list size (max 2500). + + Rate limiting applies. On excess requests, you may receive **429 Too Many Requests** (example error code(s): `NOTIFY_QUOTA`). Back off and retry later operationId: listLetters parameters: - description: "Unique request identifier, in the format of a GUID" @@ -308,6 +431,37 @@ paths: - letter x-eov-operation-handler: controllers/LetterController post: + description: | + ## Overview + + Use this endpoint to update the status for (example, PRINTED, DISPATCHED, DELIVERED) of multiple letters by providing the new statuses in the request body, optionally including reason codes and text. + + Use this endpoint when you need to report status changes for several letters at once. + + When you make a POST update request with, the endpoint will respond with a successful (202) response code or an unsuccessful (4xx/5xx) response. + + Rate limiting applies. On excess requests, you may receive **429 Too Many Requests** (example error code(s): `NOTIFY_QUOTA`). Back off and retry later. + + ### Statuses + + Allowed `status` values that can be used to are: + + - `ACCEPTED` + - `CANCELLED` + - `DELIVERED` + - `DISPATCHED` + - `ENCLOSED` + - `FAILED` + - `FORWARDED` + - `PRINTED` + - `REJECTED` + - `RETURNED` + + It is not possible to update a letter to status of `PENDING`. + + The request should not contain multiple letter objects with the same ID. + + Optionally a `reasonCode` and `reasonText` explaining the status (for example, validation failures) can be included in the request body for each update. operationId: postLetters parameters: - description: "Unique request identifier, in the format of a GUID" @@ -362,25 +516,25 @@ paths: id: 2WL5eYSWGzCHlGmzNxuqVusPxDg type: Letter - attributes: - reasonCode: 100 + reasonCode: R01 reasonText: failed validation status: RETURNED id: 2WL5eYSWGzCHlGmzNxuqVusPxDg type: Letter - attributes: - reasonCode: 100 + reasonCode: R01 reasonText: failed validation status: CANCELLED id: 2WL5eYSWGzCHlGmzNxuqVusPxDg type: Letter - attributes: - reasonCode: 100 + reasonCode: R01 reasonText: failed validation status: FAILED id: 2WL5eYSWGzCHlGmzNxuqVusPxDg type: Letter - attributes: - reasonCode: 100 + reasonCode: R01 reasonText: failed validation status: RETURNED id: 2WL5eYSWGzCHlGmzNxuqVusPxDg @@ -409,23 +563,23 @@ paths: example: 11C46F5F-CDEF-4865-94B2-0EE0EDCC26DA type: string style: simple - "404": + "400": content: application/vnd.api+json: examples: - error-not-found: + error-bad-request-invalid-request-body: value: errors: - - code: NOTIFY_RESOURCE_NOT_FOUND - detail: No resource found with that ID + - code: NOTIFY_INVALID_REQUEST + detail: The request body is invalid id: rrt-1931948104716186917-c-geu2-10664-3111479-3.0 links: about: https://digital.nhs.uk/developer/api-catalogue/nhs-notify-supplier - status: "404" - title: Resource not found + status: "400" + title: Invalid request schema: $ref: "#/components/schemas/listLetters_400_response" - description: Resource not found + description: "Bad request, invalid input data" "429": content: application/vnd.api+json: @@ -467,9 +621,14 @@ paths: /letters/{id}: get: description: | - ## Get details the status of a letter. + ## Overview + + Use this endpoint to get the current status of a single letter by its ID. + + Rate limiting applies. On excess requests, you may receive **429 Too Many Requests** (example error code(s): `NOTIFY_QUOTA`). Back off and retry later ## Sandbox test scenarios + You can test the following scenarios in our sandbox environment | Scenario | Letter Id | @@ -607,8 +766,33 @@ paths: - letter x-eov-operation-handler: controllers/LetterController patch: - description: Update the status of a letter by providing the new status in the - request body. + description: | + ## Overview + + Use this endpoint to update the status of a letter by submitting the new status in the request body, optionally providing a reason code and text. + + When you make a PATCH request with your application, the endpoint will respond with a successful (200) response code, along with the updated patient resource or an unsuccessful (4xx/5xx) response. + + Rate limiting applies. On excess requests, you may receive **429 Too Many Requests** (example error code(s): `NOTIFY_QUOTA`). Back off and retry later. + + ### Statuses + + Allowed `status` values that can be used to are: + + - `ACCEPTED` + - `CANCELLED` + - `DELIVERED` + - `DISPATCHED` + - `ENCLOSED` + - `FAILED` + - `FORWARDED` + - `PRINTED` + - `REJECTED` + - `RETURNED` + + It is not possible to update a letter to status of `PENDING`. + + Optionally a `reasonCode` and `reasonText` explaining the status (for example, validation failures) can be included in the request body. operationId: patchLetter parameters: - description: "Unique request identifier, in the format of a GUID" @@ -671,7 +855,7 @@ paths: value: data: attributes: - reasonCode: 100 + reasonCode: R01 reasonText: failed validation status: REJECTED id: 2WL5eYSWGzCHlGmzNxuqVusPxDg @@ -697,7 +881,7 @@ paths: value: data: attributes: - reasonCode: 100 + reasonCode: R01 reasonText: failed validation status: CANCELLED id: 2WL5eYSWGzCHlGmzNxuqVusPxDg @@ -723,7 +907,7 @@ paths: value: data: attributes: - reasonCode: 100 + reasonCode: R01 reasonText: failed validation status: FAILED id: 2WL5eYSWGzCHlGmzNxuqVusPxDg @@ -733,7 +917,7 @@ paths: value: data: attributes: - reasonCode: 100 + reasonCode: R01 reasonText: failed validation status: REJECTED id: 2WL5eYSWGzCHlGmzNxuqVusPxDg @@ -865,6 +1049,21 @@ paths: x-eov-operation-handler: controllers/LetterController /letters/{id}/data: get: + description: | + ## Overview + + Use this endpoint to get letter data, including downloading the letter's print-ready PDF file. + + Rate limiting applies. On excess requests, you may receive **429 Too Many Requests** (example error code(s): `NOTIFY_QUOTA`). Back off and retry later + + ## Sandbox test scenarios + + You can test the following scenarios in our sandbox environment. + + |Scenario|Request ID|Response| + |--------|-------|--------| + |Success | 2AL5eYSWGzCHlGmzNxuqVusPxDg | Returns 303 (See Other) and URL to an example PDF in the Location header | + |Not Found |2WL5eYSWGzCHlGmzNxuqVusPxDg | Returns 404 Not found and error details in the response | operationId: getDataId parameters: - description: Unique identifier of this resource @@ -980,47 +1179,26 @@ paths: tags: - data x-eov-operation-handler: controllers/DataController - head: - operationId: headDataId - parameters: - - description: Unique identifier of this resource - explode: false - in: path - name: id - required: true - schema: - example: 24L5eYSWGzCHlGmzNxuqVusPxDg - type: string - style: simple - - description: "Unique request identifier, in the format of a GUID" - explode: false - in: header - name: X-Request-ID - required: true - schema: - example: dcb9c8dc-c2f4-4d5f-8674-a2e913e040b2 - type: string - style: simple - - description: |- - An optional ID which you can use to track transactions across multiple systems. It can take any value, but we recommend avoiding `.` characters. If not provided in the request, NHS Notify will default to a system generated ID in its place. - The ID will be returned in a response header. - explode: false - in: header - name: X-Correlation-ID - required: false - schema: - example: 11C46F5F-CDEF-4865-94B2-0EE0EDCC26DA - type: string - style: simple - responses: - "200": - description: OK - summary: Check for the existence of a data file - tags: - - data - x-eov-operation-handler: controllers/DataController /mi: post: + description: | + ## Overview + + Use this endpoint to send management or operational metrics relating to letter processing and print fulfilment + + When you submit a create management information request, the endpoint will respond with a 201 (Created) response code along with the created data including a unique id for the record or an unsuccessful (4xx/5xx) response + + Rate limiting applies. On excess requests, you may receive **429 Too Many Requests** (example error code(s): `NOTIFY_QUOTA`). Back off and retry later + + ## Sandbox test scenarios + + You can test the following scenarios in our sandbox environment. + + |Scenario|Request|Response| + |--------|-------|--------| + |Success|Request for successful MI record Creation| 201 (Created) with the created management information in the response| + |Invalid Request|Invalid Request for MI record Creation| 400 (Bad Request) with the error details in the body| + |Unknown specification|Request for MI record Creation for unknown spec|404 (Not Found) with the error details in the body| operationId: createMI parameters: - description: "Unique request identifier, in the format of a GUID" @@ -1124,6 +1302,23 @@ paths: example: 11C46F5F-CDEF-4865-94B2-0EE0EDCC26DA type: string style: simple + "400": + content: + application/vnd.api+json: + examples: + error-bad-request: + value: + errors: + - code: NOTIFY_INVALID_REQUEST + detail: "Invalid request, please refer to the API specifications" + id: rrt-1931948104716186917-c-geu2-10664-3111479-3.0 + links: + about: https://digital.nhs.uk/developer/api-catalogue/nhs-notify-supplier + status: "400" + title: Invalid request + schema: + $ref: "#/components/schemas/listLetters_400_response" + description: "Bad request, invalid input data" "404": content: application/vnd.api+json: @@ -1224,14 +1419,13 @@ components: - DELIVERED - FAILED - RETURNED - - DESTROYED - FORWARDED example: PENDING type: string reasonCode: description: Reason code for the given status - example: 100 - type: number + example: R01 + type: string reasonText: description: Reason text for the given status example: failed validation @@ -1314,14 +1508,13 @@ components: - DELIVERED - FAILED - RETURNED - - DESTROYED - FORWARDED example: PENDING type: string reasonCode: description: Reason code for the given status - example: 100 - type: number + example: R01 + type: string reasonText: description: Reason text for the given status example: failed validation @@ -1441,7 +1634,7 @@ components: name: apikey type: apiKey x-nhsd-apim: - temporary: true + temporary: false monitoring: false access: [] target: diff --git a/sandbox/data/examples/createMI/requests/createMI_INVALID.json b/sandbox/data/examples/createMI/requests/createMI_request_INVALID.json similarity index 100% rename from sandbox/data/examples/createMI/requests/createMI_INVALID.json rename to sandbox/data/examples/createMI/requests/createMI_request_INVALID.json diff --git a/sandbox/data/examples/createMI/requests/createMI_NOTFOUND.json b/sandbox/data/examples/createMI/requests/createMI_request_NOTFOUND.json similarity index 100% rename from sandbox/data/examples/createMI/requests/createMI_NOTFOUND.json rename to sandbox/data/examples/createMI/requests/createMI_request_NOTFOUND.json diff --git a/sandbox/data/examples/createMI/requests/createMI_SUCCESS.json b/sandbox/data/examples/createMI/requests/createMI_request_SUCCESS.json similarity index 100% rename from sandbox/data/examples/createMI/requests/createMI_SUCCESS.json rename to sandbox/data/examples/createMI/requests/createMI_request_SUCCESS.json diff --git a/sandbox/data/examples/createMI/responses/createMI_SUCCESS.json b/sandbox/data/examples/createMI/responses/createMI_response_SUCCESS.json similarity index 100% rename from sandbox/data/examples/createMI/responses/createMI_SUCCESS.json rename to sandbox/data/examples/createMI/responses/createMI_response_SUCCESS.json diff --git a/sandbox/data/examples/errors/responses/bad-gateway.json b/sandbox/data/examples/errors/responses/bad-gateway.json new file mode 100644 index 00000000..a125f860 --- /dev/null +++ b/sandbox/data/examples/errors/responses/bad-gateway.json @@ -0,0 +1,3 @@ +{ + "message": "Bad Gateway" +} diff --git a/sandbox/data/examples/errors/responses/tooManyRequests.json b/sandbox/data/examples/errors/responses/tooManyRequests.json index 4babbd7d..36411d71 100644 --- a/sandbox/data/examples/errors/responses/tooManyRequests.json +++ b/sandbox/data/examples/errors/responses/tooManyRequests.json @@ -1,14 +1,10 @@ { - "errors": [ - { - "code": "NOTIFY_QUOTA", - "detail": "You have made too many requests. Please try again later.", - "id": "rrt-1931948104716186917-c-geu2-10664-3111479-3.0", - "links": { - "about": "https://digital.nhs.uk/developer/api-catalogue/nhs-notify-supplier" - }, - "status": "429", - "title": "Too many requests" - } - ] + "interval": 1, + "limit": 1, + "message": "Your application, Notify-Supplier-App-Restricted - Internal Dev 2, has exceeded its quota of 1 requests every 1 minute(s) and is being rate limited.", + "message_id": "rrt-4773181658036170775-c-geu2-321623-73628915-2", + "policy": "quota", + "ratelimiting_expiry_time_ms": 1765372560000, + "scope": "application", + "timeunit": "minute" } diff --git a/sandbox/data/examples/patchLetter/requests/patchLetter_CANCELLED.json b/sandbox/data/examples/patchLetter/requests/patchLetter_CANCELLED.json index 7106a2f5..945cb1d6 100644 --- a/sandbox/data/examples/patchLetter/requests/patchLetter_CANCELLED.json +++ b/sandbox/data/examples/patchLetter/requests/patchLetter_CANCELLED.json @@ -1,8 +1,8 @@ { "data": { "attributes": { - "reasonCode": "R01", - "reasonText": "failed validation", + "reasonCode": "R10", + "reasonText": "Miscellaneous", "status": "CANCELLED" }, "id": "2WL5eYSWGzCHlGmzNxuqVusPxDg", diff --git a/sandbox/data/examples/patchLetter/requests/patchLetter_FAILED.json b/sandbox/data/examples/patchLetter/requests/patchLetter_FAILED.json index 359d9bc3..2e0721ea 100644 --- a/sandbox/data/examples/patchLetter/requests/patchLetter_FAILED.json +++ b/sandbox/data/examples/patchLetter/requests/patchLetter_FAILED.json @@ -1,8 +1,8 @@ { "data": { "attributes": { - "reasonCode": "R01", - "reasonText": "failed validation", + "reasonCode": "R10", + "reasonText": "Miscellaneous", "status": "FAILED" }, "id": "2WL5eYSWGzCHlGmzNxuqVusPxDg", diff --git a/sandbox/data/examples/patchLetter/requests/patchLetter_REJECTED.json b/sandbox/data/examples/patchLetter/requests/patchLetter_REJECTED.json index c44c0558..fc11dd2c 100644 --- a/sandbox/data/examples/patchLetter/requests/patchLetter_REJECTED.json +++ b/sandbox/data/examples/patchLetter/requests/patchLetter_REJECTED.json @@ -1,8 +1,8 @@ { "data": { "attributes": { - "reasonCode": "R01", - "reasonText": "failed validation", + "reasonCode": "R07", + "reasonText": "No such address", "status": "REJECTED" }, "id": "2WL5eYSWGzCHlGmzNxuqVusPxDg", diff --git a/sandbox/data/examples/patchLetter/requests/patchLetter_RETURNED.json b/sandbox/data/examples/patchLetter/requests/patchLetter_RETURNED.json index c44c0558..fc20aa5a 100644 --- a/sandbox/data/examples/patchLetter/requests/patchLetter_RETURNED.json +++ b/sandbox/data/examples/patchLetter/requests/patchLetter_RETURNED.json @@ -2,7 +2,7 @@ "data": { "attributes": { "reasonCode": "R01", - "reasonText": "failed validation", + "reasonText": "Addressee gone away", "status": "REJECTED" }, "id": "2WL5eYSWGzCHlGmzNxuqVusPxDg", diff --git a/sandbox/data/examples/patchLetter/responses/patchLetter_CANCELLED.json b/sandbox/data/examples/patchLetter/responses/patchLetter_CANCELLED.json index a0509519..b7724000 100644 --- a/sandbox/data/examples/patchLetter/responses/patchLetter_CANCELLED.json +++ b/sandbox/data/examples/patchLetter/responses/patchLetter_CANCELLED.json @@ -1,8 +1,8 @@ { "data": { "attributes": { - "reasonCode": "R01", - "reasonText": "failed validation", + "reasonCode": "R10", + "reasonText": "Miscellaneous", "specificationId": "2WL5eYSWGzCHlGmzNxuqVusPxDg", "status": "CANCELLED" }, diff --git a/sandbox/data/examples/patchLetter/responses/patchLetter_FAILED.json b/sandbox/data/examples/patchLetter/responses/patchLetter_FAILED.json index b2c2f902..208a9459 100644 --- a/sandbox/data/examples/patchLetter/responses/patchLetter_FAILED.json +++ b/sandbox/data/examples/patchLetter/responses/patchLetter_FAILED.json @@ -1,8 +1,8 @@ { "data": { "attributes": { - "reasonCode": "R01", - "reasonText": "failed validation", + "reasonCode": "R10", + "reasonText": "Miscellaneous", "specificationId": "2WL5eYSWGzCHlGmzNxuqVusPxDg", "status": "FAILED" }, diff --git a/sandbox/data/examples/patchLetter/responses/patchLetter_REJECTED.json b/sandbox/data/examples/patchLetter/responses/patchLetter_REJECTED.json index 98a205e5..c628b4bb 100644 --- a/sandbox/data/examples/patchLetter/responses/patchLetter_REJECTED.json +++ b/sandbox/data/examples/patchLetter/responses/patchLetter_REJECTED.json @@ -1,8 +1,8 @@ { "data": { "attributes": { - "reasonCode": "R01", - "reasonText": "failed validation", + "reasonCode": "R07", + "reasonText": "No such address", "specificationId": "2WL5eYSWGzCHlGmzNxuqVusPxDg", "status": "REJECTED" }, diff --git a/sandbox/data/examples/patchLetter/responses/patchLetter_RETURNED.json b/sandbox/data/examples/patchLetter/responses/patchLetter_RETURNED.json index eb3a70a0..9224419e 100644 --- a/sandbox/data/examples/patchLetter/responses/patchLetter_RETURNED.json +++ b/sandbox/data/examples/patchLetter/responses/patchLetter_RETURNED.json @@ -2,7 +2,7 @@ "data": { "attributes": { "reasonCode": "R01", - "reasonText": "failed validation", + "reasonText": "Addressee gone away", "specificationId": "2WL5eYSWGzCHlGmzNxuqVusPxDg", "status": "RETURNED" }, diff --git a/sandbox/services/LetterService.js b/sandbox/services/LetterService.js index 1d333aa3..a603d5fa 100644 --- a/sandbox/services/LetterService.js +++ b/sandbox/services/LetterService.js @@ -82,15 +82,21 @@ const patchLetter = ({ xRequestId, id, body, xCorrelationId }) => new Promise( async (resolve, reject) => { try { const responseData = await ResponseProvider.patchLetterResponse(body); - const content = await fs.readFile(responseData.responsePath); - const fileData = JSON.parse(content); - + if (responseData.responseCode !== 202) { + const content = await fs.readFile(responseData.responsePath); + const fileData = JSON.parse(content); - resolve(Service.successResponse({ - xRequestId, - xCorrelationId, - data: fileData, - }, responseData.responseCode)); + resolve(Service.successResponse({ + xRequestId, + xCorrelationId, + data: fileData, + }, responseData.responseCode)); + } else { + resolve(Service.successResponse({ + xRequestId, + xCorrelationId, + }, responseData.responseCode)); + } } catch (e) { reject(Service.rejectResponse( e.message || 'Invalid input', @@ -105,17 +111,28 @@ const patchLetter = ({ xRequestId, id, body, xCorrelationId }) => new Promise( * xRequestId String Unique request identifier, in the format of a GUID * postLettersRequest PostLettersRequest * xCorrelationId String An optional ID which you can use to track transactions across multiple systems. It can take any value, but we recommend avoiding `.` characters. If not provided in the request, NHS Notify will default to a system generated ID in its place. The ID will be returned in a response header. (optional) -* returns listLetters_200_response +* returns 202 * */ const postLetters = ({ xRequestId, body, xCorrelationId }) => new Promise( async (resolve, reject) => { const responseData = await ResponseProvider.postLettersResponse(body); try { - resolve(Service.successResponse({ - xRequestId, - xCorrelationId - }, responseData.responseCode)); + if (responseData.responseCode !== 202) { + const content = await fs.readFile(responseData.responsePath); + const fileData = JSON.parse(content); + + resolve(Service.successResponse({ + xRequestId, + xCorrelationId, + data: fileData, + }, responseData.responseCode)); + } else { + resolve(Service.successResponse({ + xRequestId, + xCorrelationId, + }, responseData.responseCode)); + } } catch (e) { reject(Service.rejectResponse( e.message || 'Invalid input', diff --git a/sandbox/utils/ResponseProvider.js b/sandbox/utils/ResponseProvider.js index da0ca8fd..bfaf9081 100644 --- a/sandbox/utils/ResponseProvider.js +++ b/sandbox/utils/ResponseProvider.js @@ -76,16 +76,16 @@ async function getLettersResponse(limit) { async function patchLetterResponse(request) { const patchLetterFileMap = { - 'data/examples/patchLetter/requests/patchLetter_PENDING.json': {responsePath:'data/examples/patchLetter/responses/patchLetter_PENDING.json',responseCode: 200}, - 'data/examples/patchLetter/requests/patchLetter_ACCEPTED.json': {responsePath:'data/examples/patchLetter/responses/patchLetter_ACCEPTED.json',responseCode: 200}, - 'data/examples/patchLetter/requests/patchLetter_REJECTED.json': {responsePath:'data/examples/patchLetter/responses/patchLetter_REJECTED.json',responseCode: 200}, - 'data/examples/patchLetter/requests/patchLetter_PRINTED.json': {responsePath:'data/examples/patchLetter/responses/patchLetter_PRINTED.json',responseCode: 200}, - 'data/examples/patchLetter/requests/patchLetter_ENCLOSED.json': {responsePath:'data/examples/patchLetter/responses/patchLetter_ENCLOSED.json',responseCode: 200}, - 'data/examples/patchLetter/requests/patchLetter_CANCELLED.json': {responsePath:'data/examples/patchLetter/responses/patchLetter_CANCELLED.json',responseCode: 200}, - 'data/examples/patchLetter/requests/patchLetter_DISPATCHED.json': {responsePath:'data/examples/patchLetter/responses/patchLetter_DISPATCHED.json',responseCode: 200}, - 'data/examples/patchLetter/requests/patchLetter_DELIVERED.json': {responsePath:'data/examples/patchLetter/responses/patchLetter_DELIVERED.json',responseCode: 200}, - 'data/examples/patchLetter/requests/patchLetter_FAILED.json': {responsePath:'data/examples/patchLetter/responses/patchLetter_FAILED.json',responseCode: 200}, - 'data/examples/patchLetter/requests/patchLetter_RETURNED.json': {responsePath:'data/examples/patchLetter/responses/patchLetter_RETURNED.json',responseCode: 200}, + 'data/examples/patchLetter/requests/patchLetter_PENDING.json': {responsePath:'data/examples/patchLetter/responses/patchLetter_PENDING.json',responseCode: 202}, + 'data/examples/patchLetter/requests/patchLetter_ACCEPTED.json': {responsePath:'data/examples/patchLetter/responses/patchLetter_ACCEPTED.json',responseCode: 202}, + 'data/examples/patchLetter/requests/patchLetter_REJECTED.json': {responsePath:'data/examples/patchLetter/responses/patchLetter_REJECTED.json',responseCode: 202}, + 'data/examples/patchLetter/requests/patchLetter_PRINTED.json': {responsePath:'data/examples/patchLetter/responses/patchLetter_PRINTED.json',responseCode: 202}, + 'data/examples/patchLetter/requests/patchLetter_ENCLOSED.json': {responsePath:'data/examples/patchLetter/responses/patchLetter_ENCLOSED.json',responseCode: 202}, + 'data/examples/patchLetter/requests/patchLetter_CANCELLED.json': {responsePath:'data/examples/patchLetter/responses/patchLetter_CANCELLED.json',responseCode: 202}, + 'data/examples/patchLetter/requests/patchLetter_DISPATCHED.json': {responsePath:'data/examples/patchLetter/responses/patchLetter_DISPATCHED.json',responseCode: 202}, + 'data/examples/patchLetter/requests/patchLetter_DELIVERED.json': {responsePath:'data/examples/patchLetter/responses/patchLetter_DELIVERED.json',responseCode: 202}, + 'data/examples/patchLetter/requests/patchLetter_FAILED.json': {responsePath:'data/examples/patchLetter/responses/patchLetter_FAILED.json',responseCode: 202}, + 'data/examples/patchLetter/requests/patchLetter_RETURNED.json': {responsePath:'data/examples/patchLetter/responses/patchLetter_RETURNED.json',responseCode: 202}, 'data/examples/patchLetter/requests/patchLetter_INVALID.json': {responsePath:'data/examples/errors/responses/badRequest.json',responseCode: 400}, 'data/examples/patchLetter/requests/patchLetter_NOTFOUND.json': {responsePath:'data/examples/errors/responses/resourceNotFound.json',responseCode: 404}, }; @@ -93,24 +93,24 @@ async function patchLetterResponse(request) { } async function postLettersResponse(request) { - const patchLetterFileMap = { - 'data/examples/postLetter/requests/postLetters.json': {responsePath: 'data/examples/postLetter/responses/postLetters.json', responseCode: 200}, + const postLettersFileMap = { + 'data/examples/postLetter/requests/postLetters.json': {responsePath: 'data/examples/postLetter/responses/postLetters.json', responseCode: 202}, }; - return await mapExampleResponse(request, patchLetterFileMap); + return await mapExampleResponse(request, postLettersFileMap); } async function postMIResponse(request) { const postMIFileMap = { - 'data/examples/createMI/requests/createMI_SUCCESS.json': {responsePath: 'data/examples/createMI/responses/createMI_SUCCESS.json', responseCode: 200}, - 'data/examples/createMI/requests/createMI_INVALID.json': {responsePath:'data/examples/errors/responses/badRequest.json',responseCode: 400}, - 'data/examples/createMI/requests/createMI_NOTFOUND.json': {responsePath:'data/examples/errors/responses/resourceNotFound.json',responseCode: 404}, + 'data/examples/createMI/requests/createMI_request_SUCCESS.json': {responsePath: 'data/examples/createMI/responses/createMI_response_SUCCESS.json', responseCode: 200}, + 'data/examples/createMI/requests/createMI_request_INVALID.json': {responsePath:'data/examples/errors/responses/badRequest.json',responseCode: 400}, + 'data/examples/createMI/requests/createMI_request_NOTFOUND.json': {responsePath:'data/examples/errors/responses/resourceNotFound.json',responseCode: 404}, }; return await mapExampleResponse(request, postMIFileMap); } async function getLetterDataResponse(id) { const getLetterDataFileMap = { - '2AL5eYSWGzCHlGmzNxuqVusPxDg' : {responsePath: 'http://example.com', responseCode: 303}, + '2AL5eYSWGzCHlGmzNxuqVusPxDg' : {responsePath: 'https://www.w3.org/WAI/ER/tests/xhtml/testfiles/resources/pdf/dummy.pdf', responseCode: 303}, '2WL5eYSWGzCHlGmzNxuqVusPxDg' : {responsePath: 'data/examples/errors/responses/resourceNotFound.json', responseCode: 404}, }; return mapExampleGetResponse(id, getLetterDataFileMap); diff --git a/scripts/JWT/README.md b/scripts/JWT/README.md new file mode 100644 index 00000000..09e6d50e --- /dev/null +++ b/scripts/JWT/README.md @@ -0,0 +1,43 @@ +# Environments – Notify Supplier + +## Environment Matrix + +| Environment Name | Apigee Instance | Proxy URL | Application | Security Level | Private Key | KID | Notes / Env Code | +|------------------|-----------------|-----------|--------------|----------------|--------------|------|------------------| +| **Internal dev – PRs** | Internal | internal-dev.api.service.nhs.uk/nhs-notify-supplier-PR-XXX | Notify-Supplier-App-Restricted – Internal Dev 2 | level 0 | — | — | internal-dev PRs | +| **Internal dev – Main** | Internal | internal-dev.api.service.nhs.uk/nhs-notify-supplier | Notify Supplier – Application Restricted – Internal Dev Main | level 3 | [internal-dev-test-1.pem](https://eu-west-2.console.aws.amazon.com/systems-manager/parameters/%252Fnhs%252Fjwt%252Fkeys%252Finternal-dev-test-1.pem/description?region=eu-west-2&tab=Table) | internal-dev-test-1 | dev | +| **Ref** | Internal | ref.api.service.nhs.uk/nhs-notify-supplier | Notify Supplier – Application Restricted – Ref | level 3 | [ref-test-1.pem](https://eu-west-2.console.aws.amazon.com/systems-manager/parameters/%252Fnhs%252Fjwt%252Fkeys%252Fref-test-1.pem/description?region=eu-west-2&tab=Table) | ref-test-1 | prod | +| **Int** | External | int.api.service.nhs.uk/nhs-notify-supplier | Notify Supplier – Integration Testing | level 3 | [int-test-1.pem](https://eu-west-2.console.aws.amazon.com/systems-manager/parameters/%252Fnhs%252Fint%252Fjwt%252Fint-test-1.pem/description?region=eu-west-2&tab=Table) | int-test-1 | int | + +--- + +## How to Get the JWT Access Token + +1. Download the private key from AWS Systems Manager and save it locally as `$KID.pem`, for example `ref-test-1.pem`. + +2. Get the Apigee API key via one of the following: + - [Apigee Edge Console](https://apigee.com/edge) + - [Internal developer account](https://onboarding.prod.api.platform.nhs.uk/Index) + - [External developer account](https://dos-internal.ptl.api.platform.nhs.uk/Index) + +3. Run the Python script `get_bearer_token.py` with the parameters: + + ```bash + python get_bearer_token.py --kid --env --appid + ``` + + Example: + + ```bash + python get_bearer_token.py --kid ref-test-1 --env ref --appid 8Np3gFEw21JX7AGuokId0QEFTaOhG4Z2 + ``` + +4. The access token will be returned in the response + +5. Add the access token to your API requests as a header: + + | Header Name | Header Value | + |--------------|--------------| + | Authorization | `Bearer ` | + +--- diff --git a/scripts/JWT/get_bearer_token.py b/scripts/JWT/get_bearer_token.py new file mode 100644 index 00000000..e91759ca --- /dev/null +++ b/scripts/JWT/get_bearer_token.py @@ -0,0 +1,66 @@ +import uuid +import argparse +from time import time +import requests +import jwt # https://github.com/jpadilla/pyjwt + +ENV_TOKEN_URLS = { + "int": "https://int.api.service.nhs.uk/oauth2/token", + "internal-dev": "https://internal-dev.api.service.nhs.uk/oauth2-mock/token", + "prod": "https://api.service.nhs.uk/oauth2/token", + "ref": "https://ref.api.service.nhs.uk/oauth2/token", +} + +def main(): + ap = argparse.ArgumentParser(description="Fetch NHS access token using a signed client assertion (JWT).") + ap.add_argument("--kid", required=True, + help="Base name used for both the private key file (.pem) and the JWT header kid.") + ap.add_argument("--env", choices=ENV_TOKEN_URLS.keys(), required=True, + help="Environment to hit: int, internal-dev, or prod.") + ap.add_argument("--appid", help="Apigee Application ID (used for both sub and iss).") + args = ap.parse_args() + + kid = args.kid + private_key_file = f"{kid}.pem" + token_url = ENV_TOKEN_URLS[args.env] + + with open(private_key_file, "r") as f: + private_key = f.read() + + claims = { + "sub": args.appid, + "iss": args.appid, + "jti": str(uuid.uuid4()), + "aud": token_url, + "exp": int(time()) + 300, # 5mins in the future + } + + additional_headers = {"kid": kid} + + signed_jwt = jwt.encode( + claims, private_key, algorithm="RS512", headers=additional_headers + ) +# ----- 2) Exchange JWT for an access token ----- + form = { + "grant_type": "client_credentials", + "client_assertion_type": "urn:ietf:params:oauth:client-assertion-type:jwt-bearer", + "client_assertion": signed_jwt, + } + + resp = requests.post( + token_url, + headers={"content-type": "application/x-www-form-urlencoded"}, + data=form, + timeout=30, + ) + + # Raise for non-2xx responses, then print the token payload + resp.raise_for_status() + token_payload = resp.json() + + print("access_token:", token_payload.get("access_token")) + print("expires_in:", token_payload.get("expires_in")) + print("token_type:", token_payload.get("token_type")) + +if __name__ == "__main__": + main() diff --git a/scripts/build/copy-examples.sh b/scripts/build/copy-examples.sh new file mode 100755 index 00000000..e876d211 --- /dev/null +++ b/scripts/build/copy-examples.sh @@ -0,0 +1,6 @@ +find ./sandbox/data/examples -type f -name '*.json' \ + | while read f; do \ + out=./specification/api/components/examples/${f#./sandbox/data/examples/}; \ + mkdir -p $(dirname $out); \ + jq '{ value: . }' "$f" > "$out"; \ + done diff --git a/scripts/build/substitute_build_env.sh b/scripts/build/substitute_build_env.sh new file mode 100755 index 00000000..f11f3bf3 --- /dev/null +++ b/scripts/build/substitute_build_env.sh @@ -0,0 +1,26 @@ +#!/usr/bin/env bash +set -euo pipefail + +TEMPLATE="$1" +ENV_FILE="$2" +OUT="$3" + +# Extract variable names from the env file (left side of KEY=VALUE) +VARS=$(grep -E '^[A-Za-z_][A-Za-z0-9_]*=' "$ENV_FILE" | cut -d= -f1) + +# Build envsubst variable list ($VAR1 $VAR2 ...) +VARLIST="" +for v in $VARS; do + VARLIST="$VARLIST \$$v" +done +VARLIST="${VARLIST# }" # remove leading space + +# Export values from the env file +set -a +source "$ENV_FILE" +set +a + +# Run envsubst only on the selected variables +envsubst "$VARLIST" < "$TEMPLATE" > "$OUT" + +echo "Generated: $OUT" diff --git a/scripts/config/pre-commit.yaml b/scripts/config/pre-commit.yaml index 1a8a5313..85bce309 100644 --- a/scripts/config/pre-commit.yaml +++ b/scripts/config/pre-commit.yaml @@ -9,6 +9,10 @@ repos: - id: check-symlinks - id: detect-private-key - id: end-of-file-fixer + exclude: | + (?x)^( + internal/events/.*/.*\.schema\.json + )$ - id: forbid-new-submodules - id: mixed-line-ending - id: pretty-format-json @@ -18,7 +22,8 @@ repos: package(-lock)?\.json$ | sdk/.* | someotherdir/.* | - src/server/host/Properties/launchSettings.json + src/server/host/Properties/launchSettings.json | + internal/events/.*/.*\.schema\.json )$ # - id: ... diff --git a/scripts/config/vale/styles/config/vocabularies/words/accept.txt b/scripts/config/vale/styles/config/vocabularies/words/accept.txt index 9e5119ba..ead5f5a4 100644 --- a/scripts/config/vale/styles/config/vocabularies/words/accept.txt +++ b/scripts/config/vale/styles/config/vocabularies/words/accept.txt @@ -1,18 +1,19 @@ -API +actioned +API|api [A-Z]+s Bitwarden bot Cognito Cyber +[Dd]ev Dependabot -Dev devcontainer draw.io drawio +[Ee]nv endcapture endfor endraw -env Git[Hh]ub Gitleaks Grype diff --git a/scripts/devcontainer/postcreatecommand.sh b/scripts/devcontainer/postcreatecommand.sh index 39c334c8..eb7cc704 100755 --- a/scripts/devcontainer/postcreatecommand.sh +++ b/scripts/devcontainer/postcreatecommand.sh @@ -8,6 +8,8 @@ source ~/.zshrc make _install-dependencies # required before config to ensure python is available due to race between config:: make targets make config +pip install --user requests pyjwt cryptography + sudo gem install jekyll bundler jekyll --version && cd docs && bundle install diff --git a/scripts/init.mk b/scripts/init.mk index e12255c3..1b874692 100644 --- a/scripts/init.mk +++ b/scripts/init.mk @@ -46,8 +46,9 @@ _install-dependency: # Install asdf dependency - mandatory: name=[listed in the asdf install ${name} $(or ${version},) _install-dependencies: # Install all the dependencies listed in .tool-versions - for plugin in $$(grep ^[a-z] .tool-versions | sed 's/[[:space:]].*//'); do - make _install-dependency name="$${plugin}" + for plugin in $$(grep '^[a-z]' .tool-versions | cut -f1 -d' '); do \ + echo "Installing $${plugin}..."; \ + $(MAKE) _install-dependency name=$${plugin}; \ done clean:: # Remove all generated and temporary files (common) @Operations diff --git a/scripts/is_valid_increment.sh b/scripts/is_valid_increment.sh new file mode 100644 index 00000000..682f6cec --- /dev/null +++ b/scripts/is_valid_increment.sh @@ -0,0 +1,28 @@ +#!/usr/bin/bash +# Determines whether a version increment to a package is valid + +is_valid_increment() { + local current="$1" + local next="$2" + + if [[ "$current" == "null" ]]; then + if [[ "$next" == "1.0.0" ]]; then + return 0 # valid initial version + else + return 1 # invalid first version + fi + fi + + IFS='.' read -r curr_major curr_minor curr_patch <<< "$current" + IFS='.' read -r new_major new_minor new_patch <<< "$next" + + if (( new_major == curr_major && new_minor == curr_minor && new_patch == curr_patch + 1 )); then + return 0 # valid patch bump + elif (( new_major == curr_major && new_minor == curr_minor + 1 && new_patch == 0 )); then + return 0 # valid minor bump + elif (( new_major == curr_major + 1 && new_minor == 0 && new_patch == 0 )); then + return 0 # valid major bump + else + return 1 # invalid or skipped + fi +} diff --git a/scripts/publish-pact-contracts.sh b/scripts/publish-pact-contracts.sh new file mode 100755 index 00000000..93858de3 --- /dev/null +++ b/scripts/publish-pact-contracts.sh @@ -0,0 +1,25 @@ +#!/usr/bin/env bash + +published_version=$(npm view @nhsdigital/notify-supplier-api-consumer-contracts --json 2>/dev/null | jq -r '.["dist-tags"].latest') + +set -euo pipefail + +# Fail if there are uncommitted changes as this indicates unexpected changes to the contracts +git diff --quiet tests/pact-tests + +local_version=$(cat pact-contracts/package.json | jq -r '.version') + +branch=$(git branch --show-current) + +if [[ ! $branch == "main" ]]; then + echo "Not publishing package because this is not the main branch" + exit 0 +fi + +if [[ $local_version == $published_version ]]; then + echo "Local version is the same as the latest published version - skipping publish" + exit 0 +fi + +echo "Local version is different to the latest published version - publishing new version" +npm publish ./pact-contracts diff --git a/scripts/set-github-token.sh b/scripts/set-github-token.sh new file mode 100755 index 00000000..9586f5af --- /dev/null +++ b/scripts/set-github-token.sh @@ -0,0 +1,12 @@ +#!/usr/bin/env bash + +set -euo pipefail + +npm config ls -l | grep '/npm.pkg.github.com/:_authToken' -q && echo "Github token already exists" && exit 0 + +if [ -z "${GITHUB_TOKEN:-}" ]; then + read -p "Enter GitHub token: " GITHUB_TOKEN + export GITHUB_TOKEN +fi + +npm config --location user set //npm.pkg.github.com/:_authToken $GITHUB_TOKEN diff --git a/scripts/test-data/README.md b/scripts/test-data/README.md deleted file mode 100644 index 80a15aaf..00000000 --- a/scripts/test-data/README.md +++ /dev/null @@ -1,31 +0,0 @@ -# Test letter generator - -Simple script to generate test data. It uploads a PDF to the test S3 bucket and inserts a new record in the database. - -## Usage - -Log in the desired AWS account and then run the command below. You may need to set the AWS_REGION envar (eu-west-2) - -Note that the AWS account ID is required in order to resolve the bucket name. - -```bash -npm run cli -- create-letter \ - --supplier-id supplier-id \ - --environment pr147 \ - --awsAccountId 820178564574 \ - --letter-id letter-id \ - --group-id group-id \ - --specification-id specification-id \ - --status PENDING -``` - -```bash -npm run cli -- create-letter-batch \ - --supplier-id supplier-id \ - --environment main \ - --awsAccountId 820178564574 \ - --group-id group-id \ - --specification-id specification-id \ - --status PENDING \ - --count 10 -``` diff --git a/scripts/test-data/test_letter.pdf b/scripts/test-data/test_letter.pdf deleted file mode 100644 index e1cd34ba..00000000 Binary files a/scripts/test-data/test_letter.pdf and /dev/null differ diff --git a/scripts/tests/component.sh b/scripts/tests/component.sh new file mode 100644 index 00000000..751c5382 --- /dev/null +++ b/scripts/tests/component.sh @@ -0,0 +1,18 @@ +#!/bin/bash + +set -euo pipefail + +cd "$(git rev-parse --show-toplevel)" + +# run tests +cd tests +TEST_EXIT_CODE=0 +npm ci +npm run test:component || TEST_EXIT_CODE=$? +echo "TEST_EXIT_CODE=$TEST_EXIT_CODE" + +mkdir -p ../acceptance-test-report +cp -r ./playwright-report ../acceptance-test-report +[[ -e test-results ]] && cp -r ./test-results ../acceptance-test-report + +exit $TEST_EXIT_CODE diff --git a/scripts/tests/test.mk b/scripts/tests/test.mk index 742d123e..5875669e 100644 --- a/scripts/tests/test.mk +++ b/scripts/tests/test.mk @@ -71,10 +71,10 @@ test: # Run all the test tasks @Testing _test: set -e script="./scripts/tests/${name}.sh" - if [ -e "$${script}" ]; then - exec $${script} - else - echo "make test-${name} not implemented: $${script} not found" >&2 + if [ -e "$${script}" ]; then \ + exec $${script}; \ + else \ + echo "make test-${name} not implemented: $${script} not found" >&2 ; \ fi ${VERBOSE}.SILENT: \ diff --git a/scripts/utilities/letter-test-data/.eslintignore b/scripts/utilities/letter-test-data/.eslintignore new file mode 100644 index 00000000..1521c8b7 --- /dev/null +++ b/scripts/utilities/letter-test-data/.eslintignore @@ -0,0 +1 @@ +dist diff --git a/scripts/utilities/letter-test-data/.gitignore b/scripts/utilities/letter-test-data/.gitignore new file mode 100644 index 00000000..80323f7c --- /dev/null +++ b/scripts/utilities/letter-test-data/.gitignore @@ -0,0 +1,4 @@ +coverage +node_modules +dist +.reports diff --git a/scripts/utilities/letter-test-data/README.md b/scripts/utilities/letter-test-data/README.md new file mode 100644 index 00000000..87d5542f --- /dev/null +++ b/scripts/utilities/letter-test-data/README.md @@ -0,0 +1,61 @@ +# Test letter generator + +Simple script to generate test data. It uploads a PDF to the test S3 bucket and inserts a new record in the database. + +## Usage + +Log in the desired AWS account and then run the command below. You may need to set the AWS_REGION envar (eu-west-2) + +Note that the AWS account ID is required in order to resolve the bucket name. + +```bash +npm run cli -- create-letter \ + --supplier-id supplier-id \ + --environment pr147 \ + --awsAccountId 820178564574 \ + --letter-id letter-id \ + --group-id group-id \ + --specification-id specification-id \ + --status PENDING +``` + +```bash +npm run cli -- create-letter-batch \ + --supplier-id supplier-id \ + --environment main \ + --awsAccountId 820178564574 \ + --group-id group-id \ + --specification-id specification-id \ + --status PENDING \ + --count 10 +``` + +## Batch Creation Script + +For creating multiple batches with different specification and group IDs, use the bash wrapper script: + +```bash +./src/create-batch-letters.sh \ + --supplier-id supplier-id \ + --environment main \ + --awsAccountId 820178564574 \ + --count 25 \ + --status PENDING +``` + +This script creates 3 batches with the following configurations: + +- Batch 1: `--specification-id integration-specification-english --group-id group-english` +- Batch 2: `--specification-id integration-specification-braille --group-id group-accessible` +- Batch 3: `--specification-id integration-specification-arabic --group-id group-international` + +**Note:** The default configuration creates 2,505 letters total (835 letters × 3 batches) with an 18-month TTL. + +### Script Options + +- `--supplier-id` (required): Supplier ID for the letters +- `--environment` (required): Environment (e.g., pr147, main) +- `--awsAccountId` (required): AWS Account ID for S3 bucket resolution +- `--count` (optional): Number of letters per batch (default: 835) +- `--status` (optional): Letter status (default: PENDING) +- `--ttl-hours` (optional): TTL in hours (default: 13140, ~18 months) diff --git a/scripts/test-data/jest.config.ts b/scripts/utilities/letter-test-data/jest.config.ts similarity index 100% rename from scripts/test-data/jest.config.ts rename to scripts/utilities/letter-test-data/jest.config.ts diff --git a/scripts/test-data/package.json b/scripts/utilities/letter-test-data/package.json similarity index 75% rename from scripts/test-data/package.json rename to scripts/utilities/letter-test-data/package.json index c181c2cb..58be5cd6 100644 --- a/scripts/test-data/package.json +++ b/scripts/utilities/letter-test-data/package.json @@ -1,6 +1,8 @@ { "dependencies": { + "@aws-sdk/client-dynamodb": "^3.858.0", "@aws-sdk/client-s3": "^3.858.0", + "@aws-sdk/lib-dynamodb": "^3.858.0", "@internal/datastore": "*", "esbuild": "^0.25.11", "pino": "^9.7.0", @@ -11,9 +13,9 @@ "@types/jest": "^30.0.0", "jest": "^30.2.0", "jest-mock-extended": "^4.0.0", - "typescript": "^5.8.3" + "typescript": "^5.9.3" }, - "name": "nhs-notify-supplier-api-data-generator", + "name": "nhs-notify-supplier-api-letter-test-data-utility", "private": true, "scripts": { "cli": "tsx ./src/cli/index.ts", diff --git a/scripts/test-data/src/__test__/helpers/create_letter_helpers.test.ts b/scripts/utilities/letter-test-data/src/__test__/helpers/create_letter_helpers.test.ts similarity index 100% rename from scripts/test-data/src/__test__/helpers/create_letter_helpers.test.ts rename to scripts/utilities/letter-test-data/src/__test__/helpers/create_letter_helpers.test.ts diff --git a/scripts/test-data/src/cli/index.ts b/scripts/utilities/letter-test-data/src/cli/index.ts similarity index 98% rename from scripts/test-data/src/cli/index.ts rename to scripts/utilities/letter-test-data/src/cli/index.ts index 0e2a4517..915c392b 100644 --- a/scripts/test-data/src/cli/index.ts +++ b/scripts/utilities/letter-test-data/src/cli/index.ts @@ -1,5 +1,5 @@ import { hideBin } from "yargs/helpers"; -import yargs from "yargs/yargs"; +import yargs from 'yargs'; import { LetterStatusType } from "@internal/datastore/src/types"; import { randomUUID } from "crypto"; import { createLetter, createLetterDto } from "../helpers/create_letter_helpers"; @@ -54,7 +54,6 @@ async function main() { "DISPATCHED", "FAILED", "RETURNED", - "DESTROYED", "FORWARDED", "DELIVERED", ], @@ -132,7 +131,6 @@ async function main() { "DISPATCHED", "FAILED", "RETURNED", - "DESTROYED", "FORWARDED", "DELIVERED", ], diff --git a/scripts/utilities/letter-test-data/src/create-batch-letters.sh b/scripts/utilities/letter-test-data/src/create-batch-letters.sh new file mode 100755 index 00000000..eff9643e --- /dev/null +++ b/scripts/utilities/letter-test-data/src/create-batch-letters.sh @@ -0,0 +1,153 @@ +#!/bin/bash + +# Bash wrapper script for creating multiple letter batches +# This script creates 3 batches with different specification-id and group-id values + +set -e + +# Function to display usage +usage() { + echo "Usage: $0 --supplier-id --environment --awsAccountId [--count ] [--status ]" + echo "" + echo "Required parameters:" + echo " --supplier-id Supplier ID for the letters" + echo " --environment Environment (e.g., pr147, main, dev)" + echo " --awsAccountId AWS Account ID for S3 bucket resolution" + echo "" + echo "Optional parameters:" + echo " --count Number of letters per batch (default: 835)" + echo " --status Letter status (default: PENDING)" + echo " --ttl-hours TTL in hours (default: 13140)" + echo "" + echo "Example:" + echo " $0 --supplier-id supplier-123 --environment pr147 --awsAccountId 820178564574" + echo " $0 --supplier-id supplier-123 --environment main --awsAccountId 820178564574 --count 25 --status ACCEPTED" + exit 1 +} + +# Default values +COUNT=835 #3 batches = 2505 letters +STATUS="PENDING" +TTL_HOURS=13140 # Approximately 18 months + +# Parse command line arguments +while [[ $# -gt 0 ]]; do + case $1 in + --supplier-id) + SUPPLIER_ID="$2" + shift 2 + ;; + --environment) + ENVIRONMENT="$2" + shift 2 + ;; + --awsAccountId) + AWS_ACCOUNT_ID="$2" + shift 2 + ;; + --count) + COUNT="$2" + shift 2 + ;; + --status) + STATUS="$2" + shift 2 + ;; + --ttl-hours) + TTL_HOURS="$2" + shift 2 + ;; + -h|--help) + usage + ;; + *) + echo "Unknown parameter: $1" + usage + ;; + esac +done + +# Validate required parameters +if [[ -z "$SUPPLIER_ID" || -z "$ENVIRONMENT" || -z "$AWS_ACCOUNT_ID" ]]; then + echo "Error: Missing required parameters" + echo "" + usage +fi + +# Validate status +VALID_STATUSES=("PENDING" "ACCEPTED" "REJECTED" "PRINTED" "ENCLOSED" "CANCELLED" "DISPATCHED" "FAILED" "RETURNED" "FORWARDED" "DELIVERED") +if [[ ! " ${VALID_STATUSES[@]} " =~ " ${STATUS} " ]]; then + echo "Error: Invalid status '$STATUS'. Valid statuses: ${VALID_STATUSES[*]}" + exit 1 +fi + +# Validate count is a positive number +if ! [[ "$COUNT" =~ ^[1-9][0-9]*$ ]]; then + echo "Error: Count must be a positive integer" + exit 1 +fi + +echo "Creating letter batches with the following configuration:" +echo " Supplier ID: $SUPPLIER_ID" +echo " Environment: $ENVIRONMENT" +echo " AWS Account ID: $AWS_ACCOUNT_ID" +echo " Count per batch: $COUNT" +echo " Status: $STATUS" +echo " TTL Hours: $TTL_HOURS" +echo "" + +# Get the directory of this script to run npm from the correct location +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +PROJECT_DIR="$(dirname "$SCRIPT_DIR")" + +# Change to the project directory +cd "$PROJECT_DIR" + +# Define the three batches with different specification and group IDs +BATCHES=( + "integration-specification-english:group-english" + "integration-specification-braille:group-accessible" + "integration-specification-arabic:group-international" +) + +# Counter for tracking batch creation +BATCH_COUNTER=1 +TOTAL_BATCHES=${#BATCHES[@]} +TOTAL_LETTERS=$((COUNT * TOTAL_BATCHES)) + +echo "Creating $TOTAL_BATCHES batches with $COUNT letters each ($TOTAL_LETTERS total letters)..." +echo "" + +# Create each batch +for batch in "${BATCHES[@]}"; do + # Parse specification-id and group-id from the batch definition + IFS=':' read -r SPEC_ID GROUP_ID <<< "$batch" + + echo "[$BATCH_COUNTER/$TOTAL_BATCHES] Creating batch with specification-id: $SPEC_ID, group-id: $GROUP_ID-$SUPPLIER_ID" + + # Run the npm command + npm run cli -- create-letter-batch \ + --supplier-id "$SUPPLIER_ID" \ + --environment "$ENVIRONMENT" \ + --awsAccountId "$AWS_ACCOUNT_ID" \ + --specification-id "$SPEC_ID" \ + --group-id "$GROUP_ID-$SUPPLIER_ID" \ + --status "$STATUS" \ + --count "$COUNT" \ + --ttl-hours "$TTL_HOURS" + + if [[ $? -eq 0 ]]; then + echo "✓ Batch $BATCH_COUNTER completed successfully" + else + echo "✗ Batch $BATCH_COUNTER failed" + exit 1 + fi + + echo "" + ((BATCH_COUNTER++)) +done + +echo "🎉 All batches created successfully!" +echo "Total letters created: $TOTAL_LETTERS" +echo "Supplier ID: $SUPPLIER_ID" +echo "Environment: $ENVIRONMENT" diff --git a/scripts/test-data/src/helpers/create_letter_helpers.ts b/scripts/utilities/letter-test-data/src/helpers/create_letter_helpers.ts similarity index 90% rename from scripts/test-data/src/helpers/create_letter_helpers.ts rename to scripts/utilities/letter-test-data/src/helpers/create_letter_helpers.ts index e1eb0719..062154ec 100644 --- a/scripts/test-data/src/helpers/create_letter_helpers.ts +++ b/scripts/utilities/letter-test-data/src/helpers/create_letter_helpers.ts @@ -33,7 +33,7 @@ export async function createLetter(params: { targetFilename, ); - const letter: Omit = { + const letter: Omit = { id: letterId, supplierId, specificationId, @@ -65,7 +65,7 @@ export function createLetterDto(params: { url, } = params; - const letter: Omit = { + const letter: Omit = { id: letterId, supplierId, specificationId, diff --git a/scripts/test-data/src/helpers/s3_helpers.ts b/scripts/utilities/letter-test-data/src/helpers/s3_helpers.ts similarity index 100% rename from scripts/test-data/src/helpers/s3_helpers.ts rename to scripts/utilities/letter-test-data/src/helpers/s3_helpers.ts diff --git a/scripts/test-data/src/infrastructure/letter-repo-factory.ts b/scripts/utilities/letter-test-data/src/infrastructure/letter-repo-factory.ts similarity index 90% rename from scripts/test-data/src/infrastructure/letter-repo-factory.ts rename to scripts/utilities/letter-test-data/src/infrastructure/letter-repo-factory.ts index 269642c0..e0060337 100644 --- a/scripts/test-data/src/infrastructure/letter-repo-factory.ts +++ b/scripts/utilities/letter-test-data/src/infrastructure/letter-repo-factory.ts @@ -1,6 +1,6 @@ import { DynamoDBClient } from '@aws-sdk/client-dynamodb'; import { DynamoDBDocumentClient } from '@aws-sdk/lib-dynamodb'; -import pino from 'pino'; +import { pino } from 'pino'; import { LetterRepository } from '@internal/datastore'; export function createLetterRepository(environment: string, ttlHours:number): LetterRepository { @@ -9,7 +9,7 @@ export function createLetterRepository(environment: string, ttlHours:number): Le const log = pino(); const config = { lettersTableName: `nhs-${environment}-supapi-letters`, - ttlHours: ttlHours, + lettersTtlHours: ttlHours, }; return new LetterRepository(docClient, log, config); diff --git a/scripts/utilities/letter-test-data/test_letter.pdf b/scripts/utilities/letter-test-data/test_letter.pdf new file mode 100644 index 00000000..1d0f763d Binary files /dev/null and b/scripts/utilities/letter-test-data/test_letter.pdf differ diff --git a/scripts/test-data/tsconfig.json b/scripts/utilities/letter-test-data/tsconfig.json similarity index 65% rename from scripts/test-data/tsconfig.json rename to scripts/utilities/letter-test-data/tsconfig.json index 24902365..730d18dd 100644 --- a/scripts/test-data/tsconfig.json +++ b/scripts/utilities/letter-test-data/tsconfig.json @@ -1,6 +1,6 @@ { "compilerOptions": {}, - "extends": "../../tsconfig.base.json", + "extends": "../../../tsconfig.base.json", "include": [ "src/**/*", "jest.config.ts" diff --git a/scripts/utilities/supplier-data/.eslintignore b/scripts/utilities/supplier-data/.eslintignore new file mode 100644 index 00000000..1521c8b7 --- /dev/null +++ b/scripts/utilities/supplier-data/.eslintignore @@ -0,0 +1 @@ +dist diff --git a/scripts/utilities/supplier-data/.gitignore b/scripts/utilities/supplier-data/.gitignore new file mode 100644 index 00000000..80323f7c --- /dev/null +++ b/scripts/utilities/supplier-data/.gitignore @@ -0,0 +1,4 @@ +coverage +node_modules +dist +.reports diff --git a/scripts/utilities/supplier-data/README.md b/scripts/utilities/supplier-data/README.md new file mode 100644 index 00000000..2d3164d9 --- /dev/null +++ b/scripts/utilities/supplier-data/README.md @@ -0,0 +1,30 @@ +# Supplier Utility + +Simple scripts to manipulate supplier data within the Suppliers table via the internal datastore definitions. + +## Usage + +Log in the desired AWS account and then run the command below. You may need to set the AWS_REGION envar (eu-west-2) + +Note that the AWS account ID is required in order to resolve the bucket name. + +```bash +npm run cli -- put-supplier \ + --id supplier-id \ + --name supplier \ + --apimId supplier-apim-id \ + --status ENABLED + --environment pr147 \ +``` + +```bash +npm run cli -- get-supplier-by-id \ + --id supplier-id \ + --environment main \ +``` + +```bash +npm run cli -- get-supplier-by-apim-id \ + --apimId apim-supplier-id + --environment main \ +``` diff --git a/scripts/utilities/supplier-data/jest.config.ts b/scripts/utilities/supplier-data/jest.config.ts new file mode 100644 index 00000000..f2972c27 --- /dev/null +++ b/scripts/utilities/supplier-data/jest.config.ts @@ -0,0 +1,61 @@ +import type { Config } from 'jest'; + +export const baseJestConfig: Config = { + preset: 'ts-jest', + + // Automatically clear mock calls, instances, contexts and results before every test + clearMocks: true, + + // Indicates whether the coverage information should be collected while executing the test + collectCoverage: true, + + // The directory where Jest should output its coverage files + coverageDirectory: './.reports/unit/coverage', + + // Indicates which provider should be used to instrument code for coverage + coverageProvider: 'babel', + + coverageThreshold: { + global: { + branches: 100, + functions: 100, + lines: 100, + statements: -10, + }, + }, + + coveragePathIgnorePatterns: ['/__tests__/'], + transform: { '^.+\\.ts$': 'ts-jest' }, + testPathIgnorePatterns: ['.build'], + testMatch: ['**/?(*.)+(spec|test).[jt]s?(x)'], + + // Use this configuration option to add custom reporters to Jest + reporters: [ + 'default', + [ + 'jest-html-reporter', + { + pageTitle: 'Test Report', + outputPath: './.reports/unit/test-report.html', + includeFailureMsg: true, + }, + ], + ], + + // The test environment that will be used for testing + testEnvironment: 'jsdom', +}; + +const utilsJestConfig = { + ...baseJestConfig, + + testEnvironment: 'node', + + coveragePathIgnorePatterns: [ + ...(baseJestConfig.coveragePathIgnorePatterns ?? []), + 'cli/index.ts', + 'suppliers-repo-factory.ts', + ], +}; + +export default utilsJestConfig; diff --git a/scripts/utilities/supplier-data/package.json b/scripts/utilities/supplier-data/package.json new file mode 100644 index 00000000..0d3b48e9 --- /dev/null +++ b/scripts/utilities/supplier-data/package.json @@ -0,0 +1,27 @@ +{ + "dependencies": { + "@aws-sdk/client-dynamodb": "^3.858.0", + "@aws-sdk/lib-dynamodb": "^3.858.0", + "@internal/datastore": "*", + "esbuild": "^0.25.11", + "pino": "^9.7.0", + "yargs": "^17.7.2" + }, + "devDependencies": { + "@tsconfig/node22": "^22.0.2", + "@types/jest": "^30.0.0", + "jest": "^30.2.0", + "jest-mock-extended": "^4.0.0", + "typescript": "^5.8.3" + }, + "name": "nhs-notify-supplier-api-suppliers-data-utility", + "private": true, + "scripts": { + "cli": "tsx ./src/cli/index.ts", + "lint": "eslint .", + "lint:fix": "eslint . --fix", + "test:unit": "echo \"Supplier data utility has no unit tests\"", + "typecheck": "tsc --noEmit" + }, + "version": "0.0.1" +} diff --git a/scripts/utilities/supplier-data/src/cli/index.ts b/scripts/utilities/supplier-data/src/cli/index.ts new file mode 100644 index 00000000..3efab279 --- /dev/null +++ b/scripts/utilities/supplier-data/src/cli/index.ts @@ -0,0 +1,120 @@ +import { hideBin } from "yargs/helpers"; +import yargs from 'yargs'; +import { LetterStatusType } from "@internal/datastore/src/types"; +import { randomUUID } from "crypto"; +import { createSupplierRepository } from "../infrastructure/suppliers-repo-factory"; + + + +async function main() { + await yargs(hideBin(process.argv)) + .command( + "put-supplier", + "Create or update a supplier", + { + environment: { + type: "string", + demandOption: true, + }, + "id": { + type: "string", + demandOption: true, + }, + "name": { + type: "string", + demandOption: true, + }, + "apimId": { + type: "string", + demandOption: true, + }, + status: { + type: "string", + demandOption: true, + choices: [ + "ENABLED", + "DISABLED" + ], + }, + }, + async (argv) => { + // parse args + const id = argv.id; + const name = argv.name; + const apimId = argv.apimId; + const status = argv.status as "ENABLED" | "DISABLED"; + + const environment = argv.environment; + + const supplierRepository = createSupplierRepository(environment); + + const putResult = await supplierRepository.putSupplier({ + id, + name, + apimId, + status, + }); + + console.log(`PUT successful ${JSON.stringify(putResult)}`); + } + ) + .command( + "get-supplier-by-id", + "Get a supplier by their Supplier ID", + { + "id": { + type: "string", + demandOption: true, + }, + environment: { + type: "string", + demandOption: true, + }, + }, + async (argv) => { + + const id = argv.id; + const environment = argv.environment; + + const supplierRepository = createSupplierRepository(environment); + + const getResult = await supplierRepository.getSupplierById(id); + + console.log(`GET successful: ${JSON.stringify(getResult)}`); + }, + ) + .command( + "get-supplier-by-apim-id", + "Get a supplier by their APIM ID", + { + "apimId": { + type: "string", + demandOption: true, + }, + environment: { + type: "string", + demandOption: true, + }, + }, + async (argv) => { + + const apimId = argv.apimId; + const environment = argv.environment; + + const supplierRepository = createSupplierRepository(environment); + + const getResult = await supplierRepository.getSupplierByApimId(apimId); + + console.log(`GET successful: ${JSON.stringify(getResult)}`); + }, + ) + .demandCommand(1) + .parse(); +} + +if (require.main === module) { + main().catch((err) => { + console.error(err); + process.exitCode = 1; + }); +} diff --git a/scripts/utilities/supplier-data/src/infrastructure/suppliers-repo-factory.ts b/scripts/utilities/supplier-data/src/infrastructure/suppliers-repo-factory.ts new file mode 100644 index 00000000..3bddc616 --- /dev/null +++ b/scripts/utilities/supplier-data/src/infrastructure/suppliers-repo-factory.ts @@ -0,0 +1,15 @@ +import { DynamoDBClient } from '@aws-sdk/client-dynamodb'; +import { DynamoDBDocumentClient } from '@aws-sdk/lib-dynamodb'; +import { pino } from 'pino'; +import { SupplierRepository } from '@internal/datastore'; + +export function createSupplierRepository(environment: string): SupplierRepository { + const ddbClient = new DynamoDBClient({}); + const docClient = DynamoDBDocumentClient.from(ddbClient); + const log = pino(); + const config = { + suppliersTableName: `nhs-${environment}-supapi-suppliers`, + }; + + return new SupplierRepository(docClient, log, config); +} diff --git a/scripts/utilities/supplier-data/tsconfig.json b/scripts/utilities/supplier-data/tsconfig.json new file mode 100644 index 00000000..730d18dd --- /dev/null +++ b/scripts/utilities/supplier-data/tsconfig.json @@ -0,0 +1,8 @@ +{ + "compilerOptions": {}, + "extends": "../../../tsconfig.base.json", + "include": [ + "src/**/*", + "jest.config.ts" + ] +} diff --git a/sdk/Makefile b/sdk/Makefile index 76092481..ba052471 100644 --- a/sdk/Makefile +++ b/sdk/Makefile @@ -3,7 +3,7 @@ VERSION ?= "" SHELL = /bin/bash build: version # Build the project artefact @Pipeline - VER=$$(cat .version) && cd .. && npm run build --buildver=$$VER && cd sdk + VER=$$(cat .version) && cd .. && make build-yml-oas-spec APIM_ENV=prod && npm run generate --buildver=$$VER && cd sdk ./swagger-static.sh clean: # Clean-up project resources (main) @Operations diff --git a/sdk/_config.version.yml b/sdk/_config.version.yml index be925300..92f08fcc 100644 --- a/sdk/_config.version.yml +++ b/sdk/_config.version.yml @@ -1 +1 @@ -version: 0.1 +version: 1.0.1-20251212.155122+85233bf diff --git a/specification/api/components/documentation/APIDescription.md b/specification/api/components/documentation/APIDescription.md index 9a2db448..7a6311fe 100644 --- a/specification/api/components/documentation/APIDescription.md +++ b/specification/api/components/documentation/APIDescription.md @@ -1,26 +1,36 @@ ## Overview -API for letter suppliers to integrate with NHS Notify. +Use this API to retrieve letters to be printed. This API lets you: -* Get lists of letters allocated to you -* Download letter PDFs and metadata -* Update and manage letter statuses -* Submit and retrieve management information (MI) +* get lists of letters allocated to you +* download letter PDFs and metadata +* update and manage letter statuses +* submit and retrieve management information (MI) This specification represents the in-development 'next' version of the API schema -and should be treated as unstable - -Use this API to retrieve letters to be printed +and should be treated as unstable. ## Who can use this API - The NHS Notify Supplier API is designed for approved print service suppliers who support the delivery of physical letters through the [NHS Notify](https://digital.nhs.uk/services/nhs-notify) platform. +The NHS Notify Supplier API is designed for approved print service suppliers who support the delivery of physical letters through the [NHS Notify](https://digital.nhs.uk/services/nhs-notify) platform. -## Related APIs +## Access Modes -The [NHS Notify API](https://digital.nhs.uk/developer/api-catalogue/nhs-notify) is used to send messages to citizens via NHS App, email, text message or letter. +This API has one access mode. It is: + +* restricted access + +### Restricted access + +This access mode is [application-restricted](https://digital.nhs.uk/developer/guides-and-documentation/security-and-authorisation#application-restricted-apis), meaning we authenticate and authorise the calling application but not the end user. + +Authentication and authorisation of end users is the responsibility of your application. + +To use this access mode, use this security pattern: + +* [Application-restricted RESTful API - signed JWT authentication](https://digital.nhs.uk/developer/guides-and-documentation/security-and-authorisation/application-restricted-restful-apis-signed-jwt-authentication) ## API status and roadmap @@ -28,11 +38,15 @@ This API is [in production, beta](https://digital.nhs.uk/developer/guides-and-do We may make additive non-breaking changes to the API without notice, for example the addition of fields to a response or callback, or new optional fields to a request. -## Service Level +## Service level This service is a [silver](https://digital.nhs.uk/services/reference-guide#service-levels) service, meaning it is available 24 hours a day, 365 days a year and supported from 8am to 6pm, Monday to Friday excluding bank holidays. For more details, see [service levels](https://digital.nhs.uk/developer/guides-and-documentation/reference-guide#service-levels). +## Rate limits + +The default rate limit is 300TPS (Transactions Per Second), per app. If you require a higher rate limit please [contact us](https://digital.nhs.uk/developer/help-and-support). or raise this during the onboarding process. + ## Technology This API is a [REST-based](https://digital.nhs.uk/developer/guides-and-documentation/our-api-technologies#basic-rest) API. @@ -62,15 +76,25 @@ This API is available on the internet and, indirectly on the [Health and Social For more details see [Network access for APIs](https://digital.nhs.uk/developer/guides-and-documentation/network-access-for-apis). -## Security and authorisation +## Errors -This API is [application-restricted](https://digital.nhs.uk/developer/guides-and-documentation/security-and-authorisation#application-restricted-apis), meaning we authenticate the calling application but not the end user. +We use standard HTTP status codes to show whether an API request succeeded or not. They are usually in the range: -Authentication and authorisation of end users is the responsibility of your application. +* 200 to 299 if it succeeded, including code 202 if it was accepted by an API that needs to wait for further action +* 400 to 499 if it failed because of a client error by your application +* 500 to 599 if it failed because of an error on our server -To access this API, use the following security pattern: +Errors specific to each API are shown in the Endpoints section, under Response. See our [reference guide](https://digital.nhs.uk/developer/guides-and-documentation/reference-guide#http-status-codes) for more on errors. -* [Application-restricted RESTful API - signed JWT authentication](https://digital.nhs.uk/developer/guides-and-documentation/security-and-authorisation/application-restricted-restful-apis-signed-jwt-authentication) +Your API-calling application should have a mechanism to automatically try again, for example by giving status information to your end user, before giving up. See our [reference guide](https://digital.nhs.uk/developer/guides-and-documentation/reference-guide#error-handling) for more information about error handling. + +## Open source + +You might find the following [open source](https://digital.nhs.uk/developer/guides-and-documentation/reference-guide#open-source) resources useful: + +| Resource | Description | Links | +|---------------------------|----------------------------------------------------------------------|--------------------------------------------------------------------------------| +| Notify Supplier API | Source code for the API proxy, sandbox and specification. | [GitHub repo](https://github.com/NHSDigital/notify-supplier-api) | ## Environments and testing @@ -91,7 +115,7 @@ Our [sandbox environment](https://digital.nhs.uk/developer/guides-and-documentat For details of sandbox test scenarios, or to try out sandbox using our 'Try this API' feature, see the documentation for each endpoint. -Alternatively, you can try out the sandbox using our Postman collection +Alternatively, you can try out the sandbox using our Postman collection. You can find our postman collection source in our [public repository on github](https://github.com/NHSDigital/nhs-notify-supplier-api/tree/main/postman). @@ -105,16 +129,28 @@ Our integration test environment: You need to get your software approved by us before it can go live with this API. -You will also need to follow our steps to - TBD - ### Production smoke testing Before go-live, you must complete a smoke test in the NHS Notify production environment. The smoke test confirms that your live credentials, connectivity, and print workflow operate correctly end-to-end. It will be carried out in coordination with the NHS Notify Supplier API team. -You will retrieve and print one or more live test letters through the production API, send the printed output to the address provided, and submit a Management Information (MI) update for verification. + +The process is as follows: + +* retrieve and print one or more live test letters through the production API. +* send the printed output to the address provided. +* submit a Management Information (MI) update for verification. + The NHS Notify team will configure your production access, review your results, and confirm that your output meets NHS Notify print specifications. -### Onboarding +## Onboarding You need to get your software approved by us before it can go live with this API. -You will also need to be an approved NHS letter supplier under the framework agreement (ADD link) and nominate your technical and operational contacts +You will also need to be an approved NHS letter supplier under the framework agreement and nominate your technical and operational contacts. + +## Related APIs + +The [NHS Notify API](https://digital.nhs.uk/developer/api-catalogue/nhs-notify) is used to send messages to citizens via NHS App, email, text message or letter. + +## Contact us + +For help and support connecting to our APIs and to join our developer community, see [Help and support building healthcare software](https://digital.nhs.uk/developer/help-and-support). diff --git a/specification/api/components/documentation/createMI.md b/specification/api/components/documentation/createMI.md index 17aff50e..0063296e 100644 --- a/specification/api/components/documentation/createMI.md +++ b/specification/api/components/documentation/createMI.md @@ -1,10 +1,10 @@ ## Overview -Use this endpoint to send management or operational metrics relating to letter processing and print fulfilment +Use this endpoint to send management or operational metrics relating to letter processing and print fulfilment. -When you submit a create management information request, the endpoint will respond with a c201 (Created) response code along with the created data including a unique id for the record or an unsuccessful (4xx/5xx) response +When you submit a create management information request, the endpoint will respond with a 201 (Created) response code along with the created data including a unique id for the record or an unsuccessful (4xx/5xx) response. -Rate limiting applies. On excess requests, you may receive **429 Too Many Requests** (example error code(s): `NOTIFY_QUOTA`). Back off and retry later +Rate limiting applies. On excess requests, you may receive **429 Too Many Requests** (example error code(s): `NOTIFY_QUOTA`). Back off and retry later. ## Sandbox test scenarios diff --git a/specification/api/components/documentation/getDataId.md b/specification/api/components/documentation/getDataId.md index 81e742e1..c83a9241 100644 --- a/specification/api/components/documentation/getDataId.md +++ b/specification/api/components/documentation/getDataId.md @@ -2,7 +2,7 @@ Use this endpoint to get letter data, including downloading the letter's print-ready PDF file. -Rate limiting applies. On excess requests, you may receive **429 Too Many Requests** (example error code(s): `NOTIFY_QUOTA`). Back off and retry later +Rate limiting applies. On excess requests, you may receive **429 Too Many Requests** (example error code(s): `NOTIFY_QUOTA`). Back off and retry later. ## Sandbox test scenarios @@ -10,5 +10,5 @@ You can test the following scenarios in our sandbox environment. |Scenario|Request ID|Response| |--------|-------|--------| -|Success | 2AL5eYSWGzCHlGmzNxuqVusPxDg | Returns 303 (See Other) and URL to [http://example.com](http://example.com) in the Location header | -|Not Found |2WL5eYSWGzCHlGmzNxuqVusPxDg | Returns 404 Not found and error details in the response | +|Success | 2AL5eYSWGzCHlGmzNxuqVusPxDg | Returns 303 (See Other) and URL to an example PDF in the Location header | +|Not found |2WL5eYSWGzCHlGmzNxuqVusPxDg | Returns 404 Not found and error details in the response | diff --git a/specification/api/components/documentation/getLetterStatus.md b/specification/api/components/documentation/getLetterStatus.md index 257d4d7d..b1815343 100644 --- a/specification/api/components/documentation/getLetterStatus.md +++ b/specification/api/components/documentation/getLetterStatus.md @@ -2,7 +2,7 @@ Use this endpoint to get the current status of a single letter by its ID. -Rate limiting applies. On excess requests, you may receive **429 Too Many Requests** (example error code(s): `NOTIFY_QUOTA`). Back off and retry later +Rate limiting applies. On excess requests, you may receive **429 Too Many Requests** (example error code(s): `NOTIFY_QUOTA`). Back off and retry later. ## Sandbox test scenarios @@ -20,3 +20,20 @@ You can test the following scenarios in our sandbox environment | Retrieve a CANCELLED letter status | `2XL5eYSWGzCHlGmzNxuqVusPxDg`| | Retrieve a FAILED letter status | `2YL5eYSWGzCHlGmzNxuqVusPxDg`| | Retrieve a RETURNED letter status | `2ZL5eYSWGzCHlGmzNxuqVusPxDg`| + +### Example Error Codes + +Examples of reason codes and text that may be returned include (but are not limited to) + +| Reason Code | Reason Text | +|-------------|----------------------------| +|R01 |Addressee gone away | +|R02 |Address incomplete | +|R03 |Address inaccessible | +|R04 |Addressee unknown | +|R05 |Addressee gone away/Refused | +|R06 |Not called for | +|R07 |No such address | +|R08 |No reason given | +|R09 |Deceased | +|R10 |Miscellaneous | diff --git a/specification/api/components/documentation/headDataId.md b/specification/api/components/documentation/headDataId.md index 66e98407..7770d133 100644 --- a/specification/api/components/documentation/headDataId.md +++ b/specification/api/components/documentation/headDataId.md @@ -9,4 +9,4 @@ You can test the following scenarios in our sandbox environment. |Scenario|Request ID|Response| |--------|-------|--------| |Success | 2AL5eYSWGzCHlGmzNxuqVusPxDg | Returns 200 (OK)| -|Not Found |2WL5eYSWGzCHlGmzNxuqVusPxDg | Returns 404 (Not found) | +|Not found |2WL5eYSWGzCHlGmzNxuqVusPxDg | Returns 404 (Not found) | diff --git a/specification/api/components/documentation/listLetters.md b/specification/api/components/documentation/listLetters.md index 337033c7..84cc4160 100644 --- a/specification/api/components/documentation/listLetters.md +++ b/specification/api/components/documentation/listLetters.md @@ -5,4 +5,4 @@ Use this endpoint to poll letters which are ready to be printed. Returns letters whose `status` is **PENDING**. Use `limit` to control list size (max 2500). -Rate limiting applies. On excess requests, you may receive **429 Too Many Requests** (example error code(s): `NOTIFY_QUOTA`). Back off and retry later +Rate limiting applies. On excess requests, you may receive **429 Too Many Requests** (example error code(s): `NOTIFY_QUOTA`). Back off and retry later. diff --git a/specification/api/components/documentation/patchLetter.md b/specification/api/components/documentation/patchLetter.md index f577051e..e29b229d 100644 --- a/specification/api/components/documentation/patchLetter.md +++ b/specification/api/components/documentation/patchLetter.md @@ -2,7 +2,7 @@ Use this endpoint to update the status of a letter by submitting the new status in the request body, optionally providing a reason code and text. -When you make a PATCH request with your application, the endpoint will respond with a successful (200) response code, along with the updated patient resource or an unsuccessful (4xx/5xx) response. +When you make a PATCH request with your application, the endpoint will respond with an accepted (202) response code or an unsuccessful (4xx/5xx) response. Rate limiting applies. On excess requests, you may receive **429 Too Many Requests** (example error code(s): `NOTIFY_QUOTA`). Back off and retry later. @@ -13,7 +13,6 @@ Allowed `status` values that can be used to are: - `ACCEPTED` - `CANCELLED` - `DELIVERED` -- `DESTROYED` - `DISPATCHED` - `ENCLOSED` - `FAILED` @@ -25,3 +24,20 @@ Allowed `status` values that can be used to are: It is not possible to update a letter to status of `PENDING`. Optionally a `reasonCode` and `reasonText` explaining the status (for example, validation failures) can be included in the request body. + +### Example Error Codes + +Examples of reason codes and text that may be returned include (but are not limited to) + +| Reason Code | Reason Text | +|-------------|----------------------------| +|R01 |Addressee gone away | +|R02 |Address incomplete | +|R03 |Address inaccessible | +|R04 |Addressee unknown | +|R05 |Addressee gone away/Refused | +|R06 |Not called for | +|R07 |No such address | +|R08 |No reason given | +|R09 |Deceased | +|R10 |Miscellaneous | diff --git a/specification/api/components/documentation/postLetters.md b/specification/api/components/documentation/postLetters.md index b2ebee69..e1b2b127 100644 --- a/specification/api/components/documentation/postLetters.md +++ b/specification/api/components/documentation/postLetters.md @@ -4,7 +4,7 @@ Use this endpoint to update the status for (example, PRINTED, DISPATCHED, DELIVE Use this endpoint when you need to report status changes for several letters at once. -When you make a POST update request with, the endpoint will respond with a successful (202) response code or an unsuccessful (4xx/5xx) response. +When you make a POST update request with the endpoint, it will respond with a successful (202) response code or an unsuccessful (4xx/5xx) response. Rate limiting applies. On excess requests, you may receive **429 Too Many Requests** (example error code(s): `NOTIFY_QUOTA`). Back off and retry later. @@ -15,7 +15,6 @@ Allowed `status` values that can be used to are: - `ACCEPTED` - `CANCELLED` - `DELIVERED` -- `DESTROYED` - `DISPATCHED` - `ENCLOSED` - `FAILED` @@ -26,4 +25,23 @@ Allowed `status` values that can be used to are: It is not possible to update a letter to status of `PENDING`. +The request should not contain multiple letter objects with the same ID. + Optionally a `reasonCode` and `reasonText` explaining the status (for example, validation failures) can be included in the request body for each update. + +### Example Error Codes + +Examples of reason codes and text that may be returned include (but are not limited to) + +| Reason Code | Reason Text | +|-------------|----------------------------| +|R01 |Addressee gone away | +|R02 |Address incomplete | +|R03 |Address inaccessible | +|R04 |Addressee unknown | +|R05 |Addressee gone away/Refused | +|R06 |Not called for | +|R07 |No such address | +|R08 |No reason given | +|R09 |Deceased | +|R10 |Miscellaneous | diff --git a/specification/api/components/endpoints/createMI.yml b/specification/api/components/endpoints/createMI.yml index 09d0ebce..0032ae07 100644 --- a/specification/api/components/endpoints/createMI.yml +++ b/specification/api/components/endpoints/createMI.yml @@ -11,9 +11,9 @@ responses: $ref: "../responses/postMI201.yml" '400': $ref: "../responses/errors/badRequest.yml" - '404': - $ref: "../responses/errors/resourceNotFound.yml" '429': $ref: "../responses/errors/tooManyRequests.yml" '500': $ref: "../responses/errors/serverError.yml" + '502': + $ref: "../responses/errors/badGateway.yml" diff --git a/specification/api/components/endpoints/getDataId.yml b/specification/api/components/endpoints/getDataId.yml index c7574df4..1c5aef45 100644 --- a/specification/api/components/endpoints/getDataId.yml +++ b/specification/api/components/endpoints/getDataId.yml @@ -13,3 +13,5 @@ responses: $ref: "../responses/errors/tooManyRequests.yml" "500": $ref: "../responses/errors/serverError.yml" + "502": + $ref: "../responses/errors/badGateway.yml" diff --git a/specification/api/components/endpoints/getLetterStatus.yml b/specification/api/components/endpoints/getLetterStatus.yml index bee57b46..a7290dcf 100644 --- a/specification/api/components/endpoints/getLetterStatus.yml +++ b/specification/api/components/endpoints/getLetterStatus.yml @@ -11,5 +11,7 @@ responses: $ref: "../responses/errors/tooManyRequests.yml" "500": $ref: "../responses/errors/serverError.yml" + "502": + $ref: "../responses/errors/badGateway.yml" tags: - letter diff --git a/specification/api/components/endpoints/listLetters.yml b/specification/api/components/endpoints/listLetters.yml index ab877e52..638f3917 100644 --- a/specification/api/components/endpoints/listLetters.yml +++ b/specification/api/components/endpoints/listLetters.yml @@ -14,9 +14,9 @@ responses: $ref: "../responses/getLetters200.yml" '400': $ref: "../responses/errors/listLetters/listLettersBadRequest.yml" - '404': - $ref: "../responses/errors/resourceNotFound.yml" '429': $ref: "../responses/errors/tooManyRequests.yml" '500': $ref: "../responses/errors/serverError.yml" + '502': + $ref: "../responses/errors/badGateway.yml" diff --git a/specification/api/components/endpoints/patchLetter.yml b/specification/api/components/endpoints/patchLetter.yml index a6e9cff5..61ba6dad 100644 --- a/specification/api/components/endpoints/patchLetter.yml +++ b/specification/api/components/endpoints/patchLetter.yml @@ -5,8 +5,8 @@ description: requestBody: $ref: "../requests/patchLetterRequest.yml" responses: - "200": - $ref: "../responses/patchLetter200.yml" + "202": + $ref: "../responses/patchLetter202.yml" "400": $ref: "../responses/errors/patchLetter/patchLetterBadRequest.yml" "404": @@ -15,5 +15,7 @@ responses: $ref: "../responses/errors/tooManyRequests.yml" "500": $ref: "../responses/errors/serverError.yml" + "502": + $ref: "../responses/errors/badGateway.yml" tags: - letter diff --git a/specification/api/components/endpoints/postLetters.yml b/specification/api/components/endpoints/postLetters.yml index 54c5faba..876c7fd1 100644 --- a/specification/api/components/endpoints/postLetters.yml +++ b/specification/api/components/endpoints/postLetters.yml @@ -9,9 +9,11 @@ requestBody: responses: '202': $ref: "../responses/postLetters202.yml" - '404': - $ref: "../responses/errors/resourceNotFound.yml" + '400': + $ref: "../responses/errors/badRequest.yml" '429': $ref: "../responses/errors/tooManyRequests.yml" '500': $ref: "../responses/errors/serverError.yml" + '502': + $ref: "../responses/errors/badGateway.yml" diff --git a/specification/api/components/environments/common/security-schemes-ptl.yml b/specification/api/components/environments/common/security-schemes-ptl.yml new file mode 100644 index 00000000..91763ecc --- /dev/null +++ b/specification/api/components/environments/common/security-schemes-ptl.yml @@ -0,0 +1,4 @@ +app-level3: + $ref: https://proxygen.ptl.api.platform.nhs.uk/components/securitySchemes/app-level3 +app-level0: + $ref: https://proxygen.ptl.api.platform.nhs.uk/components/securitySchemes/app-level0 diff --git a/specification/api/components/environments/int.env b/specification/api/components/environments/int.env new file mode 100644 index 00000000..cfea3938 --- /dev/null +++ b/specification/api/components/environments/int.env @@ -0,0 +1,3 @@ +MONITORING_FLAG=true +TEMPORARY_FLAG=false +ENV_DIR=../environments/int diff --git a/specification/api/components/x-nhsd-apim/access-int.yml b/specification/api/components/environments/int/access.yml similarity index 100% rename from specification/api/components/x-nhsd-apim/access-int.yml rename to specification/api/components/environments/int/access.yml diff --git a/specification/api/components/environments/int/authorization.yml b/specification/api/components/environments/int/authorization.yml new file mode 100644 index 00000000..c4e9225d --- /dev/null +++ b/specification/api/components/environments/int/authorization.yml @@ -0,0 +1,9 @@ +name: Authorization +in: header +description: |- + An OAuth 2.0 bearer token. Required in integration and production environments. +required: true +schema: + type: string + pattern: ^Bearer [[:ascii:]]+$ + example: Bearer g1112R_ccQ1Ebbb4gtHBP1aaaNM diff --git a/specification/api/components/x-nhsd-apim/rate-limiting.yml b/specification/api/components/environments/int/rate-limiting.yml similarity index 100% rename from specification/api/components/x-nhsd-apim/rate-limiting.yml rename to specification/api/components/environments/int/rate-limiting.yml diff --git a/specification/api/components/environments/int/security-schemes.yml b/specification/api/components/environments/int/security-schemes.yml new file mode 100644 index 00000000..a6f42da0 --- /dev/null +++ b/specification/api/components/environments/int/security-schemes.yml @@ -0,0 +1 @@ +$ref: '../common/security-schemes-ptl.yml' diff --git a/specification/api/components/security/security-int.yml b/specification/api/components/environments/int/security.yml similarity index 100% rename from specification/api/components/security/security-int.yml rename to specification/api/components/environments/int/security.yml diff --git a/specification/api/components/environments/int/target-attributes.yml b/specification/api/components/environments/int/target-attributes.yml new file mode 100644 index 00000000..d2abc241 --- /dev/null +++ b/specification/api/components/environments/int/target-attributes.yml @@ -0,0 +1,3 @@ +- name: NHSD-Supplier-ID + header: NHSD-Supplier-ID + required: true diff --git a/specification/api/components/environments/int/target.yml b/specification/api/components/environments/int/target.yml new file mode 100644 index 00000000..5f97aa58 --- /dev/null +++ b/specification/api/components/environments/int/target.yml @@ -0,0 +1,6 @@ +type: external +healthcheck: /_status +url: https://main.suppliers.nonprod.nhsnotify.national.nhs.uk +security: + type: mtls + secret: notify-supplier-mtls-int diff --git a/specification/api/components/environments/internal-dev-pr.env b/specification/api/components/environments/internal-dev-pr.env new file mode 100644 index 00000000..a9ca9bdd --- /dev/null +++ b/specification/api/components/environments/internal-dev-pr.env @@ -0,0 +1,3 @@ +MONITORING_FLAG=false +TEMPORARY_FLAG=true +ENV_DIR=../environments/internal-dev-pr diff --git a/specification/api/components/x-nhsd-apim/access-internal-dev-pr.yml b/specification/api/components/environments/internal-dev-pr/access.yml similarity index 100% rename from specification/api/components/x-nhsd-apim/access-internal-dev-pr.yml rename to specification/api/components/environments/internal-dev-pr/access.yml diff --git a/specification/api/components/environments/internal-dev-pr/authorization.yml b/specification/api/components/environments/internal-dev-pr/authorization.yml new file mode 100644 index 00000000..e62b2067 --- /dev/null +++ b/specification/api/components/environments/internal-dev-pr/authorization.yml @@ -0,0 +1,7 @@ +name: apikey +in: header +description: |- + API Key to authorize in dynamic environments only. +required: false +schema: + type: string diff --git a/specification/api/components/environments/internal-dev-pr/rate-limiting.yml b/specification/api/components/environments/internal-dev-pr/rate-limiting.yml new file mode 100644 index 00000000..71900a20 --- /dev/null +++ b/specification/api/components/environments/internal-dev-pr/rate-limiting.yml @@ -0,0 +1,3 @@ +app-default: + limit: 300 + timeunit: second diff --git a/specification/api/components/environments/internal-dev-pr/security-schemes.yml b/specification/api/components/environments/internal-dev-pr/security-schemes.yml new file mode 100644 index 00000000..a6f42da0 --- /dev/null +++ b/specification/api/components/environments/internal-dev-pr/security-schemes.yml @@ -0,0 +1 @@ +$ref: '../common/security-schemes-ptl.yml' diff --git a/specification/api/components/security/security-internal-dev-pr.yml b/specification/api/components/environments/internal-dev-pr/security.yml similarity index 100% rename from specification/api/components/security/security-internal-dev-pr.yml rename to specification/api/components/environments/internal-dev-pr/security.yml diff --git a/specification/api/components/environments/internal-dev-pr/target-attributes.yml b/specification/api/components/environments/internal-dev-pr/target-attributes.yml new file mode 100644 index 00000000..d2abc241 --- /dev/null +++ b/specification/api/components/environments/internal-dev-pr/target-attributes.yml @@ -0,0 +1,3 @@ +- name: NHSD-Supplier-ID + header: NHSD-Supplier-ID + required: true diff --git a/specification/api/components/x-nhsd-apim/target-internal-dev-pr.yml b/specification/api/components/environments/internal-dev-pr/target.yml similarity index 100% rename from specification/api/components/x-nhsd-apim/target-internal-dev-pr.yml rename to specification/api/components/environments/internal-dev-pr/target.yml diff --git a/specification/api/components/environments/internal-dev.env b/specification/api/components/environments/internal-dev.env new file mode 100644 index 00000000..2fed2ab0 --- /dev/null +++ b/specification/api/components/environments/internal-dev.env @@ -0,0 +1,3 @@ +MONITORING_FLAG=true +TEMPORARY_FLAG=false +ENV_DIR=../environments/internal-dev diff --git a/specification/api/components/x-nhsd-apim/access-internal-dev.yml b/specification/api/components/environments/internal-dev/access.yml similarity index 100% rename from specification/api/components/x-nhsd-apim/access-internal-dev.yml rename to specification/api/components/environments/internal-dev/access.yml diff --git a/specification/api/components/environments/internal-dev/authorization.yml b/specification/api/components/environments/internal-dev/authorization.yml new file mode 100644 index 00000000..c4e9225d --- /dev/null +++ b/specification/api/components/environments/internal-dev/authorization.yml @@ -0,0 +1,9 @@ +name: Authorization +in: header +description: |- + An OAuth 2.0 bearer token. Required in integration and production environments. +required: true +schema: + type: string + pattern: ^Bearer [[:ascii:]]+$ + example: Bearer g1112R_ccQ1Ebbb4gtHBP1aaaNM diff --git a/specification/api/components/environments/internal-dev/rate-limiting.yml b/specification/api/components/environments/internal-dev/rate-limiting.yml new file mode 100644 index 00000000..71900a20 --- /dev/null +++ b/specification/api/components/environments/internal-dev/rate-limiting.yml @@ -0,0 +1,3 @@ +app-default: + limit: 300 + timeunit: second diff --git a/specification/api/components/environments/internal-dev/security-schemes.yml b/specification/api/components/environments/internal-dev/security-schemes.yml new file mode 100644 index 00000000..a6f42da0 --- /dev/null +++ b/specification/api/components/environments/internal-dev/security-schemes.yml @@ -0,0 +1 @@ +$ref: '../common/security-schemes-ptl.yml' diff --git a/specification/api/components/environments/internal-dev/security.yml b/specification/api/components/environments/internal-dev/security.yml new file mode 100644 index 00000000..c26fe2d7 --- /dev/null +++ b/specification/api/components/environments/internal-dev/security.yml @@ -0,0 +1 @@ +app-level3: [] diff --git a/specification/api/components/environments/internal-dev/target-attributes.yml b/specification/api/components/environments/internal-dev/target-attributes.yml new file mode 100644 index 00000000..d2abc241 --- /dev/null +++ b/specification/api/components/environments/internal-dev/target-attributes.yml @@ -0,0 +1,3 @@ +- name: NHSD-Supplier-ID + header: NHSD-Supplier-ID + required: true diff --git a/specification/api/components/x-nhsd-apim/target-internal-dev.yml b/specification/api/components/environments/internal-dev/target.yml similarity index 100% rename from specification/api/components/x-nhsd-apim/target-internal-dev.yml rename to specification/api/components/environments/internal-dev/target.yml diff --git a/specification/api/components/environments/prod.env b/specification/api/components/environments/prod.env new file mode 100644 index 00000000..a42f92e7 --- /dev/null +++ b/specification/api/components/environments/prod.env @@ -0,0 +1,3 @@ +MONITORING_FLAG=true +TEMPORARY_FLAG=false +ENV_DIR=../environments/prod diff --git a/specification/api/components/x-nhsd-apim/access-prod.yml b/specification/api/components/environments/prod/access.yml similarity index 100% rename from specification/api/components/x-nhsd-apim/access-prod.yml rename to specification/api/components/environments/prod/access.yml diff --git a/specification/api/components/environments/prod/authorization.yml b/specification/api/components/environments/prod/authorization.yml new file mode 100644 index 00000000..c4e9225d --- /dev/null +++ b/specification/api/components/environments/prod/authorization.yml @@ -0,0 +1,9 @@ +name: Authorization +in: header +description: |- + An OAuth 2.0 bearer token. Required in integration and production environments. +required: true +schema: + type: string + pattern: ^Bearer [[:ascii:]]+$ + example: Bearer g1112R_ccQ1Ebbb4gtHBP1aaaNM diff --git a/specification/api/components/environments/prod/rate-limiting.yml b/specification/api/components/environments/prod/rate-limiting.yml new file mode 100644 index 00000000..71900a20 --- /dev/null +++ b/specification/api/components/environments/prod/rate-limiting.yml @@ -0,0 +1,3 @@ +app-default: + limit: 300 + timeunit: second diff --git a/specification/api/components/environments/prod/security-schemes.yml b/specification/api/components/environments/prod/security-schemes.yml new file mode 100644 index 00000000..53ade6bd --- /dev/null +++ b/specification/api/components/environments/prod/security-schemes.yml @@ -0,0 +1,4 @@ +app-level3: + $ref: https://proxygen.prod.api.platform.nhs.uk/components/securitySchemes/app-level3 +app-level0: + $ref: https://proxygen.prod.api.platform.nhs.uk/components/securitySchemes/app-level0 diff --git a/specification/api/components/environments/prod/security.yml b/specification/api/components/environments/prod/security.yml new file mode 100644 index 00000000..c26fe2d7 --- /dev/null +++ b/specification/api/components/environments/prod/security.yml @@ -0,0 +1 @@ +app-level3: [] diff --git a/specification/api/components/environments/prod/target-attributes.yml b/specification/api/components/environments/prod/target-attributes.yml new file mode 100644 index 00000000..d2abc241 --- /dev/null +++ b/specification/api/components/environments/prod/target-attributes.yml @@ -0,0 +1,3 @@ +- name: NHSD-Supplier-ID + header: NHSD-Supplier-ID + required: true diff --git a/specification/api/components/x-nhsd-apim/target-prod.yml b/specification/api/components/environments/prod/target.yml similarity index 100% rename from specification/api/components/x-nhsd-apim/target-prod.yml rename to specification/api/components/environments/prod/target.yml diff --git a/specification/api/components/environments/ref.env b/specification/api/components/environments/ref.env new file mode 100644 index 00000000..0c62c810 --- /dev/null +++ b/specification/api/components/environments/ref.env @@ -0,0 +1,3 @@ +MONITORING_FLAG=true +TEMPORARY_FLAG=false +ENV_DIR=../environments/ref diff --git a/specification/api/components/x-nhsd-apim/access-ref.yml b/specification/api/components/environments/ref/access.yml similarity index 100% rename from specification/api/components/x-nhsd-apim/access-ref.yml rename to specification/api/components/environments/ref/access.yml diff --git a/specification/api/components/environments/ref/authorization.yml b/specification/api/components/environments/ref/authorization.yml new file mode 100644 index 00000000..c4e9225d --- /dev/null +++ b/specification/api/components/environments/ref/authorization.yml @@ -0,0 +1,9 @@ +name: Authorization +in: header +description: |- + An OAuth 2.0 bearer token. Required in integration and production environments. +required: true +schema: + type: string + pattern: ^Bearer [[:ascii:]]+$ + example: Bearer g1112R_ccQ1Ebbb4gtHBP1aaaNM diff --git a/specification/api/components/environments/ref/rate-limiting.yml b/specification/api/components/environments/ref/rate-limiting.yml new file mode 100644 index 00000000..71900a20 --- /dev/null +++ b/specification/api/components/environments/ref/rate-limiting.yml @@ -0,0 +1,3 @@ +app-default: + limit: 300 + timeunit: second diff --git a/specification/api/components/environments/ref/security-schemes.yml b/specification/api/components/environments/ref/security-schemes.yml new file mode 100644 index 00000000..a6f42da0 --- /dev/null +++ b/specification/api/components/environments/ref/security-schemes.yml @@ -0,0 +1 @@ +$ref: '../common/security-schemes-ptl.yml' diff --git a/specification/api/components/environments/ref/security.yml b/specification/api/components/environments/ref/security.yml new file mode 100644 index 00000000..c26fe2d7 --- /dev/null +++ b/specification/api/components/environments/ref/security.yml @@ -0,0 +1 @@ +app-level3: [] diff --git a/specification/api/components/environments/ref/target-attributes.yml b/specification/api/components/environments/ref/target-attributes.yml new file mode 100644 index 00000000..d2abc241 --- /dev/null +++ b/specification/api/components/environments/ref/target-attributes.yml @@ -0,0 +1,3 @@ +- name: NHSD-Supplier-ID + header: NHSD-Supplier-ID + required: true diff --git a/specification/api/components/environments/ref/target.yml b/specification/api/components/environments/ref/target.yml new file mode 100644 index 00000000..b95fe25c --- /dev/null +++ b/specification/api/components/environments/ref/target.yml @@ -0,0 +1,6 @@ +type: external +healthcheck: /_status +url: https://main.suppliers.nonprod.nhsnotify.national.nhs.uk +security: + type: mtls + secret: notify-supplier-mtls-ref diff --git a/specification/api/components/environments/sandbox.env b/specification/api/components/environments/sandbox.env new file mode 100644 index 00000000..21db9402 --- /dev/null +++ b/specification/api/components/environments/sandbox.env @@ -0,0 +1,3 @@ +MONITORING_FLAG=false +TEMPORARY_FLAG=false +ENV_DIR=../environments/sandbox diff --git a/specification/api/components/x-nhsd-apim/access-sandbox.yml b/specification/api/components/environments/sandbox/access.yml similarity index 100% rename from specification/api/components/x-nhsd-apim/access-sandbox.yml rename to specification/api/components/environments/sandbox/access.yml diff --git a/specification/api/components/environments/sandbox/authorization.yml b/specification/api/components/environments/sandbox/authorization.yml new file mode 100644 index 00000000..de566bd1 --- /dev/null +++ b/specification/api/components/environments/sandbox/authorization.yml @@ -0,0 +1,9 @@ +name: Authorization +in: header +description: |- + An OAuth 2.0 bearer token. Required in integration and production environments. +required: false +schema: + type: string + pattern: ^Bearer [[:ascii:]]+$ + example: Bearer g1112R_ccQ1Ebbb4gtHBP1aaaNM diff --git a/specification/api/components/environments/sandbox/rate-limiting.yml b/specification/api/components/environments/sandbox/rate-limiting.yml new file mode 100644 index 00000000..71900a20 --- /dev/null +++ b/specification/api/components/environments/sandbox/rate-limiting.yml @@ -0,0 +1,3 @@ +app-default: + limit: 300 + timeunit: second diff --git a/specification/api/components/environments/sandbox/security-schemes.yml b/specification/api/components/environments/sandbox/security-schemes.yml new file mode 100644 index 00000000..a6f42da0 --- /dev/null +++ b/specification/api/components/environments/sandbox/security-schemes.yml @@ -0,0 +1 @@ +$ref: '../common/security-schemes-ptl.yml' diff --git a/specification/api/components/security/security-sandbox.yml b/specification/api/components/environments/sandbox/security.yml similarity index 100% rename from specification/api/components/security/security-sandbox.yml rename to specification/api/components/environments/sandbox/security.yml diff --git a/specification/api/components/x-nhsd-apim/target-attributes.yml b/specification/api/components/environments/sandbox/target-attributes.yml similarity index 100% rename from specification/api/components/x-nhsd-apim/target-attributes.yml rename to specification/api/components/environments/sandbox/target-attributes.yml diff --git a/specification/api/components/x-nhsd-apim/target-sandbox.yml b/specification/api/components/environments/sandbox/target.yml similarity index 100% rename from specification/api/components/x-nhsd-apim/target-sandbox.yml rename to specification/api/components/environments/sandbox/target.yml diff --git a/specification/api/components/parameters/authorization/authorization-template.yml b/specification/api/components/parameters/authorization/authorization-template.yml new file mode 100644 index 00000000..420a173d --- /dev/null +++ b/specification/api/components/parameters/authorization/authorization-template.yml @@ -0,0 +1 @@ +$ref: ../$ENV_DIR/authorization.yml diff --git a/specification/api/components/requests/patchLetterRequest.yml b/specification/api/components/requests/patchLetterRequest.yml index 2d2af997..8e485681 100644 --- a/specification/api/components/requests/patchLetterRequest.yml +++ b/specification/api/components/requests/patchLetterRequest.yml @@ -10,53 +10,40 @@ content: examples: patch-letter-request-default: summary: Request for default (PENDING) update - value: - $ref: ../examples/patchLetter/requests/patchLetter_DEFAULT.json + $ref: ../examples/patchLetter/requests/patchLetter_DEFAULT.json patch-letter-request-pending: summary: Request for PENDING update - value: - $ref: ../examples/patchLetter/requests/patchLetter_PENDING.json + $ref: ../examples/patchLetter/requests/patchLetter_PENDING.json patch-letter-request-accepted: summary: Request for ACCEPTED update - value: - $ref: ../examples/patchLetter/requests/patchLetter_ACCEPTED.json + $ref: ../examples/patchLetter/requests/patchLetter_ACCEPTED.json patch-letter-request-rejected: summary: Request for REJECTED update - value: - $ref: ../examples/patchLetter/requests/patchLetter_REJECTED.json + $ref: ../examples/patchLetter/requests/patchLetter_REJECTED.json patch-letter-request-printed: summary: Request for PRINTED update - value: - $ref: ../examples/patchLetter/requests/patchLetter_PRINTED.json + $ref: ../examples/patchLetter/requests/patchLetter_PRINTED.json patch-letter-request-enclosed: summary: Request for ENCLOSED update - value: - $ref: ../examples/patchLetter/requests/patchLetter_ENCLOSED.json + $ref: ../examples/patchLetter/requests/patchLetter_ENCLOSED.json patch-letter-request-cancelled: summary: Request for CANCELLED update - value: - $ref: ../examples/patchLetter/requests/patchLetter_CANCELLED.json + $ref: ../examples/patchLetter/requests/patchLetter_CANCELLED.json patch-letter-request-dispatched: summary: Request for DISPATCHED update - value: - $ref: ../examples/patchLetter/requests/patchLetter_DISPATCHED.json + $ref: ../examples/patchLetter/requests/patchLetter_DISPATCHED.json patch-letter-request-delivered: summary: Request for DELIVERED update - value: - $ref: ../examples/patchLetter/requests/patchLetter_DELIVERED.json + $ref: ../examples/patchLetter/requests/patchLetter_DELIVERED.json patch-letter-request-failed: summary: Request for FAILED update - value: - $ref: ../examples/patchLetter/requests/patchLetter_FAILED.json + $ref: ../examples/patchLetter/requests/patchLetter_FAILED.json patch-letter-request-returned: summary: Request for RETURNED update - value: - $ref: ../examples/patchLetter/requests/patchLetter_RETURNED.json + $ref: ../examples/patchLetter/requests/patchLetter_RETURNED.json patch-letter-request-invalid: summary: Request for an invalid update - value: - $ref: ../examples/patchLetter/requests/patchLetter_INVALID.json + $ref: ../examples/patchLetter/requests/patchLetter_INVALID.json patch-letter-request-not-found: summary: Request for a non existant letter - value: - $ref: ../examples/patchLetter/requests/patchLetter_NOTFOUND.json + $ref: ../examples/patchLetter/requests/patchLetter_NOTFOUND.json diff --git a/specification/api/components/requests/postLettersRequest.yml b/specification/api/components/requests/postLettersRequest.yml index c36ec5aa..e4370337 100644 --- a/specification/api/components/requests/postLettersRequest.yml +++ b/specification/api/components/requests/postLettersRequest.yml @@ -11,5 +11,4 @@ content: $ref: ../schemas/letterUpdateItem.yml examples: post-letter-request: - value: - $ref: ../examples/postLetter/requests/postLetters.json + $ref: ../examples/postLetter/requests/postLetters.json diff --git a/specification/api/components/requests/postMIRequest.yml b/specification/api/components/requests/postMIRequest.yml index 8d1bce83..6bcfc62e 100644 --- a/specification/api/components/requests/postMIRequest.yml +++ b/specification/api/components/requests/postMIRequest.yml @@ -5,13 +5,10 @@ content: examples: create-mi-request-success: summary: Request for successful MI record Creation - value: - $ref: "../examples/createMI/requests/createMI_SUCCESS.json" + $ref: "../examples/createMI/requests/createMI_request_SUCCESS.json" create-mi-request-invalid: summary: Invalid Request for MI record Creation - value: - $ref: "../examples/createMI/requests/createMI_INVALID.json" + $ref: "../examples/createMI/requests/createMI_request_INVALID.json" create-mi-request-notfound: summary: Request for MI record Creation for unknown spec - value: - $ref: "../examples/createMI/requests/createMI_NOTFOUND.json" + $ref: "../examples/createMI/requests/createMI_request_NOTFOUND.json" diff --git a/specification/api/components/responses/errors/badGateway.yml b/specification/api/components/responses/errors/badGateway.yml new file mode 100644 index 00000000..3d1cce78 --- /dev/null +++ b/specification/api/components/responses/errors/badGateway.yml @@ -0,0 +1,8 @@ +description: "Bad gateway" +content: + application/vnd.api+json: + schema: + $ref: "../../schemas/apiGatewayError.yml" + examples: + error-bad-request: + $ref: ../../examples/errors/responses/bad-gateway.json diff --git a/specification/api/components/responses/errors/badRequest.yml b/specification/api/components/responses/errors/badRequest.yml index 0973621e..2b656000 100644 --- a/specification/api/components/responses/errors/badRequest.yml +++ b/specification/api/components/responses/errors/badRequest.yml @@ -5,5 +5,4 @@ content: $ref: "../../schemas/errorResponse.yml" examples: error-bad-request: - value: - $ref: ../../examples/errors/responses/badRequest.json + $ref: ../../examples/errors/responses/badRequest.json diff --git a/specification/api/components/responses/errors/listLetters/listLettersBadRequest.yml b/specification/api/components/responses/errors/listLetters/listLettersBadRequest.yml index e81292ce..496a7d58 100644 --- a/specification/api/components/responses/errors/listLetters/listLettersBadRequest.yml +++ b/specification/api/components/responses/errors/listLetters/listLettersBadRequest.yml @@ -5,8 +5,6 @@ content: $ref: "../../../schemas/errorResponse.yml" examples: error-bad-request-unknown-parameter: - value: - $ref: ../../../examples/errors/responses/getLetter/unknownParameter.json + $ref: ../../../examples/errors/responses/getLetter/unknownParameter.json error-bad-request-invalid-limit: - value: - $ref: ../../../examples/errors/responses/getLetter/limitInvalidValue.json + $ref: ../../../examples/errors/responses/getLetter/limitInvalidValue.json diff --git a/specification/api/components/responses/errors/patchLetter/patchLetterBadRequest.yml b/specification/api/components/responses/errors/patchLetter/patchLetterBadRequest.yml index 09afe4bc..11777eb9 100644 --- a/specification/api/components/responses/errors/patchLetter/patchLetterBadRequest.yml +++ b/specification/api/components/responses/errors/patchLetter/patchLetterBadRequest.yml @@ -5,5 +5,4 @@ content: $ref: "../../../schemas/errorResponse.yml" examples: error-bad-request-invalid-request-body: - value: - $ref: ../../../examples/errors/responses/patchLetter/invalidBody.json + $ref: ../../../examples/errors/responses/patchLetter/invalidBody.json diff --git a/specification/api/components/responses/errors/resourceNotFound.yml b/specification/api/components/responses/errors/resourceNotFound.yml index d6ad9a22..53217fe7 100644 --- a/specification/api/components/responses/errors/resourceNotFound.yml +++ b/specification/api/components/responses/errors/resourceNotFound.yml @@ -5,5 +5,4 @@ content: $ref: "../../schemas/errorResponse.yml" examples: error-not-found: - value: - $ref: ../../examples/errors/responses/resourceNotFound.json + $ref: ../../examples/errors/responses/resourceNotFound.json diff --git a/specification/api/components/responses/errors/serverError.yml b/specification/api/components/responses/errors/serverError.yml index 8d81108d..880ad833 100644 --- a/specification/api/components/responses/errors/serverError.yml +++ b/specification/api/components/responses/errors/serverError.yml @@ -5,5 +5,4 @@ content: $ref: "../../schemas/errorResponse.yml" examples: error-server-error: - value: - $ref: ../../examples/errors/responses/serverError.json + $ref: ../../examples/errors/responses/serverError.json diff --git a/specification/api/components/responses/errors/tooManyRequests.yml b/specification/api/components/responses/errors/tooManyRequests.yml index 072930ca..86aec81b 100644 --- a/specification/api/components/responses/errors/tooManyRequests.yml +++ b/specification/api/components/responses/errors/tooManyRequests.yml @@ -1,9 +1,33 @@ description: Too many requests content: - application/vnd.api+json: + application/json: schema: - $ref: "../../schemas/errorResponse.yml" + type: object + properties: + message: + type: string + example: "Your application has exceeded its quota" + scope: + type: string + example: "application" + policy: + type: string + example: "quota" + limit: + type: integer + example: 300 + interval: + type: integer + example: 1 + timeunit: + type: string + example: "minute" + ratelimiting_expiry_time_ms: + type: integer + example: 1625498765123 + message_id: + type: string + example: "rrt-4773181658036170775-c-geu2-321623-73628915-2" examples: error-too-many-requests: - value: - $ref: ../../examples/errors/responses/tooManyRequests.json + $ref: ../../examples/errors/responses/tooManyRequests.json diff --git a/specification/api/components/responses/getLetter200.yml b/specification/api/components/responses/getLetter200.yml index d4896d37..b13efcf6 100644 --- a/specification/api/components/responses/getLetter200.yml +++ b/specification/api/components/responses/getLetter200.yml @@ -10,5 +10,4 @@ content: $ref: "../schemas/letterStatusData.yml" examples: get-letters: - value: - $ref: ../examples/getLetter/responses/getLetter-24L5eYSWGzCHlGmzNxuqVusPxDg.json + $ref: ../examples/getLetter/responses/getLetter-24L5eYSWGzCHlGmzNxuqVusPxDg.json diff --git a/specification/api/components/responses/getLetters200.yml b/specification/api/components/responses/getLetters200.yml index 99349644..48e53124 100644 --- a/specification/api/components/responses/getLetters200.yml +++ b/specification/api/components/responses/getLetters200.yml @@ -10,5 +10,4 @@ content: $ref: "../schemas/lettersListResponse.yml" examples: get-letters: - value: - $ref: ../examples/getLetters/responses/getLetters_pending.json + $ref: ../examples/getLetters/responses/getLetters_pending.json diff --git a/specification/api/components/responses/patchLetter200.yml b/specification/api/components/responses/patchLetter200.yml deleted file mode 100644 index 2f7d79c6..00000000 --- a/specification/api/components/responses/patchLetter200.yml +++ /dev/null @@ -1,14 +0,0 @@ -description: Letter resource updated successfully -headers: - X-Request-ID: - $ref: "../responseHeaders/xRequestId.yml" - X-Correlation-ID: - $ref: "../responseHeaders/xCorrelationId.yml" -content: - application/vnd.api+json: - schema: - $ref: "../schemas/letterStatusData.yml" - examples: - patch_letter-response: - value: - $ref: ../examples/patchLetter/responses/patchLetter_ACCEPTED.json diff --git a/specification/api/components/responses/patchLetter202.yml b/specification/api/components/responses/patchLetter202.yml new file mode 100644 index 00000000..37447b5a --- /dev/null +++ b/specification/api/components/responses/patchLetter202.yml @@ -0,0 +1,6 @@ +description: 202 (Accepted) Acknowledges Letter resource update has been received +headers: + X-Request-ID: + $ref: "../responseHeaders/xRequestId.yml" + X-Correlation-ID: + $ref: "../responseHeaders/xCorrelationId.yml" diff --git a/specification/api/components/responses/postMI201.yml b/specification/api/components/responses/postMI201.yml index e9dacc8e..d3ffd71f 100644 --- a/specification/api/components/responses/postMI201.yml +++ b/specification/api/components/responses/postMI201.yml @@ -10,5 +10,4 @@ content: $ref: "../schemas/miResponse.yml" examples: create-mi-response: - value: - $ref: "../examples/createMI/responses/createMI_SUCCESS.json" + $ref: "../examples/createMI/responses/createMI_response_SUCCESS.json" diff --git a/specification/api/components/schemas/apiGatewayError.yml b/specification/api/components/schemas/apiGatewayError.yml new file mode 100644 index 00000000..a36048f6 --- /dev/null +++ b/specification/api/components/schemas/apiGatewayError.yml @@ -0,0 +1,9 @@ +description: API gateway error response +content: + application/json: + schema: + type: object + properties: + message: + type: string + example: "Bad Gateway" diff --git a/specification/api/components/schemas/letterStatus.yml b/specification/api/components/schemas/letterStatus.yml index ceeb6f7d..a95551d7 100644 --- a/specification/api/components/schemas/letterStatus.yml +++ b/specification/api/components/schemas/letterStatus.yml @@ -10,7 +10,6 @@ enum: - DELIVERED - FAILED - RETURNED - - DESTROYED - FORWARDED description: The supplier status of an individual letter example: PENDING diff --git a/specification/api/components/security-schemes/security-schemes-template.yml b/specification/api/components/security-schemes/security-schemes-template.yml new file mode 100644 index 00000000..8a25f7d8 --- /dev/null +++ b/specification/api/components/security-schemes/security-schemes-template.yml @@ -0,0 +1 @@ +$ref: $ENV_DIR/security-schemes.yml diff --git a/specification/api/components/security/security-internal-dev.yml b/specification/api/components/security/security-internal-dev.yml deleted file mode 100644 index 878d7f27..00000000 --- a/specification/api/components/security/security-internal-dev.yml +++ /dev/null @@ -1 +0,0 @@ -app-level3: [ ] diff --git a/specification/api/components/security/security-prod.yml b/specification/api/components/security/security-prod.yml deleted file mode 100644 index 878d7f27..00000000 --- a/specification/api/components/security/security-prod.yml +++ /dev/null @@ -1 +0,0 @@ -app-level3: [ ] diff --git a/specification/api/components/security/security-ref.yml b/specification/api/components/security/security-ref.yml deleted file mode 100644 index 878d7f27..00000000 --- a/specification/api/components/security/security-ref.yml +++ /dev/null @@ -1 +0,0 @@ -app-level3: [ ] diff --git a/specification/api/components/security/security-template.yml b/specification/api/components/security/security-template.yml index ffd1d328..58aea08e 100644 --- a/specification/api/components/security/security-template.yml +++ b/specification/api/components/security/security-template.yml @@ -1 +1 @@ -$ref: $SECURITY +$ref: $ENV_DIR/security.yml diff --git a/specification/api/components/security/security.yml b/specification/api/components/security/security.yml deleted file mode 100644 index 393524ce..00000000 --- a/specification/api/components/security/security.yml +++ /dev/null @@ -1 +0,0 @@ -$ref: security-prod.yml diff --git a/specification/api/components/x-nhsd-apim/access-template.yml b/specification/api/components/x-nhsd-apim/access-template.yml deleted file mode 100644 index cee5c6f0..00000000 --- a/specification/api/components/x-nhsd-apim/access-template.yml +++ /dev/null @@ -1 +0,0 @@ -$ref: $ACCESS diff --git a/specification/api/components/x-nhsd-apim/access.yml b/specification/api/components/x-nhsd-apim/access.yml deleted file mode 100644 index c69c222d..00000000 --- a/specification/api/components/x-nhsd-apim/access.yml +++ /dev/null @@ -1 +0,0 @@ -$ref: access-prod.yml diff --git a/specification/api/components/x-nhsd-apim/target-int.yml b/specification/api/components/x-nhsd-apim/target-int.yml deleted file mode 100644 index 040d1228..00000000 --- a/specification/api/components/x-nhsd-apim/target-int.yml +++ /dev/null @@ -1,6 +0,0 @@ -type: external -healthcheck: /_status -url: https://suppliers.int.nhsnotify.national.nhs.uk -security: - type: mtls - secret: nhs-notify-supplier-mtls-int diff --git a/specification/api/components/x-nhsd-apim/target-ref.yml b/specification/api/components/x-nhsd-apim/target-ref.yml deleted file mode 100644 index 8ace3f78..00000000 --- a/specification/api/components/x-nhsd-apim/target-ref.yml +++ /dev/null @@ -1,6 +0,0 @@ -type: external -healthcheck: /_status -url: https://suppliers.ref.nhsnotify.national.nhs.uk -security: - type: mtls - secret: nhs-notify-supplier-mtls-ref diff --git a/specification/api/components/x-nhsd-apim/target-template.yml b/specification/api/components/x-nhsd-apim/target-template.yml deleted file mode 100644 index b3eebe5c..00000000 --- a/specification/api/components/x-nhsd-apim/target-template.yml +++ /dev/null @@ -1 +0,0 @@ -$ref: $TARGET diff --git a/specification/api/components/x-nhsd-apim/target.yml b/specification/api/components/x-nhsd-apim/target.yml deleted file mode 100644 index a0f578ca..00000000 --- a/specification/api/components/x-nhsd-apim/target.yml +++ /dev/null @@ -1 +0,0 @@ -$ref: target-prod.yml diff --git a/specification/api/components/x-nhsd-apim/x-nhsd-apim-template.yml b/specification/api/components/x-nhsd-apim/x-nhsd-apim-template.yml new file mode 100644 index 00000000..8edb1b04 --- /dev/null +++ b/specification/api/components/x-nhsd-apim/x-nhsd-apim-template.yml @@ -0,0 +1,10 @@ +temporary: $TEMPORARY_FLAG +monitoring: $MONITORING_FLAG +access: + $ref: $ENV_DIR/access.yml +target: + $ref: $ENV_DIR/target.yml +target-attributes: + $ref: $ENV_DIR/target-attributes.yml +ratelimiting: + $ref: $ENV_DIR/rate-limiting.yml diff --git a/specification/api/notify-supplier-phase1.yml b/specification/api/notify-supplier-phase1.yml index bccf356e..2e26e8b7 100644 --- a/specification/api/notify-supplier-phase1.yml +++ b/specification/api/notify-supplier-phase1.yml @@ -9,6 +9,7 @@ security: paths: /letters: parameters: + - $ref: 'components/parameters/authorization/authorization.yml' - $ref: 'components/parameters/requestId.yml' - $ref: 'components/parameters/correlationId.yml' post: @@ -17,6 +18,7 @@ paths: $ref: 'components/endpoints/listLetters.yml' '/letters/{id}': parameters: + - $ref: 'components/parameters/authorization/authorization.yml' - $ref: 'components/parameters/requestId.yml' - $ref: 'components/parameters/correlationId.yml' - $ref: 'components/parameters/resourceId.yml' @@ -26,15 +28,17 @@ paths: $ref: 'components/endpoints/patchLetter.yml' '/letters/{id}/data': parameters: + - $ref: 'components/parameters/authorization/authorization.yml' - $ref: 'components/parameters/resourceId.yml' - $ref: 'components/parameters/requestId.yml' - $ref: 'components/parameters/correlationId.yml' get: $ref: 'components/endpoints/getDataId.yml' - head: - $ref: 'components/endpoints/headDataId.yml' + #head: + # $ref: 'components/endpoints/headDataId.yml' /mi: parameters: + - $ref: 'components/parameters/authorization/authorization.yml' - $ref: 'components/parameters/requestId.yml' - $ref: 'components/parameters/correlationId.yml' post: @@ -46,13 +50,10 @@ paths: responses: "200": description: Service is healthy - summary: Health check endpoint + summary: Health check components: securitySchemes: - app-level3: - $ref: https://proxygen.ptl.api.platform.nhs.uk/components/securitySchemes/app-level3 - app-level0: - $ref: https://proxygen.ptl.api.platform.nhs.uk/components/securitySchemes/app-level0 + $ref: ./components/security-schemes/security-schemes.yml tags: - name: letter @@ -67,13 +68,4 @@ servers: - url: 'http://127.0.0.1:9000' description: Local development server x-nhsd-apim: - temporary: false - monitoring: false - access: - $ref: ./components/x-nhsd-apim/access.yml - target: - $ref: ./components/x-nhsd-apim/target.yml - target-attributes: - $ref: ./components/x-nhsd-apim/target-attributes.yml - ratelimiting: - $ref: ./components/x-nhsd-apim/rate-limiting.yml + $ref: './components/x-nhsd-apim/x-nhsd-apim.yml' diff --git a/tests/component-tests/apiGateway-tests/create-mi.spec.ts b/tests/component-tests/apiGateway-tests/create-mi.spec.ts new file mode 100644 index 00000000..1c72d13d --- /dev/null +++ b/tests/component-tests/apiGateway-tests/create-mi.spec.ts @@ -0,0 +1,132 @@ +import { expect, test } from "@playwright/test"; +import getRestApiGatewayBaseUrl from "../../helpers/aws-gateway-helper"; +import { MI_ENDPOINT } from "../../constants/api-constants"; +import { + createHeaderWithNoCorrelationId, + createHeaderWithNoRequestId, + createInvalidRequestHeaders, + createValidRequestHeaders, +} from "../../constants/request-headers"; +import { + miInvalidDateRequest, + miInvalidRequest, + miValidRequest, +} from "./testCases/create-mi"; +import { + error400InvalidDate, + error400ResponseBody, + error403ResponseBody, + requestId500Error, +} from "../../helpers/common-types"; + +let baseUrl: string; + +test.beforeAll(async () => { + baseUrl = await getRestApiGatewayBaseUrl(); +}); + +test.describe("API Gateway Tests to Verify Mi Endpoint", () => { + test(`Post /mi returns 200 when a valid request is passed`, async ({ + request, + }) => { + const headers = createValidRequestHeaders(); + const body = miValidRequest(); + + const response = await request.post(`${baseUrl}/${MI_ENDPOINT}`, { + headers, + data: body, + }); + + const responseBody = await response.json(); + expect(response.status()).toBe(201); + expect(responseBody.data.attributes).toMatchObject({ + groupId: "group123", + lineItem: "envelope-business-standard", + quantity: 10, + specificationId: "Test-Spec-Id", + stockRemaining: 100, + timestamp: body.data.attributes.timestamp, + }); + expect(responseBody.data.type).toBe("ManagementInformation"); + }); + + test(`Post /mi returns 400 when a invalid request is passed`, async ({ + request, + }) => { + const headers = createValidRequestHeaders(); + const body = miInvalidRequest(); + + const response = await request.post(`${baseUrl}/${MI_ENDPOINT}`, { + headers, + data: body, + }); + + const responseBody = await response.json(); + expect(response.status()).toBe(400); + expect(responseBody).toMatchObject(error400ResponseBody()); + }); + + test(`Post /mi returns 403 when a authentication header is not passed`, async ({ + request, + }) => { + const headers = createInvalidRequestHeaders(); + const body = miValidRequest(); + + const response = await request.post(`${baseUrl}/${MI_ENDPOINT}`, { + headers, + data: body, + }); + + const responseBody = await response.json(); + expect(response.status()).toBe(403); + expect(responseBody).toMatchObject(error403ResponseBody()); + }); + + test(`Post /mi returns 500 when a correlationId is not passed`, async ({ + request, + }) => { + const headers = createHeaderWithNoCorrelationId(); + const body = miValidRequest(); + + const response = await request.post(`${baseUrl}/${MI_ENDPOINT}`, { + headers, + data: body, + }); + + const res = await response.json(); + expect(response.status()).toBe(500); + expect(res.errors[0].detail).toBe("Unexpected error"); + }); + + test(`Post /mi returns 500 when a x-request-id is not passed`, async ({ + request, + }) => { + const headers = createHeaderWithNoRequestId(); + const body = miValidRequest(); + + const response = await request.post(`${baseUrl}/${MI_ENDPOINT}`, { + headers, + data: body, + }); + + const responseBody = await response.json(); + expect(response.status()).toBe(500); + expect(responseBody).toMatchObject(requestId500Error()); + }); + + test(`Post /mi returns 400 when a invalid Date is passed`, async ({ + request, + }) => { + const headers = createValidRequestHeaders(); + const body = miInvalidDateRequest(); + + const response = await request.post(`${baseUrl}/${MI_ENDPOINT}`, { + headers, + data: body, + }); + + const responseBody = await response.json(); + expect(response.status()).toBe(400); + expect(responseBody).toMatchObject(error400InvalidDate()); + }); +}); diff --git a/tests/component-tests/apiGateway-tests/createMi.spec.ts b/tests/component-tests/apiGateway-tests/createMi.spec.ts deleted file mode 100644 index 0455c06d..00000000 --- a/tests/component-tests/apiGateway-tests/createMi.spec.ts +++ /dev/null @@ -1,110 +0,0 @@ -import {test, expect} from '@playwright/test'; -import { getRestApiGatewayBaseUrl } from "../../helpers/awsGatewayHelper"; -import { MI_ENDPOINT } from '../../constants/api_constants'; -import { createHeaderWithNoCorrelationId, createHeaderWithNoRequestId, createInvalidRequestHeaders, createValidRequestHeaders } from '../../constants/request_headers'; -import { miInvalidDateRequest, miInvalidRequest, miValidRequest } from './testCases/createMi'; -import { time } from 'console'; -import { error400InvalidDate, error400ResponseBody, error403ResponseBody, error404ResponseBody, requestId500Error } from '../../helpers/commonTypes'; - -let baseUrl: string; - -test.beforeAll(async () => { - baseUrl = await getRestApiGatewayBaseUrl(); -}); - -test.describe('API Gateway Tests to Verify Mi Endpoint', () => { - test(`Post /mi returns 200 when a valid request is passed`, async ({ request }) => { - - const headers = createValidRequestHeaders(); - const body = miValidRequest(); - - const response = await request.post(`${baseUrl}/${MI_ENDPOINT}`, { - headers: headers, - data: body - }); - - const responseBody = await response.json(); - expect(response.status()).toBe(201); - expect(responseBody.data.attributes).toMatchObject({ - groupId: 'group123', - lineItem: 'envelope-business-standard', - quantity: 10, - specificationId: 'Test-Spec-Id', - stockRemaining: 100, - timestamp: body.data.attributes.timestamp, - }); - expect(responseBody.data.type).toBe('ManagementInformation'); - }); - - test(`Post /mi returns 400 when a invalid request is passed`, async ({ request }) => { - const headers = createValidRequestHeaders(); - const body = miInvalidRequest(); - - const response = await request.post(`${baseUrl}/${MI_ENDPOINT}`, { - headers: headers, - data: body - }); - - const responseBody = await response.json(); - expect(response.status()).toBe(400); - expect(responseBody).toMatchObject(error400ResponseBody()); - }); - - test(`Post /mi returns 403 when a authentication header is not passed`, async ({ request }) => { - const headers = createInvalidRequestHeaders(); - const body = miValidRequest(); - - const response = await request.post(`${baseUrl}/${MI_ENDPOINT}`, { - headers: headers, - data: body - }); - - const responseBody = await response.json(); - expect(response.status()).toBe(403); - expect(responseBody).toMatchObject(error403ResponseBody()); - }); - - test(`Post /mi returns 500 when a correlationId is not passed`, async ({ request }) => { - const headers = createHeaderWithNoCorrelationId(); - const body = miValidRequest(); - - const response = await request.post(`${baseUrl}/${MI_ENDPOINT}`, { - headers: headers, - data: body - }); - - const res = await response.json(); - expect(response.status()).toBe(500); - expect(res.errors[0].detail).toBe("The request headers don't contain the APIM correlation id"); - }); - - test(`Post /mi returns 500 when a x-request-id is not passed`, async ({ request }) => { - const headers = createHeaderWithNoRequestId(); - const body = miValidRequest(); - - const response = await request.post(`${baseUrl}/${MI_ENDPOINT}`, { - headers: headers, - data: body - }); - - const responseBody = await response.json(); - expect(response.status()).toBe(500); - expect(responseBody).toMatchObject(requestId500Error()); - }); - - test(`Post /mi returns 400 when a invalid Date is passed`, async ({ request }) => { - const headers = createValidRequestHeaders(); - const body = miInvalidDateRequest(); - - const response = await request.post(`${baseUrl}/${MI_ENDPOINT}`, { - headers: headers, - data: body - }); - - const responseBody = await response.json(); - expect(response.status()).toBe(400); - expect(responseBody).toMatchObject(error400InvalidDate()); - }); - - -}); diff --git a/tests/component-tests/apiGateway-tests/get-letter-status.spec.ts b/tests/component-tests/apiGateway-tests/get-letter-status.spec.ts new file mode 100644 index 00000000..aa5d475c --- /dev/null +++ b/tests/component-tests/apiGateway-tests/get-letter-status.spec.ts @@ -0,0 +1,79 @@ +import { expect, test } from "@playwright/test"; +import getRestApiGatewayBaseUrl from "../../helpers/aws-gateway-helper"; +import { getLettersBySupplier } from "../../helpers/generate-fetch-test-data"; +import { SUPPLIERID, SUPPLIER_LETTERS } from "../../constants/api-constants"; +import { createValidRequestHeaders } from "../../constants/request-headers"; +import { + error404ResponseBody, + error500ResponseBody, +} from "../../helpers/common-types"; + +let baseUrl: string; + +test.beforeAll(async () => { + baseUrl = await getRestApiGatewayBaseUrl(); +}); + +test.describe("API Gateway Tests to Verify Get Letter Status Endpoint", () => { + test(`Get /letters/{id} returns 200 and valid response for a given id`, async ({ + request, + }) => { + const letters = await getLettersBySupplier(SUPPLIERID, "PENDING", 1); + + if (!letters?.length) { + test.fail(true, `No PENDING letters found for supplier ${SUPPLIERID}`); + return; + } + const letter = letters[0]; + const headers = createValidRequestHeaders(); + const response = await request.get( + `${baseUrl}/${SUPPLIER_LETTERS}/${letter.id}`, + { + headers, + }, + ); + + const responseBody = await response.json(); + + expect(response.status()).toBe(200); + expect(responseBody).toMatchObject({ + data: { + attributes: { + status: "PENDING", + specificationId: letter.specificationId, + groupId: letter.groupId, + }, + id: letter.id, + type: "Letter", + }, + }); + }); + + test(`Get /letters/{id} returns 404 if no resource is found for id`, async ({ + request, + }) => { + const id = "11"; + const headers = createValidRequestHeaders(); + const response = await request.get(`${baseUrl}/${SUPPLIER_LETTERS}/${id}`, { + headers, + }); + + const responseBody = await response.json(); + expect(response.status()).toBe(404); + expect(responseBody).toMatchObject(error404ResponseBody()); + }); + + test(`Get /letters/{id} returns 500 if letter is not found for supplierId ${SUPPLIERID}`, async ({ + request, + }) => { + const id = "non-existing-id-12345"; + const headers = createValidRequestHeaders(); + const response = await request.get(`${baseUrl}/${SUPPLIER_LETTERS}/${id}`, { + headers, + }); + + const responseBody = await response.json(); + expect(response.status()).toBe(500); + expect(responseBody).toMatchObject(error500ResponseBody()); + }); +}); diff --git a/tests/component-tests/apiGateway-tests/get-letters.spec.ts b/tests/component-tests/apiGateway-tests/get-letters.spec.ts new file mode 100644 index 00000000..d01a97d4 --- /dev/null +++ b/tests/component-tests/apiGateway-tests/get-letters.spec.ts @@ -0,0 +1,93 @@ +import { expect, test } from "@playwright/test"; +import { SUPPLIER_LETTERS } from "../../constants/api-constants"; +import { + createHeaderWithNoCorrelationId, + createInvalidRequestHeaders, + createValidRequestHeaders, +} from "../../constants/request-headers"; +import getRestApiGatewayBaseUrl from "../../helpers/aws-gateway-helper"; + +let baseUrl: string; + +test.beforeAll(async () => { + baseUrl = await getRestApiGatewayBaseUrl(); +}); + +test.describe("API Gateway Tests To Get List Of Pending Letters", () => { + test("GET /letters should return 200 and list items", async ({ request }) => { + const header = createValidRequestHeaders(); + const response = await request.get(`${baseUrl}/${SUPPLIER_LETTERS}`, { + headers: header, + params: { + limit: "2", + }, + }); + + expect(response.status()).toBe(200); + const responseBody = await response.json(); + expect(responseBody.data.length).toBeGreaterThanOrEqual(1); + }); + + test("GET /letters with invalid authentication should return 403", async ({ + request, + }) => { + const header = createInvalidRequestHeaders(); + const response = await request.get(`${baseUrl}/${SUPPLIER_LETTERS}`, { + headers: header, + params: { + limit: "2", + }, + }); + expect(response.status()).toBe(403); + const responseBody = await response.json(); + expect(responseBody).toMatchObject({ + Message: + "User is not authorized to access this resource with an explicit deny in an identity-based policy", + }); + }); + + test("GET /letters with empty correlationId should return 500", async ({ + request, + }) => { + const header = createHeaderWithNoCorrelationId(); + const response = await request.get(`${baseUrl}/${SUPPLIER_LETTERS}`, { + headers: header, + params: { + limit: "2", + }, + }); + expect(response.status()).toBe(500); + const responseBody = await response.json(); + expect(responseBody.errors[0].code).toBe("NOTIFY_INTERNAL_SERVER_ERROR"); + expect(responseBody.errors[0].detail).toBe("Unexpected error"); + }); + + test("GET /letters with invalid query param return 400", async ({ + request, + }) => { + const header = createValidRequestHeaders(); + const response = await request.get(`${baseUrl}/${SUPPLIER_LETTERS}`, { + headers: header, + params: { + limit: "?", + }, + }); + expect(response.status()).toBe(400); + const responseBody = await response.json(); + expect(responseBody).toMatchObject({ + errors: [ + { + id: "12345", + code: "NOTIFY_INVALID_REQUEST", + links: { + about: + "https://digital.nhs.uk/developer/api-catalogue/nhs-notify-supplier", + }, + status: "400", + title: "Invalid request", + detail: "The limit parameter is not a number", + }, + ], + }); + }); +}); diff --git a/tests/component-tests/apiGateway-tests/getLetterStatus.spec.ts b/tests/component-tests/apiGateway-tests/getLetterStatus.spec.ts deleted file mode 100644 index 7382cb79..00000000 --- a/tests/component-tests/apiGateway-tests/getLetterStatus.spec.ts +++ /dev/null @@ -1,71 +0,0 @@ -import { test, expect } from '@playwright/test'; -import { getRestApiGatewayBaseUrl } from '../../helpers/awsGatewayHelper'; -import { getLettersBySupplier } from '../../helpers/generate_fetch_testData'; -import { SUPPLIER_LETTERS, SUPPLIERID } from '../../constants/api_constants'; -import { createValidRequestHeaders } from '../../constants/request_headers'; -import { error404ResponseBody, error500ResponseBody } from '../../helpers/commonTypes'; - -let baseUrl: string; - -test.beforeAll(async () => { - baseUrl = await getRestApiGatewayBaseUrl(); -}); - -test.describe('API Gateway Tests to Verify Get Letter Status Endpoint', () => { - test(`Get /letters/{id} returns 200 and valid response for a given id`, async ({ request }) => { - - const letters = await getLettersBySupplier(SUPPLIERID, 'PENDING', 1); - - if (!letters?.length) { - test.fail(true, `No PENDING letters found for supplier ${SUPPLIERID}`); - return; - } - const letter = letters[0]; - const headers = createValidRequestHeaders(); - const response = await request.get(`${baseUrl}/${SUPPLIER_LETTERS}/${letter.id}`, { - headers: headers, - }); - - const responseBody = await response.json(); - - expect(response.status()).toBe(200); - expect(responseBody).toMatchObject({ - data:{ - attributes: { - status: 'PENDING', - specificationId: letter.specificationId, - groupId: letter.groupId, - }, - id: letter.id, - type: 'Letter' - } - }); - }); - - test(`Get /letters/{id} returns 404 if no resource is found for id`, async ({ request }) => - { - const id = '11'; - const headers = createValidRequestHeaders(); - const response = await request.get(`${baseUrl}/${SUPPLIER_LETTERS}/${id}`, { - headers: headers, - }); - - const responseBody = await response.json(); - expect(response.status()).toBe(404); - expect(responseBody).toMatchObject(error404ResponseBody()); - }); - - test(`Get /letters/{id} returns 500 if letter is not found for supplierId ${SUPPLIERID}`, async ({ request }) => - { - const id = 'non-existing-id-12345'; - const headers = createValidRequestHeaders(); - const response = await request.get(`${baseUrl}/${SUPPLIER_LETTERS}/${id}`, { - headers: headers, - }); - - const responseBody = await response.json(); - expect(response.status()).toBe(500); - expect(responseBody).toMatchObject(error500ResponseBody(id)); - }); - -}); diff --git a/tests/component-tests/apiGateway-tests/getLetters.spec.ts b/tests/component-tests/apiGateway-tests/getLetters.spec.ts deleted file mode 100644 index b21fa5b9..00000000 --- a/tests/component-tests/apiGateway-tests/getLetters.spec.ts +++ /dev/null @@ -1,93 +0,0 @@ -import { test, expect } from '@playwright/test'; -import { SUPPLIER_LETTERS } from '../../constants/api_constants'; -import { createHeaderWithNoCorrelationId, createInvalidRequestHeaders, createValidRequestHeaders } from '../../constants/request_headers'; -import { getRestApiGatewayBaseUrl } from '../../helpers/awsGatewayHelper'; -import { validateApiResponse } from '../../helpers/validateJsonSchema'; - -let baseUrl: string; - -test.beforeAll(async () => { - baseUrl = await getRestApiGatewayBaseUrl(); -}); - -test.describe('API Gateway Tests To Get List Of Pending Letters', () => -{ - test('GET /letters should return 200 and list items', async ({ request }) => - { - const header = createValidRequestHeaders(); - const response = await request.get(`${baseUrl}/${SUPPLIER_LETTERS}` ,{ - headers: header, - params: { - limit:'2'}, - }, - ); - - expect(response.status()).toBe(200); - const responseBody = await response.json(); - expect(responseBody.data.length.toString()).toEqual('2'); - - const validationResult = validateApiResponse("get", "/letters", response.status(), responseBody); - if (validationResult) { - console.error("API response validation failed:", validationResult); - } - expect(validationResult).toBeUndefined(); - }); - - test('GET /letters with invalid authentication should return 403', async ({ request }) => { - const header = createInvalidRequestHeaders(); - const response = await request.get(`${baseUrl}/${SUPPLIER_LETTERS}` ,{ - headers: header, - params:{ - limit:'2' - }, - }, - ); - expect(response.status()).toBe(403); - const responseBody = await response.json(); - expect(responseBody).toMatchObject({ - Message : 'User is not authorized to access this resource with an explicit deny in an identity-based policy' } - ); - }); - - test('GET /letters with empty correlationId should return 500', async ({ request }) => { - const header = createHeaderWithNoCorrelationId(); - const response = await request.get(`${baseUrl}/${SUPPLIER_LETTERS}` ,{ - headers: header, - params:{ - limit:'2' - }, - }, - ); - expect(response.status()).toBe(500); - const responseBody = await response.json(); - expect(responseBody.errors[0].code).toBe('NOTIFY_INTERNAL_SERVER_ERROR'); - expect(responseBody.errors[0].detail).toBe("The request headers don't contain the APIM correlation id"); - - }); - - test('GET /letters with invalid query param return 400', async ({ request }) => { - const header = createValidRequestHeaders(); - const response = await request.get(`${baseUrl}/${SUPPLIER_LETTERS}` ,{ - headers: header, - params:{ - limit:'?' - }, - }); - expect(response.status()).toBe(400); - const responseBody = await response.json(); - expect(responseBody).toMatchObject({ - errors: [ - { - id: '12345', - code: 'NOTIFY_INVALID_REQUEST', - links: { - about: 'https://digital.nhs.uk/developer/api-catalogue/nhs-notify-supplier' - }, - status: '400', - title: 'Invalid request', - detail: 'The limit parameter is not a number' - } - ] - }); - }); -}); diff --git a/tests/component-tests/apiGateway-tests/testCases/UpdateLetterStatus.ts b/tests/component-tests/apiGateway-tests/testCases/UpdateLetterStatus.ts deleted file mode 100644 index 875541b1..00000000 --- a/tests/component-tests/apiGateway-tests/testCases/UpdateLetterStatus.ts +++ /dev/null @@ -1,114 +0,0 @@ - -import { RequestHeaders } from '../../../constants/request_headers'; -import { SUPPLIERID } from '../../../constants/api_constants'; -import { ErrorMessageBody } from '../../../helpers/commonTypes'; - -export type PatchMessageRequestBody = { - data: { - type: string; - id: string; - attributes: { - reasonCode?: string | number; - reasonText?: string; - status: string; - }; - }; -}; - -export type PatchMessageResponseBody = { - data: { - type: string; - id: string; - attributes: { - reasonCode?: number; - reasonText?: string; - status: string; - specificationId:string; - groupId?:string; - }; - }; -}; - -export function patchRequestHeaders(): RequestHeaders { - let requestHeaders: RequestHeaders; - requestHeaders = { - headerauth1: process.env.HEADERAUTH || '', - 'NHSD-Supplier-ID': SUPPLIERID, - 'NHSD-Correlation-ID': '12344', - 'X-Request-ID': 'requestId1' - }; - return requestHeaders; -}; - - -export function patchValidRequestBody (id: string, status: string) : PatchMessageRequestBody{ - let requestBody: PatchMessageRequestBody; - - requestBody = { - data: { -  attributes: { -   status: status, - }, - type: 'Letter', - id: id - } - - }; - return requestBody; -} - -export function patchFailureRequestBody (id: string, status: string) : PatchMessageRequestBody{ - let requestBody: PatchMessageRequestBody; - - requestBody = { - data: { - attributes: { - status: status, - reasonCode: 123, - reasonText: 'Test Reason' - }, - type: 'Letter', - id: id - } - - }; - return requestBody; -} - -export function patch400ErrorResponseBody () : ErrorMessageBody{ - let responseBody: ErrorMessageBody; - responseBody = { - errors: [ - { - id : '12344', - code : 'NOTIFY_INVALID_REQUEST', - "links": { - "about": "https://digital.nhs.uk/developer/api-catalogue/nhs-notify-supplier" - }, - "status": "400", - "title": "Invalid request", - "detail": "The request body is invalid" - } - ] - }; - return responseBody; -}; - -export function patch500ErrorResponseBody (id: string) : ErrorMessageBody{ - let responseBody: ErrorMessageBody; - responseBody = { - errors: [ - { - id: "12344", - code: "NOTIFY_INTERNAL_SERVER_ERROR", - links: { - "about": "https://digital.nhs.uk/developer/api-catalogue/nhs-notify-supplier" - }, - status: "500", - title: "Internal server error", - detail: `Letter with id ${id} not found for supplier ${SUPPLIERID}` - } - ] - }; - return responseBody; -} diff --git a/tests/component-tests/apiGateway-tests/testCases/create-mi.ts b/tests/component-tests/apiGateway-tests/testCases/create-mi.ts new file mode 100644 index 00000000..0fcbd2f9 --- /dev/null +++ b/tests/component-tests/apiGateway-tests/testCases/create-mi.ts @@ -0,0 +1,64 @@ +export type MiRequestBody = { + data: { + type: string; + attributes: { + groupId: string; + lineItem: string; + quantity: number; + specificationId: string; + stockRemaining: number; + timestamp: string; + }; + }; +}; + +export function miValidRequest(): MiRequestBody { + const requestBody: MiRequestBody = { + data: { + attributes: { + groupId: "group123", + lineItem: "envelope-business-standard", + quantity: 10, + specificationId: "Test-Spec-Id", + stockRemaining: 100, + timestamp: new Date().toISOString(), + }, + type: "ManagementInformation", + }, + }; + return requestBody; +} + +export function miInvalidRequest(): MiRequestBody { + const requestBody: MiRequestBody = { + data: { + attributes: { + groupId: "group123", + lineItem: "envelope-business-standard", + quantity: 10, + specificationId: "Test-Spec-Id", + stockRemaining: 100, + timestamp: new Date().toISOString(), + }, + type: "?", + }, + }; + return requestBody; +} + +export function miInvalidDateRequest(): MiRequestBody { + const requestBody: MiRequestBody = { + data: { + attributes: { + groupId: "group123", + lineItem: "envelope-business-standard", + quantity: 10, + specificationId: "Test-Spec-Id", + stockRemaining: 100, + timestamp: "2021-10-28T", + }, + type: "ManagementInformation", + }, + }; + return requestBody; +} diff --git a/tests/component-tests/apiGateway-tests/testCases/createMi.ts b/tests/component-tests/apiGateway-tests/testCases/createMi.ts deleted file mode 100644 index f7f0b222..00000000 --- a/tests/component-tests/apiGateway-tests/testCases/createMi.ts +++ /dev/null @@ -1,70 +0,0 @@ - - - -export type MiRequestBody = { - data: { - type: string; - attributes: { - groupId: string; - lineItem: string; - quantity: number; - specificationId: string; - stockRemaining: number; - timestamp: string; - }; - }; -}; - -export function miValidRequest() : MiRequestBody{ - let requestBody: MiRequestBody; - - requestBody = { - data: { -  attributes: { - groupId: 'group123', - lineItem: 'envelope-business-standard', - quantity: 10, - specificationId: 'Test-Spec-Id', - stockRemaining: 100, - timestamp: new Date().toISOString(), - }, - type: 'ManagementInformation', - }}; - return requestBody; -} - -export function miInvalidRequest() : MiRequestBody{ - let requestBody: MiRequestBody; - - requestBody = { - data: { -  attributes: { - groupId: 'group123', - lineItem: 'envelope-business-standard', - quantity: 10, - specificationId: 'Test-Spec-Id', - stockRemaining: 100, - timestamp: new Date().toISOString(), - }, - type: '?', - }}; - return requestBody; -} - -export function miInvalidDateRequest() : MiRequestBody{ - let requestBody: MiRequestBody; - - requestBody = { - data: { -  attributes: { - groupId: 'group123', - lineItem: 'envelope-business-standard', - quantity: 10, - specificationId: 'Test-Spec-Id', - stockRemaining: 100, - timestamp: '2021-10-28T', - }, - type: 'ManagementInformation', - }}; - return requestBody; -} diff --git a/tests/component-tests/apiGateway-tests/testCases/update-letter-status.ts b/tests/component-tests/apiGateway-tests/testCases/update-letter-status.ts new file mode 100644 index 00000000..5f4d14db --- /dev/null +++ b/tests/component-tests/apiGateway-tests/testCases/update-letter-status.ts @@ -0,0 +1,118 @@ +import { RequestHeaders } from "../../../constants/request-headers"; +import { SUPPLIERID } from "../../../constants/api-constants"; +import { ErrorMessageBody } from "../../../helpers/common-types"; + +export type PatchMessageRequestBody = { + data: { + type: string; + id: string; + attributes: { + reasonCode?: string; + reasonText?: string; + status: string; + }; + }; +}; + +export type PatchMessageResponseBody = { + data: { + type: string; + id: string; + attributes: { + reasonCode?: string; + reasonText?: string; + status: string; + specificationId: string; + groupId?: string; + }; + }; +}; + +export function patchRequestHeaders(): RequestHeaders { + let requestHeaders: RequestHeaders; + requestHeaders = { + headerauth1: process.env.HEADERAUTH || "", + "NHSD-Supplier-ID": SUPPLIERID, + "NHSD-Correlation-ID": "12344", + "X-Request-ID": "requestId1", + }; + return requestHeaders; +} + +export function patchValidRequestBody( + id: string, + status: string, +): PatchMessageRequestBody { + let requestBody: PatchMessageRequestBody; + + requestBody = { + data: { + attributes: { + status, + }, + type: "Letter", + id, + }, + }; + return requestBody; +} + +export function patchFailureRequestBody( + id: string, + status: string, +): PatchMessageRequestBody { + let requestBody: PatchMessageRequestBody; + + requestBody = { + data: { + attributes: { + status, + reasonCode: "R01", + reasonText: "Test Reason", + }, + type: "Letter", + id, + }, + }; + return requestBody; +} + +export function patch400ErrorResponseBody(): ErrorMessageBody { + let responseBody: ErrorMessageBody; + responseBody = { + errors: [ + { + id: "12344", + code: "NOTIFY_INVALID_REQUEST", + links: { + about: + "https://digital.nhs.uk/developer/api-catalogue/nhs-notify-supplier", + }, + status: "400", + title: "Invalid request", + detail: "The request body is invalid", + }, + ], + }; + return responseBody; +} + +export function patch500ErrorResponseBody(id: string): ErrorMessageBody { + let responseBody: ErrorMessageBody; + responseBody = { + errors: [ + { + id: "12344", + code: "NOTIFY_INTERNAL_SERVER_ERROR", + links: { + about: + "https://digital.nhs.uk/developer/api-catalogue/nhs-notify-supplier", + }, + status: "500", + title: "Internal server error", + detail: `Letter with id ${id} not found for supplier ${SUPPLIERID}`, + }, + ], + }; + return responseBody; +} diff --git a/tests/component-tests/apiGateway-tests/update-letter-status.spec.ts b/tests/component-tests/apiGateway-tests/update-letter-status.spec.ts new file mode 100644 index 00000000..bd90cb26 --- /dev/null +++ b/tests/component-tests/apiGateway-tests/update-letter-status.spec.ts @@ -0,0 +1,143 @@ +import { expect, test } from "@playwright/test"; +import { randomUUID } from "node:crypto"; +import { SUPPLIERID, SUPPLIER_LETTERS } from "../../constants/api-constants"; +import getRestApiGatewayBaseUrl from "../../helpers/aws-gateway-helper"; +import { + patch400ErrorResponseBody, + patchFailureRequestBody, + patchRequestHeaders, + patchValidRequestBody, +} from "./testCases/update-letter-status"; +import { + createTestData, + deleteLettersBySupplier, + getLettersBySupplier, +} from "../../helpers/generate-fetch-test-data"; +import { createInvalidRequestHeaders } from "../../constants/request-headers"; +import { + error400IdError, + error403ResponseBody, +} from "../../helpers/common-types"; + +let baseUrl: string; + +test.beforeAll(async () => { + baseUrl = await getRestApiGatewayBaseUrl(); +}); + +test.describe("API Gateway Tests to Verify Patch Status Endpoint", () => { + test(`Patch /letters returns 202 and status is updated to ACCEPTED`, async ({ + request, + }) => { + await createTestData(SUPPLIERID); + const letters = await getLettersBySupplier(SUPPLIERID, "PENDING", 1); + + if (!letters?.length) { + test.fail(true, `No PENDING letters found for supplier ${SUPPLIERID}`); + return; + } + const letter = letters[0]; + const headers = patchRequestHeaders(); + const body = patchValidRequestBody(letter.id, "ACCEPTED"); + + const response = await request.patch( + `${baseUrl}/${SUPPLIER_LETTERS}/${letter.id}`, + { + headers, + data: body, + }, + ); + + expect(response.status()).toBe(202); + + await deleteLettersBySupplier(letter.id); + }); + + test(`Patch /letters returns 202 and status is updated to REJECTED`, async ({ + request, + }) => { + await createTestData(SUPPLIERID); + const letters = await getLettersBySupplier(SUPPLIERID, "PENDING", 1); + + if (!letters?.length) { + test.fail(true, `No PENDING letters found for supplier ${SUPPLIERID}`); + return; + } + const letter = letters[0]; + const headers = patchRequestHeaders(); + const body = patchFailureRequestBody(letter.id, "REJECTED"); + + const response = await request.patch( + `${baseUrl}/${SUPPLIER_LETTERS}/${letter.id}`, + { + headers, + data: body, + }, + ); + + expect(response.status()).toBe(202); + + await deleteLettersBySupplier(letter.id); + }); + + test(`Patch /letters returns 400 if request Body is invalid`, async ({ + request, + }) => { + const id = randomUUID(); + const headers = patchRequestHeaders(); + const body = patchValidRequestBody(id, ""); + + const response = await request.patch( + `${baseUrl}/${SUPPLIER_LETTERS}/${id}`, + { + headers, + data: body, + }, + ); + + const responseBody = await response.json(); + + expect(response.status()).toBe(400); + expect(responseBody).toMatchObject(patch400ErrorResponseBody()); + }); + + test(`Patch /letters returns 400 if Id doesn't match with Id in request`, async ({ + request, + }) => { + const headers = patchRequestHeaders(); + const id = randomUUID(); + const body = patchValidRequestBody("Id", "PENDING"); + + const response = await request.patch( + `${baseUrl}/${SUPPLIER_LETTERS}/${id}`, + { + headers, + data: body, + }, + ); + + const responseBody = await response.json(); + expect(response.status()).toBe(400); + expect(responseBody).toMatchObject(error400IdError()); + }); + + test(`Patch /letters returns 403 for invalid headers`, async ({ + request, + }) => { + const headers = createInvalidRequestHeaders(); + const id = randomUUID(); + const body = patchValidRequestBody(id, "PENDING"); + + const response = await request.patch( + `${baseUrl}/${SUPPLIER_LETTERS}/${id}`, + { + headers, + data: body, + }, + ); + + const responseBody = await response.json(); + expect(response.status()).toBe(403); + expect(responseBody).toMatchObject(error403ResponseBody()); + }); +}); diff --git a/tests/component-tests/apiGateway-tests/updateLetterStatus.spec.ts b/tests/component-tests/apiGateway-tests/updateLetterStatus.spec.ts deleted file mode 100644 index 99484bf0..00000000 --- a/tests/component-tests/apiGateway-tests/updateLetterStatus.spec.ts +++ /dev/null @@ -1,133 +0,0 @@ -import { test, expect } from '@playwright/test'; -import { SUPPLIER_LETTERS, SUPPLIERID } from '../../constants/api_constants'; -import { getRestApiGatewayBaseUrl } from '../../helpers/awsGatewayHelper'; -import { patch400ErrorResponseBody, patch500ErrorResponseBody, patchFailureRequestBody, patchRequestHeaders, patchValidRequestBody } from './testCases/updateLetterStatus'; -import { createTestData, deleteLettersBySupplier, getLettersBySupplier } from '../../helpers/generate_fetch_testData'; -import { randomUUID } from 'crypto'; -import { createInvalidRequestHeaders } from '../../constants/request_headers'; -import { error403ResponseBody } from '../../helpers/commonTypes'; - -let baseUrl: string; - -test.beforeAll(async () => { - baseUrl = await getRestApiGatewayBaseUrl(); -}); - -test.describe('API Gateway Tests to Verify Patch Status Endpoint', () => { - test(`Patch /letters returns 200 and status is updated to ACCEPTED`, async ({ request }) => { - - await createTestData(SUPPLIERID); - const letters = await getLettersBySupplier(SUPPLIERID, 'PENDING', 1); - - if (!letters?.length) { - test.fail(true, `No PENDING letters found for supplier ${SUPPLIERID}`); - return; - } - const letter = letters[0]; - const headers = patchRequestHeaders(); - const body = patchValidRequestBody(letter.id, 'ACCEPTED'); - - const response = await request.patch(`${baseUrl}/${SUPPLIER_LETTERS}/${letter.id}`, { - headers: headers, - data: body - }); - - const responseBody = await response.json(); - expect(response.status()).toBe(200); - expect(responseBody).toMatchObject({ - data:{ - attributes: { - status: 'ACCEPTED', - specificationId: letter.specificationId, - groupId: letter.groupId, - }, - id: letter.id, - type: 'Letter' - } - }); - - await deleteLettersBySupplier(letter.id); - }); - - test(`Patch /letters returns 200 and status is updated to REJECTED`, async ({ request }) => { - - await createTestData(SUPPLIERID); - const letters = await getLettersBySupplier(SUPPLIERID, 'PENDING', 1); - - if (!letters?.length) { - test.fail(true, `No PENDING letters found for supplier ${SUPPLIERID}`); - return; - } - const letter = letters[0]; - const headers = patchRequestHeaders(); - const body = patchFailureRequestBody(letter.id, 'REJECTED'); - - const response = await request.patch(`${baseUrl}/${SUPPLIER_LETTERS}/${letter.id}`, { - headers: headers, - data: body - }); - - const responseBody = await response.json(); - expect(response.status()).toBe(200); - expect(responseBody).toMatchObject({ - data:{ - attributes: { - status: 'REJECTED', - specificationId: letter.specificationId, - groupId: letter.groupId, - }, - id: letter.id, - type: 'Letter' - } - }); - - await deleteLettersBySupplier(letter.id); - }); - - test(`Patch /letters returns 400 if request Body is invalid`, async ({ request }) => { - - const id = randomUUID() - const headers = patchRequestHeaders(); - const body = patchValidRequestBody(id, ''); - - const response = await request.patch(`${baseUrl}/${SUPPLIER_LETTERS}/${id}`, { - headers: headers, - data: body - }); - - const responseBody = await response.json(); - - expect(response.status()).toBe(400); - expect(responseBody).toMatchObject(patch400ErrorResponseBody()); - }); - - test(`Patch /letters returns 500 if Id doesn't exist for SupplierId`, async ({ request }) => { - const headers = patchRequestHeaders(); - const id = randomUUID() - const body = patchValidRequestBody(id, 'PENDING'); - - const response = await request.patch(`${baseUrl}/${SUPPLIER_LETTERS}/${id}`, { - headers: headers, - data: body - }); - - const responseBody = await response.json(); - expect(response.status()).toBe(500); - expect(responseBody).toMatchObject(patch500ErrorResponseBody(id)); - }); - - test(`Patch /letters returns 403 for invalid headers`, async ({ request }) => { - const headers = createInvalidRequestHeaders(); - const id = randomUUID() - const body = patchValidRequestBody(id, 'PENDING'); - - const response = await request.patch(`${baseUrl}/${SUPPLIER_LETTERS}/${id}`, { - headers: headers, - data: body - }); - - const responseBody = await response.json(); - expect(response.status()).toBe(403); - expect(responseBody).toMatchObject(error403ResponseBody()); - }); -}); diff --git a/tests/config/global-setup.ts b/tests/config/global-setup.ts new file mode 100644 index 00000000..b1ea091b --- /dev/null +++ b/tests/config/global-setup.ts @@ -0,0 +1,35 @@ +import { + checkSupplierExists, + createSupplierEntry, + createTestData, +} from "../helpers/generate-fetch-test-data"; +import { SUPPLIERID } from "../constants/api-constants"; + +async function globalSetup() { + console.log(""); + console.log("*** BEGINNING GLOBAL SETUP ***"); + console.log(""); + + // create test data + + await createTestData(SUPPLIERID, 10); + + // check supplier exists + const supplier = await checkSupplierExists(SUPPLIERID); + if (supplier) { + console.log(`Supplier with ID ${SUPPLIERID} already exists.`); + console.log(""); + console.log("*** GLOBAL SETUP COMPLETE ***"); + console.log(""); + return; + } + + console.log(`Creating supplier entry with ID: ${SUPPLIERID}`); + await createSupplierEntry(SUPPLIERID); + + console.log(""); + console.log("*** GLOBAL SETUP COMPLETE ***"); + console.log(""); +} + +export default globalSetup; diff --git a/tests/config/main.config.ts b/tests/config/main.config.ts index 4c87088a..30938643 100644 --- a/tests/config/main.config.ts +++ b/tests/config/main.config.ts @@ -1,17 +1,18 @@ -import {defineConfig, PlaywrightTestConfig } from '@playwright/test'; -import baseConfig from './playwright.base.config'; -import { getReporters } from './reporters'; -import path from 'path'; +import { PlaywrightTestConfig, defineConfig } from "@playwright/test"; +import path from "node:path"; +import baseConfig from "./playwright.base.config"; +import { getReporters } from "./reporters"; const localConfig: PlaywrightTestConfig = { ...baseConfig, + globalSetup: path.resolve(__dirname, "./global-setup.ts"), /* Reporter to use. See https://playwright.dev/docs/test-reporters */ - reporter: getReporters('api-test'), + reporter: getReporters("api-test"), projects: [ { - name: 'component-tests', - testDir: path.resolve(__dirname, '../component-tests'), - testMatch: '**/*.spec.ts', + name: "component-tests", + testDir: path.resolve(__dirname, "../component-tests"), + testMatch: "**/*.spec.ts", }, ], }; diff --git a/tests/config/playwright.base.config.ts b/tests/config/playwright.base.config.ts index c6edb35a..736421ab 100644 --- a/tests/config/playwright.base.config.ts +++ b/tests/config/playwright.base.config.ts @@ -1,7 +1,7 @@ -import { defineConfig, PlaywrightTestConfig } from '@playwright/test'; -import 'dotenv/config'; +/* eslint-disable radix */ +import { PlaywrightTestConfig, defineConfig } from "@playwright/test"; +import "dotenv/config"; -const baseUrl = process.env.NHSD_APIM_PROXY_URL || 'http://localhost:3000/'; const envMaxInstances = Number.parseInt(process.env.WORKERS_MAX_INST!) || 10; /** * See https://playwright.dev/docs/test-configuration. diff --git a/tests/config/reporters.ts b/tests/config/reporters.ts index 94a68a63..f30c0815 100644 --- a/tests/config/reporters.ts +++ b/tests/config/reporters.ts @@ -1,4 +1,5 @@ import type { ReporterDescription } from '@playwright/test'; +import path from 'path'; const resultsDir = process.env.RESULTS_DIR || 'results'; const reportsDir = process.env.REPORTS_DIR || 'reports'; @@ -21,7 +22,7 @@ export function getReporters(allureFolder: string) { [ 'html', { - outputFolder: `../target/test-artifacts/${reportsDir}/html-report`, + outputFolder: path.resolve(__dirname, '../playwright-report'), open: process.env.CI ? 'never' : 'on-failure', }, ], diff --git a/tests/constants/api-constants.ts b/tests/constants/api-constants.ts new file mode 100644 index 00000000..3ce26719 --- /dev/null +++ b/tests/constants/api-constants.ts @@ -0,0 +1,10 @@ +export const SUPPLIER_LETTERS = "letters"; +export const SUPPLIER_API_URL_SANDBOX = + "https://internal-dev-sandbox.api.service.nhs.uk/nhs-notify-supplier"; +export const AWS_REGION = "eu-west-2"; +export const envName = process.env.PR_NUMBER ?? "main"; +export const API_NAME = `nhs-${envName}-supapi`; +export const LETTERSTABLENAME = `nhs-${envName}-supapi-letters`; +export const SUPPLIERID = "TestSupplier1"; +export const MI_ENDPOINT = "mi"; +export const SUPPLIERTABLENAME = `nhs-${envName}-supapi-suppliers`; diff --git a/tests/constants/api_constants.ts b/tests/constants/api_constants.ts deleted file mode 100644 index 53e1d703..00000000 --- a/tests/constants/api_constants.ts +++ /dev/null @@ -1,8 +0,0 @@ -export const SUPPLIER_LETTERS = 'letters'; -export const SUPPLIER_API_URL_SANDBOX = 'https://internal-dev-sandbox.api.service.nhs.uk/nhs-notify-supplier'; -export const AWS_REGION = 'eu-west-2'; -export const envName = process.env.PR_NUMBER ?? 'main'; -export const API_NAME = `nhs-${envName}-supapi`; -export const LETTERSTABLENAME = `nhs-${envName}-supapi-letters`; -export const SUPPLIERID = 'TestSupplier1'; -export const MI_ENDPOINT = 'mi'; diff --git a/tests/constants/request-headers.ts b/tests/constants/request-headers.ts new file mode 100644 index 00000000..6e113e2a --- /dev/null +++ b/tests/constants/request-headers.ts @@ -0,0 +1,63 @@ +/* eslint-disable prefer-const */ +import { randomUUID } from "node:crypto"; +import { SUPPLIERID } from "./api-constants"; + +export const sandBoxHeader: RequestSandBoxHeaders = { + "X-Request-ID": randomUUID(), + "NHSD-Supplier-ID": SUPPLIERID, + "Content-Type": "application/vnd.api+json", + "X-Correlation-ID": randomUUID(), +}; + +export interface RequestHeaders { + "NHSD-Supplier-ID": string; + "NHSD-Correlation-ID": string; + [key: string]: string; +} + +export interface RequestSandBoxHeaders { + "X-Request-ID": string; + "Content-Type": string; + "X-Correlation-ID": string; + [key: string]: string; +} + +export function createInvalidRequestHeaders(): RequestHeaders { + let requestHeaders: RequestHeaders; + requestHeaders = { + "NHSD-Supplier-ID": "NoSupplier", + "NHSD-Correlation-ID": "1234", + "X-Request-ID": "requestId1", + }; + return requestHeaders; +} + +export function createHeaderWithNoCorrelationId(): RequestHeaders { + let requestHeaders: RequestHeaders; + requestHeaders = { + "NHSD-Supplier-ID": SUPPLIERID, + "NHSD-Correlation-ID": "", + "X-Request-ID": "requestId1", + }; + return requestHeaders; +} + +export function createValidRequestHeaders(): RequestHeaders { + let requestHeaders: RequestHeaders; + requestHeaders = { + "NHSD-Supplier-ID": SUPPLIERID, + "NHSD-Correlation-ID": "12345", + "X-Request-ID": "requestId1", + }; + return requestHeaders; +} + +export function createHeaderWithNoRequestId(): RequestHeaders { + let requestHeaders: RequestHeaders; + requestHeaders = { + "NHSD-Supplier-ID": SUPPLIERID, + "NHSD-Correlation-ID": "1234", + "X-Request-ID": "", + }; + return requestHeaders; +} diff --git a/tests/constants/request_headers.ts b/tests/constants/request_headers.ts deleted file mode 100644 index 5440f818..00000000 --- a/tests/constants/request_headers.ts +++ /dev/null @@ -1,66 +0,0 @@ -import { randomUUID } from 'node:crypto'; -import { SUPPLIERID } from './api_constants'; - -export const sandBoxHeader: RequestSandBoxHeaders = { - 'X-Request-ID': randomUUID(), - 'Content-Type': 'application/vnd.api+json', - 'X-Correlation-ID': randomUUID(), -}; - -export interface RequestHeaders { - headerauth1: string; - 'NHSD-Supplier-ID': string; - 'NHSD-Correlation-ID': string; - [key: string]: string; -} - -export interface RequestSandBoxHeaders { - 'X-Request-ID': string; - 'Content-Type': string; - 'X-Correlation-ID': string; - [key: string]: string; -} - -export function createInvalidRequestHeaders(): RequestHeaders { - let requestHeaders: RequestHeaders; - requestHeaders = { - headerauth1: '', - 'NHSD-Supplier-ID': SUPPLIERID, - 'NHSD-Correlation-ID': '1234', - 'X-Request-ID': 'requestId1' - }; - return requestHeaders; -} - -export function createHeaderWithNoCorrelationId(): RequestHeaders { - let requestHeaders: RequestHeaders; - requestHeaders = { - headerauth1: process.env.HEADERAUTH || '', - 'NHSD-Supplier-ID': SUPPLIERID, - 'NHSD-Correlation-ID': '', - 'X-Request-ID': 'requestId1' - }; - return requestHeaders; -} - -export function createValidRequestHeaders(): RequestHeaders{ - let requestHeaders: RequestHeaders; - requestHeaders = { - headerauth1: process.env.HEADERAUTH || '', - 'NHSD-Supplier-ID': SUPPLIERID, - 'NHSD-Correlation-ID': '12345', - 'X-Request-ID': 'requestId1' - }; - return requestHeaders; -} - -export function createHeaderWithNoRequestId(): RequestHeaders { - let requestHeaders: RequestHeaders; - requestHeaders = { - headerauth1: process.env.HEADERAUTH || '', - 'NHSD-Supplier-ID': SUPPLIERID, - 'NHSD-Correlation-ID': '1234', - 'X-Request-ID': '' - }; - return requestHeaders; -} diff --git a/tests/helpers/awsGatewayHelper.ts b/tests/helpers/aws-gateway-helper.ts similarity index 60% rename from tests/helpers/awsGatewayHelper.ts rename to tests/helpers/aws-gateway-helper.ts index 9e0db8bb..42f87fe2 100644 --- a/tests/helpers/awsGatewayHelper.ts +++ b/tests/helpers/aws-gateway-helper.ts @@ -1,8 +1,10 @@ -import { APIGatewayClient, GetRestApisCommand } from "@aws-sdk/client-api-gateway"; -import { AWS_REGION, API_NAME } from "../constants/api_constants"; - -export async function getRestApiGatewayBaseUrl(): Promise { +import { + APIGatewayClient, + GetRestApisCommand, +} from "@aws-sdk/client-api-gateway"; +import { API_NAME, AWS_REGION } from "../constants/api-constants"; +export default async function getRestApiGatewayBaseUrl(): Promise { const region = AWS_REGION; const client = new APIGatewayClient({ region }); diff --git a/tests/helpers/common-types.ts b/tests/helpers/common-types.ts new file mode 100644 index 00000000..e831f151 --- /dev/null +++ b/tests/helpers/common-types.ts @@ -0,0 +1,140 @@ + +export type ErrorLink = { + about: string; +}; + +type ErrorResponse = { + id: string; + code: string; + links: ErrorLink; + status: string; + title: string; + detail: string; +}; + +export type ErrorMessageBody = { + errors: ErrorResponse[]; +}; + +export function error404ResponseBody(): ErrorMessageBody { + const responseBody: ErrorMessageBody = { + errors: [ + { + id: "12345", + code: "NOTIFY_LETTER_NOT_FOUND", + links: { + about: + "https://digital.nhs.uk/developer/api-catalogue/nhs-notify-supplier", + }, + status: "404", + title: "Not found", + detail: "No resource found with that ID", + }, + ], + }; + return responseBody; +} + +export function error500ResponseBody(): ErrorMessageBody { + const responseBody: ErrorMessageBody = { + errors: [ + { + id: "12345", + code: "NOTIFY_INTERNAL_SERVER_ERROR", + links: { + about: + "https://digital.nhs.uk/developer/api-catalogue/nhs-notify-supplier", + }, + status: "500", + title: "Internal server error", + detail: "Unexpected error", + }, + ], + }; + return responseBody; +} + +export function error400ResponseBody(): ErrorMessageBody { + const responseBody: ErrorMessageBody = { + errors: [ + { + id: "12345", + code: "NOTIFY_INVALID_REQUEST", + links: { + about: + "https://digital.nhs.uk/developer/api-catalogue/nhs-notify-supplier", + }, + status: "400", + title: "Invalid request", + detail: "The request body is invalid", + }, + ], + }; + return responseBody; +} + +export function requestId500Error(): ErrorMessageBody { + const responseBody: ErrorMessageBody = { + errors: [ + { + id: "1234", + code: "NOTIFY_INTERNAL_SERVER_ERROR", + links: { + about: + "https://digital.nhs.uk/developer/api-catalogue/nhs-notify-supplier", + }, + status: "500", + title: "Internal server error", + detail: "Unexpected error", + }, + ], + }; + return responseBody; +} + +export function error403ResponseBody(): { Message: string } { + return { + Message: + "User is not authorized to access this resource with an explicit deny in an identity-based policy", + }; +} + +export function error400InvalidDate(): ErrorMessageBody { + const responseBody: ErrorMessageBody = { + errors: [ + { + id: "12345", + code: "NOTIFY_INVALID_REQUEST", + links: { + about: + "https://digital.nhs.uk/developer/api-catalogue/nhs-notify-supplier", + }, + status: "400", + title: "Invalid request", + detail: + "Timestamps should be UTC date/times in ISO8601 format, with a Z suffix", + }, + ], + }; + return responseBody; +} + +export function error400IdError(): ErrorMessageBody { + const responseBody: ErrorMessageBody = { + errors: [ + { + id: "12344", + code: "NOTIFY_INVALID_REQUEST", + links: { + about: + "https://digital.nhs.uk/developer/api-catalogue/nhs-notify-supplier", + }, + status: "400", + title: "Invalid request", + detail: + "The letter ID in the request body does not match the letter ID path parameter", + }, + ], + }; + return responseBody; +} diff --git a/tests/helpers/commonTypes.ts b/tests/helpers/commonTypes.ts deleted file mode 100644 index c76ef4f7..00000000 --- a/tests/helpers/commonTypes.ts +++ /dev/null @@ -1,124 +0,0 @@ -import { error } from "console"; -import { SUPPLIERID } from "../constants/api_constants"; - -export type ErrorLink = { - about: string; -}; - -type ErrorResponse = { - id: string; - code: string; - links: ErrorLink; - status: string; - title: string; - detail: string; -}; - -export type ErrorMessageBody = { - errors: ErrorResponse[]; -}; - -export function error404ResponseBody () : ErrorMessageBody{ - let responseBody: ErrorMessageBody; - responseBody = { - errors: [ - { - id: "12345", - code: "NOTIFY_LETTER_NOT_FOUND", - links: { - "about": "https://digital.nhs.uk/developer/api-catalogue/nhs-notify-supplier" - }, - status: "404", - title: "Not found", - detail: "No resource found with that ID" - } - ] - }; - return responseBody; -}; - -export function error500ResponseBody (id: string) : ErrorMessageBody{ - let responseBody: ErrorMessageBody; - responseBody = { - errors: [ - { - id: "12345", - code: "NOTIFY_INTERNAL_SERVER_ERROR", - links: { - "about": "https://digital.nhs.uk/developer/api-catalogue/nhs-notify-supplier" - }, - status: "500", - title: "Internal server error", - detail: `Letter with id ${id} not found for supplier ${SUPPLIERID}` - } - ] - }; - return responseBody; -} - - -export function error400ResponseBody () : ErrorMessageBody{ - - let responseBody: ErrorMessageBody; - responseBody = { - errors: [ - { - id: '12345', - code: "NOTIFY_INVALID_REQUEST", - links: { - "about": "https://digital.nhs.uk/developer/api-catalogue/nhs-notify-supplier" - }, - status: "400", - title: "Invalid request", - detail: "The request body is invalid" - } - ] - }; - return responseBody; -} - -export function requestId500Error () : ErrorMessageBody{ - let responseBody: ErrorMessageBody; - - responseBody = { - errors: [ - { - id: "1234", - code: "NOTIFY_INTERNAL_SERVER_ERROR", - links: { - "about": "https://digital.nhs.uk/developer/api-catalogue/nhs-notify-supplier" - }, - status: "500", - title: "Internal server error", - detail: "The request headers don't contain the x-request-id" - } - ] - }; - return responseBody; -} - -export function error403ResponseBody () : { Message: string }{ - return { - Message : 'User is not authorized to access this resource with an explicit deny in an identity-based policy' - }; -} - -export function error400InvalidDate () : ErrorMessageBody{ - - let responseBody: ErrorMessageBody; - responseBody = { - errors: [ - { - id: '12345', - code: "NOTIFY_INVALID_REQUEST", - links: { - "about": "https://digital.nhs.uk/developer/api-catalogue/nhs-notify-supplier" - }, - status: "400", - title: "Invalid request", - detail: "Timestamps should be UTC date/times in ISO8601 format, with a Z suffix" - } - ] - }; - return responseBody; -} diff --git a/tests/helpers/generate-fetch-test-data.ts b/tests/helpers/generate-fetch-test-data.ts new file mode 100644 index 00000000..5294f243 --- /dev/null +++ b/tests/helpers/generate-fetch-test-data.ts @@ -0,0 +1,115 @@ +import { DynamoDBClient } from "@aws-sdk/client-dynamodb"; +import { + DeleteCommand, + DynamoDBDocumentClient, + QueryCommand, +} from "@aws-sdk/lib-dynamodb"; +import { + LETTERSTABLENAME, + SUPPLIERID, + SUPPLIERTABLENAME, + envName, +} from "../constants/api-constants"; +import { createSupplierData, runCreateLetter } from "./pnpm-helpers"; + +const ddb = new DynamoDBClient({}); +const docClient = DynamoDBDocumentClient.from(ddb); + +export interface SupplierApiLetters { + supplierId: string; + specificationId: string; + supplierStatus: string; + createdAt: string; + supplierStatusSk: string; + updatedAt: string; + groupId: string; + reasonCode: string; + id: string; + url: string; + ttl: string; + reasonText: string; + status: string; +} + +export async function createTestData( + supplierId: string, + count?: number, +): Promise { + await runCreateLetter({ + filter: "nhs-notify-supplier-api-letter-test-data-utility", + supplierId, + environment: envName, + awsAccountId: "820178564574", + groupId: "TestGroupID", + specificationId: "TestSpecificationID", + status: "PENDING", + count: count || 1, + }); +} + +export const getLettersBySupplier = async ( + supplierId: string, + status: string, + limit: number, +) => { + const supplierStatus = `${supplierId}#${status}`; + const params = { + TableName: LETTERSTABLENAME, + IndexName: "supplierStatus-index", + KeyConditionExpression: "supplierStatus = :supplierStatus", + ProjectionExpression: + "id, specificationId, groupId, reasonCode, reasonText", + ExpressionAttributeValues: { + ":supplierStatus": supplierStatus, + }, + Limit: limit, + }; + + const { Items } = await docClient.send(new QueryCommand(params)); + if (!Items || Items.length === 0) { + throw new Error(`Unexpectedly found no data found for ${supplierId}.`); + } + return Items as SupplierApiLetters[]; +}; + +export const deleteLettersBySupplier = async (id: string) => { + const resp = await docClient.send( + new DeleteCommand({ + TableName: LETTERSTABLENAME, + Key: { supplierId: SUPPLIERID, id }, + ReturnValues: "ALL_OLD", + }), + ); + return resp.Attributes; +}; + +export async function checkSupplierExists( + supplierId: string, +): Promise { + try { + const params = { + TableName: SUPPLIERTABLENAME, + KeyConditionExpression: "id = :id", + ExpressionAttributeValues: { + ":id": supplierId, + }, + }; + + const { Items } = await docClient.send(new QueryCommand(params)); + return Items !== undefined && Items.length > 0; + } catch (error) { + console.error("Error checking supplier existence:", error); + return false; + } +} + +export async function createSupplierEntry(supplierId: string): Promise { + await createSupplierData({ + filter: "nhs-notify-supplier-api-letter-test-data-utility", + supplierId, + apimId: supplierId, + name: "TestSupplier", + environment: envName, + status: "ENABLED", + }); +} diff --git a/tests/helpers/generate_fetch_testData.ts b/tests/helpers/generate_fetch_testData.ts deleted file mode 100644 index 2fd63aa6..00000000 --- a/tests/helpers/generate_fetch_testData.ts +++ /dev/null @@ -1,70 +0,0 @@ -import { envName, LETTERSTABLENAME, SUPPLIERID } from "../constants/api_constants"; -import { runCreateLetter } from "./pnpmHelpers"; -import { DynamoDBClient } from '@aws-sdk/client-dynamodb'; -import { DeleteCommand, DynamoDBDocumentClient, QueryCommand } from '@aws-sdk/lib-dynamodb'; - -const ddb = new DynamoDBClient({}); -const docClient = DynamoDBDocumentClient.from(ddb); - -export interface SupplierApiLetters { - supplierId: string, - specificationId: string, - supplierStatus: string, - createdAt: string, - supplierStatusSk: string, - updatedAt: string, - groupId: string, - reasonCode: string, - id: string, - url: string, - ttl: string, - reasonText: string, - status: string -}; - -export async function createTestData(supplierId: string): Promise { - - await runCreateLetter({ - filter: 'nhs-notify-supplier-api-data-generator', - supplierId: supplierId, - environment: envName, - awsAccountId: '820178564574', - groupId: 'TestGroupID', - specificationId: 'TestSpecificationID', - status: 'PENDING', - count: 1, - }); -}; - -export const getLettersBySupplier = async(supplierId: string, status: string, limit: number) => { - - const supplierStatus = `${supplierId}#${status}`; - const params = { - TableName: LETTERSTABLENAME, - IndexName: 'supplierStatus-index', - KeyConditionExpression: 'supplierStatus = :supplierStatus', - ProjectionExpression: - 'id, specificationId, groupId, reasonCode, reasonText', - ExpressionAttributeValues: { - ':supplierStatus': supplierStatus, - }, - Limit: limit, - }; - - const { Items } = await docClient.send(new QueryCommand(params)); - if (!Items || Items.length === 0) { - throw new Error(`Unexpectedly found no data found for ${supplierId}.`); - } - return Items as SupplierApiLetters[]; -}; - -export const deleteLettersBySupplier = async(id: string) => { - const resp = await docClient.send( - new DeleteCommand({ - TableName: LETTERSTABLENAME, - Key: { supplierId: SUPPLIERID, id }, - ReturnValues: 'ALL_OLD', - }) - ) - return resp.Attributes; -} diff --git a/tests/helpers/pnpm-helpers.ts b/tests/helpers/pnpm-helpers.ts new file mode 100644 index 00000000..13f0d4f4 --- /dev/null +++ b/tests/helpers/pnpm-helpers.ts @@ -0,0 +1,138 @@ +import { spawn } from "node:child_process"; +import path from "node:path"; + +/** + * Runs the "create-letter" CLI command via npm. + * + * @param options Command-line options for the script. + */ +export async function runCreateLetter(options: { + filter?: string; + supplierId: string; + environment: string; + awsAccountId: string; + groupId: string; + specificationId: string; + status: string; + count: number; +}) { + const { + awsAccountId, + count, + environment, + filter, + groupId, + specificationId, + status, + supplierId, + } = options; + + const workspaceRoot = path.resolve( + __dirname, + "../../scripts/utilities/letter-test-data", + ); + const cmd = process.platform === "win32" ? "npm.cmd" : "npm"; + const root = path.resolve(workspaceRoot); + + // Build arguments array + const args = [ + "-w", + String(filter), + // '--filter', String(filter), + "run", + "cli", + "--", + "create-letter-batch", + "--supplier-id", + supplierId, + "--environment", + environment, + "--awsAccountId", + awsAccountId, + "--group-id", + groupId, + "--specification-id", + specificationId, + "--status", + status, + "--count", + String(count), + ]; + + await new Promise((resolve, reject) => { + let output = ""; + const child = spawn(cmd, args, { + stdio: "inherit", + cwd: root, + shell: false, + }); + child.stdout?.on("id", (id) => { + const text = id.toString(); + output += text; + process.stdout.write(text); + }); + + child.on("close", (code) => + code === 0 ? resolve() : reject(new Error(`pnpm exited with ${code}`)), + ); + child.on("error", reject); + }); +} + +export async function createSupplierData(options: { + filter?: string; + supplierId: string; + name: string; + apimId: string; + environment: string; + status: string; +}) { + const { apimId, environment, name, status, supplierId, filter } = options; + + const workspaceRoot = path.resolve( + __dirname, + "../../scripts/utilities/supplier-data", + ); + const cmd = process.platform === "win32" ? "npm.cmd" : "npm"; + const root = path.resolve(workspaceRoot); + + // Build arguments array + const args = [ + "-w", + String(filter), + // '--filter', String(filter), + "run", + "cli", + "--", + "put-supplier", + "--id", + supplierId, + "--name", + name, + "--apimId", + apimId, + "--status", + status, + "--environment", + environment, + ]; + + await new Promise((resolve, reject) => { + let output = ""; + const child = spawn(cmd, args, { + stdio: "inherit", + cwd: root, + shell: false, + }); + child.stdout?.on("id", (id) => { + const text = id.toString(); + output += text; + process.stdout.write(text); + }); + + child.on("close", (code) => + code === 0 ? resolve() : reject(new Error(`pnpm exited with ${code}`)), + ); + child.on("error", reject); + }); +} diff --git a/tests/helpers/pnpmHelpers.ts b/tests/helpers/pnpmHelpers.ts deleted file mode 100644 index e0e748c0..00000000 --- a/tests/helpers/pnpmHelpers.ts +++ /dev/null @@ -1,76 +0,0 @@ -import { spawn } from 'child_process'; -import path from 'path'; - -/** - * Runs the "create-letter" CLI command via npm. - * - * @param options Command-line options for the script. - */ -export async function runCreateLetter(options: { - filter?: string; - supplierId: string; - environment: string; - awsAccountId: string; - groupId: string; - specificationId: string; - status: string; - count: number; -}) { - const { - filter, - supplierId, - environment, - awsAccountId, - groupId, - specificationId, - status, - count, - } = options; - - const workspaceRoot = path.resolve(__dirname, '../../scripts/test-data'); - const cmd = process.platform === 'win32' ? 'npm.cmd' : 'npm'; - const root = path.resolve(workspaceRoot); - console.log('Workspace root:', root); - - // Build arguments array - const args = [ - '-w', String(filter), - // '--filter', String(filter), - 'run', - 'cli', - '--', - 'create-letter', - '--supplier-id', - supplierId, - '--environment', - environment, - '--awsAccountId', - awsAccountId, - '--group-id', - groupId, - '--specification-id', - specificationId, - '--status', - status, - '--count', - String(count), - ]; - console.log('🚀 Running:', [cmd, ...args].join(' ')); - - await new Promise((resolve, reject) => { - let output = ''; - const child = spawn(cmd, args, { - stdio: 'inherit', - cwd: root, - shell: false, - }); - child.stdout?.on('id', (id) => { - const text = id.toString(); - output += text; - process.stdout.write(text); - }); - - child.on('close', (code) => code === 0 ? resolve() : reject(new Error(`pnpm exited with ${code}`))); - child.on('error', reject); - }); -} diff --git a/tests/helpers/validate-json-schema.ts b/tests/helpers/validate-json-schema.ts new file mode 100644 index 00000000..b3987d99 --- /dev/null +++ b/tests/helpers/validate-json-schema.ts @@ -0,0 +1,49 @@ +import OpenAPIResponseValidator from "openapi-response-validator"; +import path from "node:path"; + +const paths = path.resolve(__dirname, "../../build/notify-supplier.json"); + +type ValidationResult = ReturnType< + OpenAPIResponseValidator["validateResponse"] +>; +/** + * Validate a response against the OpenAPI spec for a given endpoint and method. + * + * @param method - HTTP method in lowercase ('get', 'post', etc.) + * @param path - API path (must match OpenAPI path exactly, e.g., '/items') + * @param status - HTTP status code returned by the API + * @param body - Response body to validate + * @returns ValidationResult or undefined if valid + */ +export default async function validateApiResponse( + method: string, + pathVar: string, + status: number, + body: any, +): Promise { + const openapiDoc = await import(paths); + + const pathItem = (openapiDoc.default.paths as Record)[pathVar]; + + if (!pathItem) throw new Error(`Path ${pathVar} not found in OpenAPI spec`); + + const operation = pathItem[method]; + if (!operation) + throw new Error(`Method ${method.toUpperCase()} not defined for ${path}`); + + // Find the response schema for the actual status code + const responseSchema = + operation.responses[status] || operation.responses.default; + if (!responseSchema) { + throw new Error( + `No schema defined for status ${status} at ${method.toUpperCase()} ${pathVar}`, + ); + } + + const validator = new OpenAPIResponseValidator({ + responses: { [status]: responseSchema }, + components: openapiDoc.default.components, + }); + + return validator.validateResponse(status, body); +} diff --git a/tests/helpers/validateJsonSchema.ts b/tests/helpers/validateJsonSchema.ts deleted file mode 100644 index e8e70cce..00000000 --- a/tests/helpers/validateJsonSchema.ts +++ /dev/null @@ -1,42 +0,0 @@ -import OpenAPIResponseValidator from "openapi-response-validator"; -import path from 'path'; - -const paths = path.resolve(__dirname, '../../build/notify-supplier.json'); -const openapiDoc = require(paths); - -type ValidationResult = ReturnType; -/** - * Validate a response against the OpenAPI spec for a given endpoint and method. - * - * @param method - HTTP method in lowercase ('get', 'post', etc.) - * @param path - API path (must match OpenAPI path exactly, e.g., '/items') - * @param status - HTTP status code returned by the API - * @param body - Response body to validate - * @returns ValidationResult or undefined if valid - */ -export function validateApiResponse( - method: string, - path: string, - status: number, - body: any -): ValidationResult { - const pathItem = (openapiDoc.paths as Record)[path]; - - if (!pathItem) throw new Error(`Path ${path} not found in OpenAPI spec`); - - const operation = pathItem[method]; - if (!operation) throw new Error(`Method ${method.toUpperCase()} not defined for ${path}`); - - // Find the response schema for the actual status code - const responseSchema = operation.responses[status] || operation.responses["default"]; - if (!responseSchema) { - throw new Error(`No schema defined for status ${status} at ${method.toUpperCase()} ${path}`); - } - - const validator = new OpenAPIResponseValidator({ - responses: { [status]: responseSchema }, - components: openapiDoc.components, - }); - - return validator.validateResponse(status, body); -} diff --git a/tests/jest.config.ts b/tests/jest.config.ts new file mode 100644 index 00000000..98557bd8 --- /dev/null +++ b/tests/jest.config.ts @@ -0,0 +1,10 @@ +export default { + preset: "ts-jest", + testEnvironment: "node", + testMatch: ["**/*.test.ts"], + coveragePathIgnorePatterns: ["/node_modules/", "__tests__"], + testPathIgnorePatterns: ["/node_modules/", "/dist/"], + transform: { + "^.+\\.ts$": ["ts-jest"], + }, +}; diff --git a/tests/mtls/mtls_test.spec.ts b/tests/mtls/mtls-test.spec.ts similarity index 100% rename from tests/mtls/mtls_test.spec.ts rename to tests/mtls/mtls-test.spec.ts diff --git a/tests/package.json b/tests/package.json index 78743234..743fc64c 100644 --- a/tests/package.json +++ b/tests/package.json @@ -4,32 +4,46 @@ "@aws-sdk/client-api-gateway": "^3.906.0", "@aws-sdk/client-dynamodb": "^3.858.0", "@aws-sdk/lib-dynamodb": "^3.858.0", + "@nhsdigital/nhs-notify-event-schemas-letter-rendering": "^2.0.1", + "@pact-foundation/pact": "^16.0.2", "allure-js-commons": "^3.3.3", - "charenc": "^0.0.2", - "crypt": "^0.0.2", "dotenv": "^17.2.2", - "is-buffer": "^1.1.6", "md5": "^2.3.0", "openapi-response-validator": "^12.1.3", "playwright": "^1.54.2", "playwright-core": "^1.54.2", - "undici-types": "^7.10.0" + "undici-types": "^7.10.0", + "zod": "^4.1.11" }, "description": "", "devDependencies": { "@playwright/test": "^1.55.1", + "@types/jest": "^29.5.14", "@types/node": "^24.3.1", "allure-commandline": "^2.34.1", - "allure-playwright": "^3.3.3" + "allure-playwright": "^3.3.3", + "jest": "^30.1.3", + "ts-jest": "^29.4.0", + "ts-node": "^10.9.2", + "typescript": "^5.9.3" }, "keywords": [], "license": "ISC", "main": "index.js", "name": "nhs-notify-supplier-api-tests", + "overrides": { + "@playwright/test": "1.57.0", + "playwright-core": "1.57.0" + }, "scripts": { "clean": "rimraf $(pwd)/target", + "lint": "eslint .", + "lint:fix": "eslint . --fix", "test:component": "playwright test --config=config/main.config.ts --max-failures=10 --project=component-tests", - "test:sandbox": "playwright test --config=config/sandbox.config.ts --max-failures=10 --project=sandbox" + "test:pact": "./pact-tests/run-pact-tests.sh", + "test:sandbox": "playwright test --config=config/sandbox.config.ts --max-failures=10 --project=sandbox", + "test:unit": "echo \"No unit tests for tests package\"", + "typecheck": "tsc --noEmit" }, - "version": "1.0.0" + "version": "0.0.1" } diff --git a/tests/pact-tests/consumer/letter-request-prepared.consumer.pact.test.ts b/tests/pact-tests/consumer/letter-request-prepared.consumer.pact.test.ts new file mode 100644 index 00000000..b73de11f --- /dev/null +++ b/tests/pact-tests/consumer/letter-request-prepared.consumer.pact.test.ts @@ -0,0 +1,85 @@ +/* eslint-disable security/detect-unsafe-regex, sonarjs/slow-regex */ +import path from "node:path"; +import { + MatchersV3, + MessageConsumerPact, + asynchronousBodyHandler, +} from "@pact-foundation/pact"; +import { $LetterRequestPreparedEventV2 } from "@nhsdigital/nhs-notify-event-schemas-letter-rendering"; + +async function handle(event: unknown) { + $LetterRequestPreparedEventV2.parse(event); +} + +describe("Pact Message Consumer - LetterRequestPrepared Event", () => { + const messagePact = new MessageConsumerPact({ + consumer: "supplier-api", + provider: "letter-request-prepared", + dir: path.resolve(__dirname, "../../.pacts/letter-rendering"), + pactfileWriteMode: "update", + logLevel: "error", + }); + + it("should validate a LetterRequest PREPARED v2 event", async () => { + await messagePact + .expectsToReceive("LetterRequestPrepared") + .withContent({ + id: MatchersV3.uuid("12f1f09c-a555-4d9b-8405-0b33490bc929"), + time: MatchersV3.datetime( + "yyyy-MM-dd'T'HH:mm:ss.SSS'Z'", + "2025-07-29T08:50:57.350Z", + ), + type: "uk.nhs.notify.letter-rendering.letter-request.prepared.v2", + source: MatchersV3.string( + "/data-plane/letter-rendering/comms-mgr-prod/prod", + ), + specversion: MatchersV3.regex(/\d+\.\d+/, "1.0"), + datacontenttype: "application/json", + dataschema: MatchersV3.regex( + /^https:\/\/notify\.nhs\.uk\/cloudevents\/schemas\/letter-rendering\/letter-request\.prepared\.2\.\d+\.\d+\.schema\.json$/, + "https://notify.nhs.uk/cloudevents/schemas/letter-rendering/letter-request.prepared.2.0.1.schema.json", + ), + dataschemaversion: MatchersV3.regex(/\d+\.\d+\.\d+/, "2.0.0"), + traceparent: MatchersV3.string( + "00-0af7651916cd43dd8448eb211c80319c-b7ad6b7169203331-01", + ), + recordedtime: MatchersV3.datetime( + "yyyy-MM-dd'T'HH:mm:ss.SSS'Z'", + "2025-08-28T08:45:00.000Z", + ), + severitynumber: MatchersV3.number(2), + severitytext: "INFO", + plane: "data", + subject: MatchersV3.regex( + /^client\/[\d_a-z-]+\/letter-request\/[^/]+(?:\/.*)?$/, + "client/00f3b388-bbe9-41c9-9e76-052d37ee8988/letter-request/0o5Fs0EELR0fUjHjbCnEtdUwQe4_0o5Fs0EELR0fUjHjbCnEtdUwQe5", + ), + data: { + campaignId: MatchersV3.string("flu-campaign-2025"), + clientId: MatchersV3.string("987e6543-21c0-4d5b-8f9a-abcdef123456"), + createdAt: MatchersV3.datetime( + "yyyy-MM-dd'T'HH:mm:ss.SSS'Z'", + "2025-07-29T08:45:00.000Z", + ), + domainId: MatchersV3.string( + "1y3q9987e6543-21c0-4d5b-8f9a-abcdef123456_34hEIElNxpdXPrNv6OBbU0bqNwG_34hEP2Xc3rGunPUAPe0Mst9IIoA", + ), + pageCount: MatchersV3.number(1), + requestId: MatchersV3.string("34hEIFCIw5DUTCRDMGv70CEzGgF"), + requestItemId: MatchersV3.string("34hEIElNxpdXPrNv6OBbU0bqNwG"), + requestItemPlanId: MatchersV3.string("34hEP2Xc3rGunPUAPe0Mst9IIoA"), + letterVariantId: MatchersV3.string("1y3q9v2zzzz"), + sha256Hash: MatchersV3.string( + "3a7bd3e2360a3d80c4d4e8b1e3e5e6e7e8e9e0e1e2e3e4e5e6e7e8e9e0e1e2e3", + ), + status: "PREPARED", + templateId: MatchersV3.string("template-005"), + url: MatchersV3.regex( + /^s3:\/\/.+/, + "s3://comms-123456789012-eu-west-2-pdf-pipeline/rendered/client/35b9VJ4ejJZXk0Z9HtQI9khryiz_35b9VgjHJYKoseXAHWT9i44qSbz.pdf", + ), + }, + }) + .verify(asynchronousBodyHandler(handle)); + }); +}); diff --git a/tests/pact-tests/provider/letter-request-prepared.provider.pact.test.ts b/tests/pact-tests/provider/letter-request-prepared.provider.pact.test.ts new file mode 100644 index 00000000..9fbf3384 --- /dev/null +++ b/tests/pact-tests/provider/letter-request-prepared.provider.pact.test.ts @@ -0,0 +1,25 @@ +import path from "node:path"; +import { MessageProviderPact } from "@pact-foundation/pact"; +import LetterRequestPreparedEvent from "@nhsdigital/nhs-notify-event-schemas-letter-rendering/examples/LetterRequestPrepared/v2.0.1.json"; + +describe("Letter rendering message provider tests", () => { + test("verify pacts", async () => { + const p = new MessageProviderPact({ + provider: "letter-rendering", + pactUrls: [ + path.join( + __dirname, + "../../.pacts/letter-rendering/supplier-api-letter-request-prepared.json", + ), + ], + messageProviders: { + LetterRequestPrepared: async () => ({ + ...LetterRequestPreparedEvent, + }), + }, + logLevel: "error", + }); + + await expect(p.verify()).resolves.not.toThrow(); + }, 60_000); +}); diff --git a/tests/pact-tests/run-pact-tests.sh b/tests/pact-tests/run-pact-tests.sh new file mode 100755 index 00000000..163268e4 --- /dev/null +++ b/tests/pact-tests/run-pact-tests.sh @@ -0,0 +1,20 @@ +#!/usr/bin/env bash + +set -euo pipefail + +# Ensure we have the latest package matching the major version +npm install --no-lockfile @nhsdigital/nhs-notify-event-schemas-letter-rendering@^2.0.0 + +# Remove old PACTs +rm -rf ./.pacts + +# Generate the PACT contracts +jest ./pact-tests/consumer + +# Check the PACT contracts by running them against the sample events published by the provider +jest ./pact-tests/provider + +# Copy over the new consumer PACT contracts to a publishable package +rm -rf ../pact-contracts/pacts +mkdir -p ../pact-contracts/pacts +cp -r ./.pacts/ ../pact-contracts/pacts diff --git a/tests/pact-tests/utils/get-sample-events.ts b/tests/pact-tests/utils/get-sample-events.ts new file mode 100644 index 00000000..bee79001 --- /dev/null +++ b/tests/pact-tests/utils/get-sample-events.ts @@ -0,0 +1,20 @@ +/* eslint-disable security/detect-non-literal-fs-filename */ +import path from "node:path"; +import { readdirSync } from "node:fs"; + +export const getSampleEvent = (eventType: string, version = "v2.0.0") => { + const basePath = path.join( + __dirname, + "../../node_modules/@nhsdigital/nhs-notify-event-schemas-letter-rendering/examples", + eventType, + version, + ); + + const fileNames = readdirSync(basePath); + + if (fileNames.length === 0) { + throw new Error(`Could not find sample ${eventType} events`); + } + + return path.join(basePath, fileNames[0]); +}; diff --git a/tests/sandbox/get-letter-status.spec.ts b/tests/sandbox/get-letter-status.spec.ts new file mode 100644 index 00000000..52401f71 --- /dev/null +++ b/tests/sandbox/get-letter-status.spec.ts @@ -0,0 +1,31 @@ +import { expect, test } from "@playwright/test"; +import { + SUPPLIER_API_URL_SANDBOX, + SUPPLIER_LETTERS, +} from "../constants/api-constants"; +import { apiSandboxGetLetterStatusTestData } from "./testCases/get-letter-status-test-cases"; + +test.describe("Sandbox Tests To Get Letter Status", () => { + for (const { + expectedResponse, + expectedStatus, + header, + id, + testCase, + } of apiSandboxGetLetterStatusTestData) { + test(`Get Letter Status endpoint returns ${testCase}`, async ({ + request, + }) => { + const response = await request.get( + `${SUPPLIER_API_URL_SANDBOX}/${SUPPLIER_LETTERS}/${id}`, + { + headers: header, + }, + ); + + expect(response.status()).toBe(expectedStatus); + const res = await response.json(); + expect(res).toEqual(expectedResponse); + }); + } +}); diff --git a/tests/sandbox/get-list-of-letters.spec.ts b/tests/sandbox/get-list-of-letters.spec.ts new file mode 100644 index 00000000..624a2227 --- /dev/null +++ b/tests/sandbox/get-list-of-letters.spec.ts @@ -0,0 +1,35 @@ +import { expect, test } from "@playwright/test"; +import { + SUPPLIER_API_URL_SANDBOX, + SUPPLIER_LETTERS, +} from "../constants/api-constants"; +import { apiSandboxGetLettersRequestTestData } from "./testCases/get-list-of-letters-test-cases"; + +test.describe("Sandbox Tests To Get List Of Pending Letters ", () => { + for (const { + expectedResponse, + expectedStatus, + header, + limit, + testCase, + } of apiSandboxGetLettersRequestTestData) { + test(`Get /Letters endpoint returns ${testCase}`, async ({ request }) => { + const response = await request.get( + `${SUPPLIER_API_URL_SANDBOX}/${SUPPLIER_LETTERS}`, + { + headers: header, + params: { + limit, + }, + }, + ); + + const res = await response.json(); + await expect(response.status()).toBe(expectedStatus); + expect(res).toEqual(expectedResponse); + if (response.status() === 200) { + expect(res.data.length.toString()).toEqual(limit); + } + }); + } +}); diff --git a/tests/sandbox/getLetterStatus.spec.ts b/tests/sandbox/getLetterStatus.spec.ts deleted file mode 100644 index 3d10845d..00000000 --- a/tests/sandbox/getLetterStatus.spec.ts +++ /dev/null @@ -1,21 +0,0 @@ -import { test, expect, request } from '@playwright/test'; -import { SUPPLIER_API_URL_SANDBOX, SUPPLIER_LETTERS} from '../constants/api_constants'; -import { apiSandboxGetLetterStatusTestData } from './testCases/getLetterStatus_testCases'; - - -test.describe('Sandbox Tests To Get Letter Status', () => -{ - apiSandboxGetLetterStatusTestData.forEach(({ testCase, header, id, expectedStatus, expectedResponse }) => { - test(`Get Letter Status endpoint returns ${testCase}`, async ({ request }) => { - - const response = await request.get(`${SUPPLIER_API_URL_SANDBOX}/${SUPPLIER_LETTERS}/${id}` ,{ - headers: header - }, - ); - - const res = await response.json(); - expect(res).toEqual(expectedResponse); - - }); - }); -}); diff --git a/tests/sandbox/getListOfLetters.spec.ts b/tests/sandbox/getListOfLetters.spec.ts deleted file mode 100644 index e2bca376..00000000 --- a/tests/sandbox/getListOfLetters.spec.ts +++ /dev/null @@ -1,27 +0,0 @@ -import { test, expect, request } from '@playwright/test'; -import { SUPPLIER_API_URL_SANDBOX, SUPPLIER_LETTERS} from '../constants/api_constants'; -import { apiSandboxGetLettersRequestTestData } from './testCases/getListOfLetters_testCases'; - - -test.describe('Sandbox Tests To Get List Of Pending Letters ', () => -{ - apiSandboxGetLettersRequestTestData.forEach(({ testCase, header, limit, expectedStatus, expectedResponse }) => { - test(`Get /Letters endpoint returns ${testCase}`, async ({ request }) => { - - const response = await request.get(`${SUPPLIER_API_URL_SANDBOX}/${SUPPLIER_LETTERS}` ,{ - headers: header, - params:{ - limit: limit - }, - }, - ); - - const res = await response.json(); - await expect(response.status()).toBe(expectedStatus); - expect(res).toEqual(expectedResponse); - if (response.status() === 200){ - expect(res.data.length.toString()).toEqual(limit); - } - }); - }); -}); diff --git a/tests/sandbox/testCases/get-letter-status-test-cases.ts b/tests/sandbox/testCases/get-letter-status-test-cases.ts new file mode 100644 index 00000000..36c9c327 --- /dev/null +++ b/tests/sandbox/testCases/get-letter-status-test-cases.ts @@ -0,0 +1,140 @@ +import { + RequestSandBoxHeaders, + sandBoxHeader, +} from "../../constants/request-headers"; +import { NoRequestIdHeaders } from "./get-list-of-letters-test-cases"; + +type ApiSandboxGetLetterStatusTestCase = { + testCase: string; + id: string; + header?: RequestSandBoxHeaders | NoRequestIdHeaders; + expectedStatus: number; + expectedResponse?: + | GetLetterStatusResponse + | GetLetterStatusErrorResponse + | GetRejectedLetterResponse; +}; + +export type GetLetterStatusResponse = { + data: GetLetterData; +}; + +export type GetRejectedLetterResponse = { + data: RejectedLetterData; +}; + +type GetLetterData = { + type: string; + id: string; + attributes: { + specificationId: string; + groupId: string; + status: string; + }; +}; + +type RejectedLetterData = { + type: string; + id: string; + attributes: { + specificationId: string; + groupId: string; + status: string; + reasonCode: string; + reasonText: string; + }; +}; + +export type GetLetterStatusErrorResponse = { + errors: ApiErrors[]; +}; + +type ApiErrors = { + code: string; + detail: string; + id: string; + links: { + about: string; + }; + status: string; + title: string; +}; + +export const apiSandboxGetLetterStatusTestData: ApiSandboxGetLetterStatusTestCase[] = + [ + { + testCase: "200 response and ACCEPTED record is fetched successfully", + id: "2AL5eYSWGzCHlGmzNxuqVusPxDg", + header: sandBoxHeader, + expectedStatus: 200, + expectedResponse: { + data: { + id: "2AL5eYSWGzCHlGmzNxuqVusPxDg", + type: "Letter", + attributes: { + specificationId: "2WL5eYSWGzCHlGmzNxuqVusPxDg", + groupId: "c5d93f917f5546d08beccf770a915d96", + status: "ACCEPTED", + }, + }, + }, + }, + { + testCase: "200 response and REJECTED record is fetched successfully", + id: "2WL5eYSWGzCHlGmzNxuqVusPxDg", + header: sandBoxHeader, + expectedStatus: 200, + expectedResponse: { + data: { + id: "2WL5eYSWGzCHlGmzNxuqVusPxDg", + type: "Letter", + attributes: { + specificationId: "2WL5eYSWGzCHlGmzNxuqVusPxDg", + groupId: "c5d93f917f5546d08beccf770a915d96", + status: "REJECTED", + reasonCode: "R01", + reasonText: "failed validation", + }, + }, + }, + }, + { + testCase: "200 response and CANCELLED record is fetched successfully", + id: "2XL5eYSWGzCHlGmzNxuqVusPxDg", + header: sandBoxHeader, + expectedStatus: 200, + expectedResponse: { + data: { + id: "2XL5eYSWGzCHlGmzNxuqVusPxDg", + type: "Letter", + attributes: { + specificationId: "2WL5eYSWGzCHlGmzNxuqVusPxDg", + groupId: "c5d93f917f5546d08beccf770a915d96", + status: "CANCELLED", + reasonCode: "R01", + }, + }, + }, + }, + { + testCase: "404 response when no record is found for the given id", + id: "24L5eYSWGzCHlGmzNxuqVusP", + header: sandBoxHeader, + expectedStatus: 200, + expectedResponse: { + errors: [ + { + status: "404", + title: "Resource not found", + code: "NOTIFY_RESOURCE_NOT_FOUND", + detail: "No resource found with that ID", + id: "rrt-1931948104716186917-c-geu2-10664-3111479-3.0", + links: { + about: + "https://digital.nhs.uk/developer/api-catalogue/nhs-notify-supplier", + }, + }, + ], + }, + }, + ]; diff --git a/tests/sandbox/testCases/get-list-of-letters-test-cases.ts b/tests/sandbox/testCases/get-list-of-letters-test-cases.ts new file mode 100644 index 00000000..b41534b3 --- /dev/null +++ b/tests/sandbox/testCases/get-list-of-letters-test-cases.ts @@ -0,0 +1,101 @@ +import { randomUUID } from "node:crypto"; +import { + RequestSandBoxHeaders, + sandBoxHeader, +} from "../../constants/request-headers"; + +type ApiSandboxGetLettersRequestTestCase = { + testCase: string; + limit: string; + header?: RequestSandBoxHeaders | NoRequestIdHeaders; + expectedStatus: number; + expectedResponse?: SandboxSuccessResponse | SandboxErrorResponse; +}; + +export type SandboxSuccessResponse = { + data: ApiData[]; +}; + +type ApiData = { + type: string; + id: string; + attributes: { + specificationId: string; + groupId: string; + status: string; + }; +}; + +export type SandboxErrorResponse = { + message: string; + errors: ApiErrors[]; +}; + +type ApiErrors = { + path: string; + message: string; + errorCode: string; +}; + +export type NoRequestIdHeaders = Omit; + +const NoRequestIdHeaders: NoRequestIdHeaders = { + "Content-Type": "application/vnd.api+json", + "X-Correlation-ID": randomUUID(), +}; + +export const apiSandboxGetLettersRequestTestData: ApiSandboxGetLettersRequestTestCase[] = + [ + { + testCase: "200 response if record is fetched successfully", + limit: "1", + header: sandBoxHeader, + expectedStatus: 200, + expectedResponse: { + data: [ + { + id: "fcfd849ceec940e8832b41f4fc161e09", + type: "Letter", + attributes: { + specificationId: "2WL5eYSWGzCHlGmzNxuqVusPxDg", + groupId: "c5d93f917f5546d08beccf770a915d96", + status: "PENDING", + }, + }, + ], + }, + }, + { + testCase: "400 response if invalid limit is passed", + limit: "XX", + header: sandBoxHeader, + expectedStatus: 400, + expectedResponse: { + message: "request.query.limit should be number", + errors: [ + { + path: ".query.limit", + message: "should be number", + errorCode: "type.openapi.validation", + }, + ], + }, + }, + + { + testCase: "400 response if invalid headers are passed", + limit: "2", + header: NoRequestIdHeaders, + expectedStatus: 400, + expectedResponse: { + message: "request.headers should have required property 'x-request-id'", + errors: [ + { + path: ".headers.x-request-id", + message: "should have required property 'x-request-id'", + errorCode: "required.openapi.validation", + }, + ], + }, + }, + ]; diff --git a/tests/sandbox/testCases/getLetterStatus_testCases.ts b/tests/sandbox/testCases/getLetterStatus_testCases.ts deleted file mode 100644 index 54333de1..00000000 --- a/tests/sandbox/testCases/getLetterStatus_testCases.ts +++ /dev/null @@ -1,137 +0,0 @@ -import { randomUUID } from "node:crypto"; -import { RequestSandBoxHeaders, sandBoxHeader} from "../../constants/request_headers"; -import { NoRequestIdHeaders, SandboxErrorResponse, SandboxSuccessResponse } from "./getListOfLetters_testCases"; - -type ApiSandboxGetLetterStatusTestCase = { - testCase: string; - id: string, - header?: RequestSandBoxHeaders | NoRequestIdHeaders; - expectedStatus: number; - expectedResponse?: GetLetterStatusResponse | GetLetterStatusErrorResponse | GetRejectedLetterResponse; -}; - -export type GetLetterStatusResponse = { - data: GetLetterData; -}; - -export type GetRejectedLetterResponse = { - data: RejectedLetterData; -}; - -type GetLetterData = -{ - type: string; - id: string; - attributes: { - specificationId: string; - groupId: string; - status: string; - } -}; - -type RejectedLetterData = -{ - type: string; - id: string; - attributes: { - specificationId: string; - groupId: string; - status: string; - reasonCode: number; - reasonText: string; - } -}; - -export type GetLetterStatusErrorResponse = { - errors: ApiErrors[]; -}; - -type ApiErrors = { - code: string; - detail: string; - id: string; - links:{ - about: string; - }, - status: string; - title: string; -}; - -export const apiSandboxGetLetterStatusTestData: ApiSandboxGetLetterStatusTestCase[] = [ -{ - testCase: '200 response and ACCEPTED record is fetched successfully', - id: '2AL5eYSWGzCHlGmzNxuqVusPxDg', - header: sandBoxHeader, - expectedStatus: 200, - expectedResponse: { - data: - { - id: '2AL5eYSWGzCHlGmzNxuqVusPxDg', - type: 'Letter', - attributes: { - specificationId: '2WL5eYSWGzCHlGmzNxuqVusPxDg', - groupId: 'c5d93f917f5546d08beccf770a915d96', - status: 'ACCEPTED', - }, - } - } -}, -{ - testCase: '200 response and REJECTED record is fetched successfully', - id: '2WL5eYSWGzCHlGmzNxuqVusPxDg', - header: sandBoxHeader, - expectedStatus: 200, - expectedResponse: { - data: - { - id: '2WL5eYSWGzCHlGmzNxuqVusPxDg', - type: 'Letter', - attributes: { - specificationId: '2WL5eYSWGzCHlGmzNxuqVusPxDg', - groupId: 'c5d93f917f5546d08beccf770a915d96', - status: 'REJECTED', - reasonCode: 100, - reasonText: "failed validation", - }, - } - } -}, -{ - testCase: '200 response and CANCELLED record is fetched successfully', - id: '2XL5eYSWGzCHlGmzNxuqVusPxDg', - header: sandBoxHeader, - expectedStatus: 200, - expectedResponse: { - data: - { - id: '2XL5eYSWGzCHlGmzNxuqVusPxDg', - type: 'Letter', - attributes: { - specificationId: '2WL5eYSWGzCHlGmzNxuqVusPxDg', - groupId: 'c5d93f917f5546d08beccf770a915d96', - status: 'CANCELLED', - reasonCode: 100 - }, - } - } -}, -{ - testCase: '404 response when no record is found for the given id', - id: '24L5eYSWGzCHlGmzNxuqVusP', - header: sandBoxHeader, - expectedStatus: 200, - expectedResponse: { - errors: [ - { - status: '404', - title: 'Resource not found', - code:'NOTIFY_RESOURCE_NOT_FOUND', - detail: 'No resource found with that ID', - id: 'rrt-1931948104716186917-c-geu2-10664-3111479-3.0', - links: { - about: 'https://digital.nhs.uk/developer/api-catalogue/nhs-notify-supplier' - } - } - ] - } -}]; diff --git a/tests/sandbox/testCases/getListOfLetters_testCases.ts b/tests/sandbox/testCases/getListOfLetters_testCases.ts deleted file mode 100644 index 422786d0..00000000 --- a/tests/sandbox/testCases/getListOfLetters_testCases.ts +++ /dev/null @@ -1,97 +0,0 @@ -import { RequestSandBoxHeaders, sandBoxHeader} from '../../constants/request_headers'; -import { randomUUID } from 'node:crypto'; - - -type ApiSandboxGetLettersRequestTestCase = { - testCase: string; - limit: string, - header?: RequestSandBoxHeaders | NoRequestIdHeaders; - expectedStatus: number; - expectedResponse?: SandboxSuccessResponse | SandboxErrorResponse; -}; - -export type SandboxSuccessResponse = { - data: ApiData []; -}; - -type ApiData = -{ - type: string; - id: string; - attributes: { - specificationId: string; - groupId: string; - status: string; - } -}; - -export type SandboxErrorResponse = { - message: string; - errors: ApiErrors[]; -}; - -type ApiErrors = { - path: string; - message: string; - errorCode: string; -}; - -export type NoRequestIdHeaders = Omit; - -const NoRequestIdHeaders: NoRequestIdHeaders = { - 'Content-Type': 'application/vnd.api+json', - 'X-Correlation-ID': randomUUID(), -}; - -export const apiSandboxGetLettersRequestTestData: ApiSandboxGetLettersRequestTestCase[] = [ -{ - testCase: '200 response if record is fetched successfully', - limit: '1', - header: sandBoxHeader, - expectedStatus: 200, - expectedResponse: { - data: [ - { - id: 'fcfd849ceec940e8832b41f4fc161e09', - type: 'Letter', - attributes: { - specificationId: '2WL5eYSWGzCHlGmzNxuqVusPxDg', - groupId: 'c5d93f917f5546d08beccf770a915d96', - status: 'PENDING', - }, - }] - }, -}, -{ - testCase: '400 response if invalid limit is passed', - limit: 'XX', - header: sandBoxHeader, - expectedStatus: 400, - expectedResponse: { - message: 'request.query.limit should be number', - errors: [ - { - path:'.query.limit', - message:'should be number', - errorCode:'type.openapi.validation' - } - ] - } -}, - -{ - testCase: '400 response if invalid headers are passed', - limit: '2', - header: NoRequestIdHeaders, - expectedStatus: 400, - expectedResponse: { - message: "request.headers should have required property 'x-request-id'", - errors: [ - { - path: '.headers.x-request-id', - message: "should have required property 'x-request-id'", - errorCode:'required.openapi.validation' - } - ] - } -}]; diff --git a/tests/sandbox/testCases/update-letter-status-test-cases.ts b/tests/sandbox/testCases/update-letter-status-test-cases.ts new file mode 100644 index 00000000..061e30b8 --- /dev/null +++ b/tests/sandbox/testCases/update-letter-status-test-cases.ts @@ -0,0 +1,116 @@ +import { + PatchMessageRequestBody, + PatchMessageResponseBody, +} from "../../component-tests/apiGateway-tests/testCases/update-letter-status"; +import { + RequestSandBoxHeaders, + sandBoxHeader, +} from "../../constants/request-headers"; +import { ErrorMessageBody } from "../../helpers/common-types"; +import { SandboxErrorResponse } from "./get-list-of-letters-test-cases"; + +export type ApiSandboxUpdateLetterStatusTestData = { + testCase: string; + id: string; + header: RequestSandBoxHeaders; + body?: PatchMessageRequestBody; + expectedStatus: number; + expectedResponse?: + | PatchMessageResponseBody + | SandboxErrorResponse + | ErrorMessageBody; +}; + +export const apiSandboxUpdateLetterStatusTestData: ApiSandboxUpdateLetterStatusTestData[] = + [ + { + testCase: "202 response if record is updated with status PENDING", + id: "24L5eYSWGzCHlGmzNxuqVusPxDg", + header: sandBoxHeader, + body: { + data: { + type: "Letter", + id: "2WL5eYSWGzCHlGmzNxuqVusPxDg", + attributes: { + status: "PENDING", + }, + }, + }, + expectedStatus: 202, + }, + + { + testCase: "202 response if record is updated with status REJECTED", + id: "24L5eYSWGzCHlGmzNxuqVusPxDg", + header: sandBoxHeader, + body: { + data: { + type: "Letter", + id: "2WL5eYSWGzCHlGmzNxuqVusPxDg", + attributes: { + status: "REJECTED", + reasonCode: "R07", + reasonText: "No such address", + }, + }, + }, + expectedStatus: 202, + }, + { + testCase: "404 response if no resource is found for the given id", + id: "0", + header: sandBoxHeader, + body: { + data: { + type: "Letter", + id: "0", + attributes: { + status: "PENDING", + }, + }, + }, + expectedStatus: 404, + expectedResponse: { + errors: [ + { + id: "rrt-1931948104716186917-c-geu2-10664-3111479-3.0", + code: "NOTIFY_RESOURCE_NOT_FOUND", + links: { + about: + "https://digital.nhs.uk/developer/api-catalogue/nhs-notify-supplier", + }, + status: "404", + title: "Resource not found", + detail: "No resource found with that ID", + }, + ], + }, + }, + { + testCase: "400 response if request body is invalid", + id: "2WL5eYSWGzCHlGmzNxuqVusPxDg", + header: sandBoxHeader, + body: { + data: { + type: "Letter", + id: "2WL5eYSWGzCHlGmzNxuqVusPxDg", + attributes: { + status: "NO_STATUS", + }, + }, + }, + expectedStatus: 400, + expectedResponse: { + message: + "request.body.data.attributes.status should be equal to one of the allowed values: PENDING, ACCEPTED, REJECTED, PRINTED, ENCLOSED, CANCELLED, DISPATCHED, DELIVERED, FAILED, RETURNED, FORWARDED", + errors: [ + { + path: ".body.data.attributes.status", + message: + "should be equal to one of the allowed values: PENDING, ACCEPTED, REJECTED, PRINTED, ENCLOSED, CANCELLED, DISPATCHED, DELIVERED, FAILED, RETURNED, FORWARDED", + errorCode: "enum.openapi.validation", + }, + ], + }, + }, + ]; diff --git a/tests/sandbox/testCases/update-multiple-status-test-cases.ts b/tests/sandbox/testCases/update-multiple-status-test-cases.ts new file mode 100644 index 00000000..06b8716f --- /dev/null +++ b/tests/sandbox/testCases/update-multiple-status-test-cases.ts @@ -0,0 +1,132 @@ +import { + RequestSandBoxHeaders, + sandBoxHeader, +} from "../../constants/request-headers"; + +export type ApiSandboxUpdateLetterStatusTestData = { + testCase: string; + header: RequestSandBoxHeaders; + body: PostMessageRequestBody; + expectedStatus: number; +}; + +type PostMessageRequestBody = { + data: postRequest[]; +}; + +type postRequest = { + type: string; + id: string; + attributes: { + reasonCode?: string; + reasonText?: string; + status: string; + }; +}; + +export const apiSandboxMultipleLetterStatusTestData: ApiSandboxUpdateLetterStatusTestData[] = + [ + { + testCase: "200 response if records are updated", + header: sandBoxHeader, + body: { + data: [ + { + attributes: { + status: "PENDING", + }, + id: "2WL5eYSWGzCHlGmzNxuqVusPxDg", + type: "Letter", + }, + { + attributes: { + status: "ACCEPTED", + }, + id: "2WL5eYSWGzCHlGmzNxuqVusPxDg", + type: "Letter", + }, + { + attributes: { + status: "PRINTED", + }, + id: "2WL5eYSWGzCHlGmzNxuqVusPxDg", + type: "Letter", + }, + { + attributes: { + status: "ENCLOSED", + }, + id: "2WL5eYSWGzCHlGmzNxuqVusPxDg", + type: "Letter", + }, + { + attributes: { + status: "DISPATCHED", + }, + id: "2WL5eYSWGzCHlGmzNxuqVusPxDg", + type: "Letter", + }, + { + attributes: { + status: "DELIVERED", + }, + id: "2WL5eYSWGzCHlGmzNxuqVusPxDg", + type: "Letter", + }, + { + attributes: { + reasonCode: "R01", + reasonText: "failed validation", + status: "RETURNED", + }, + id: "2WL5eYSWGzCHlGmzNxuqVusPxDg", + type: "Letter", + }, + { + attributes: { + reasonCode: "R01", + reasonText: "failed validation", + status: "CANCELLED", + }, + id: "2WL5eYSWGzCHlGmzNxuqVusPxDg", + type: "Letter", + }, + { + attributes: { + reasonCode: "R01", + reasonText: "failed validation", + status: "FAILED", + }, + id: "2WL5eYSWGzCHlGmzNxuqVusPxDg", + type: "Letter", + }, + { + attributes: { + reasonCode: "R01", + reasonText: "failed validation", + status: "RETURNED", + }, + id: "2WL5eYSWGzCHlGmzNxuqVusPxDg", + type: "Letter", + }, + ], + }, + expectedStatus: 200, + }, + { + testCase: "404 response if invalid request is passed", + header: sandBoxHeader, + body: { + data: [ + { + attributes: { + status: "PENDING", + }, + id: "1234", + type: "Letter", + }, + ], + }, + expectedStatus: 404, + }, + ]; diff --git a/tests/sandbox/testCases/updateLetterStatus_testCases.ts b/tests/sandbox/testCases/updateLetterStatus_testCases.ts deleted file mode 100644 index 35765676..00000000 --- a/tests/sandbox/testCases/updateLetterStatus_testCases.ts +++ /dev/null @@ -1,123 +0,0 @@ - -import { PatchMessageRequestBody, PatchMessageResponseBody } from '../../component-tests/apiGateway-tests/testCases/UpdateLetterStatus'; -import { RequestSandBoxHeaders, sandBoxHeader } from '../../constants/request_headers'; -import { ErrorMessageBody } from '../../helpers/commonTypes'; -import { SandboxErrorResponse } from './getListOfLetters_testCases'; - - -export type ApiSandboxUpdateLetterStatusTestData = { - testCase: string; - id: string, - header: RequestSandBoxHeaders; - body?: PatchMessageRequestBody; - expectedStatus: number; - expectedResponse?: PatchMessageResponseBody | SandboxErrorResponse | ErrorMessageBody; -}; - -export const apiSandboxUpdateLetterStatusTestData: ApiSandboxUpdateLetterStatusTestData[] = [ - { - testCase: '200 response if record is updated with status PENDING', - id: '2WL5eYSWGzCHlGmzNxuqVusPxDg', - header: sandBoxHeader, - body: { - data: { - type: 'Letter', - id: '2WL5eYSWGzCHlGmzNxuqVusPxDg', - attributes: { - status: 'PENDING', - }, - } - }, - expectedStatus: 200, - expectedResponse: { - data: { - type: 'Letter', - id: '2WL5eYSWGzCHlGmzNxuqVusPxDg', - attributes: { - status: 'PENDING', - specificationId:'2WL5eYSWGzCHlGmzNxuqVusPxDg', - }, - } - }, - }, - - { - testCase: '200 response if record is updated with status REJECTED', - id: '2WL5eYSWGzCHlGmzNxuqVusPxDg', - header: sandBoxHeader, - body: { - data: { - type: 'Letter', - id: '2WL5eYSWGzCHlGmzNxuqVusPxDg', - attributes: { - status: 'REJECTED', - reasonCode: 100, - reasonText: 'failed validation', - }, - } - }, - expectedStatus: 200, - expectedResponse: { - data: { - type: 'Letter', - id: '2WL5eYSWGzCHlGmzNxuqVusPxDg', - attributes: { - reasonCode: 100, - reasonText: 'failed validation', - status: 'REJECTED', - specificationId:'2WL5eYSWGzCHlGmzNxuqVusPxDg', - }, - } - }, - }, - { - testCase: '404 response if no resource is found for the given id', - id: '0', - header: sandBoxHeader, - body: { - data: { - type: 'Letter', - id: '0', - attributes: { - status: 'PENDING', - }, - } - }, - expectedStatus: 404, - expectedResponse: { - errors: [{ - id: 'rrt-1931948104716186917-c-geu2-10664-3111479-3.0', - code: 'NOTIFY_RESOURCE_NOT_FOUND', - links: { - about: "https://digital.nhs.uk/developer/api-catalogue/nhs-notify-supplier" - }, - status: '404', - title: 'Resource not found', - detail: 'No resource found with that ID' - }] - }, - }, - { - testCase: '400 response if request body is invalid', - id: '2WL5eYSWGzCHlGmzNxuqVusPxDg', - header: sandBoxHeader, - body: { - data: { - type: 'Letter', - id: '2WL5eYSWGzCHlGmzNxuqVusPxDg', - attributes: { - status: 'NO_STATUS', - }, - } - }, - expectedStatus: 400, - expectedResponse: { - message: 'request.body.data.attributes.status should be equal to one of the allowed values: PENDING, ACCEPTED, REJECTED, PRINTED, ENCLOSED, CANCELLED, DISPATCHED, DELIVERED, FAILED, RETURNED, DESTROYED, FORWARDED', - errors: [{ - path: '.body.data.attributes.status', - message: 'should be equal to one of the allowed values: PENDING, ACCEPTED, REJECTED, PRINTED, ENCLOSED, CANCELLED, DISPATCHED, DELIVERED, FAILED, RETURNED, DESTROYED, FORWARDED', - errorCode: 'enum.openapi.validation' - }] - }, - }, -]; diff --git a/tests/sandbox/testCases/updateMultipleStatus_testCases.ts b/tests/sandbox/testCases/updateMultipleStatus_testCases.ts deleted file mode 100644 index f94a17e7..00000000 --- a/tests/sandbox/testCases/updateMultipleStatus_testCases.ts +++ /dev/null @@ -1,125 +0,0 @@ -import { RequestSandBoxHeaders, sandBoxHeader } from "../../constants/request_headers"; - -export type ApiSandboxUpdateLetterStatusTestData = { - testCase: string; - header: RequestSandBoxHeaders; - body: PostMessageRequestBody; - expectedStatus: number; -}; - -type PostMessageRequestBody = { - data: postRequest [] -} - -type postRequest = { - type: string; - id: string; - attributes: { - reasonCode?: string | number; - reasonText?: string; - status: string; - } -}; - -export const apiSandboxMultipleLetterStatusTestData: ApiSandboxUpdateLetterStatusTestData[] = -[{ - testCase: '200 response if records are updated', - header: sandBoxHeader, - body:{ - data : - [{ - attributes: { - status: 'PENDING' - }, - id: '2WL5eYSWGzCHlGmzNxuqVusPxDg', - type: 'Letter' - }, - { - attributes: { - 'status': 'ACCEPTED' - }, - id: '2WL5eYSWGzCHlGmzNxuqVusPxDg', - type: 'Letter' - }, - { - attributes: { - status: 'PRINTED' - }, - id: '2WL5eYSWGzCHlGmzNxuqVusPxDg', - type: 'Letter' - }, - { - attributes: { - status: 'ENCLOSED' - }, - id: '2WL5eYSWGzCHlGmzNxuqVusPxDg', - type: 'Letter' - }, - { - attributes: { - status: 'DISPATCHED' - }, - id: '2WL5eYSWGzCHlGmzNxuqVusPxDg', - type: 'Letter' - }, - { - attributes: { - status: 'DELIVERED' - }, - id: '2WL5eYSWGzCHlGmzNxuqVusPxDg', - type: 'Letter' - }, - { - attributes: { - reasonCode: 100, - reasonText: 'failed validation', - status: 'RETURNED' - }, - id: '2WL5eYSWGzCHlGmzNxuqVusPxDg', - type: 'Letter' - }, - { - attributes: { - reasonCode: 100, - reasonText: 'failed validation', - status: 'CANCELLED' - }, - id: '2WL5eYSWGzCHlGmzNxuqVusPxDg', - type: 'Letter' - }, - { - attributes: { - reasonCode: 100, - reasonText: 'failed validation', - status: 'FAILED' - }, - id: '2WL5eYSWGzCHlGmzNxuqVusPxDg', - type: 'Letter' - }, - { - attributes: { - reasonCode: 100, - reasonText: 'failed validation', - status: 'RETURNED' - }, - id: '2WL5eYSWGzCHlGmzNxuqVusPxDg', - type: 'Letter' - } - ]}, - expectedStatus: 200 -}, -{ - testCase: '404 response if invalid request is passed', - header: sandBoxHeader, - body:{ - data : - [{ - attributes: { - status: 'PENDING' - }, - id: '1234', - type: 'Letter' - }] - }, - expectedStatus:404, -}]; diff --git a/tests/sandbox/update-letter-status.spec.ts b/tests/sandbox/update-letter-status.spec.ts new file mode 100644 index 00000000..dcd724fd --- /dev/null +++ b/tests/sandbox/update-letter-status.spec.ts @@ -0,0 +1,31 @@ +import { expect, test } from "@playwright/test"; +import { + SUPPLIER_API_URL_SANDBOX, + SUPPLIER_LETTERS, +} from "../constants/api-constants"; +import { apiSandboxUpdateLetterStatusTestData } from "./testCases/update-letter-status-test-cases"; + +test.describe("Sandbox Tests To Update Letter Status", () => { + for (const { + body, + expectedResponse, + expectedStatus, + header, + id, + testCase, + } of apiSandboxUpdateLetterStatusTestData) { + test(`Patch /Letters endpoint returns ${testCase}`, async ({ request }) => { + const response = await request.patch( + `${SUPPLIER_API_URL_SANDBOX}/${SUPPLIER_LETTERS}/${id}`, + { + headers: header, + data: body, + }, + ); + + const res = await response.json(); + expect(response.status()).toBe(expectedStatus); + expect(res).toEqual(expectedResponse); + }); + } +}); diff --git a/tests/sandbox/update-multiple-letter-status.spec.ts b/tests/sandbox/update-multiple-letter-status.spec.ts new file mode 100644 index 00000000..f7e30198 --- /dev/null +++ b/tests/sandbox/update-multiple-letter-status.spec.ts @@ -0,0 +1,26 @@ +import { expect, test } from "@playwright/test"; +import { + SUPPLIER_API_URL_SANDBOX, + SUPPLIER_LETTERS, +} from "../constants/api-constants"; +import { apiSandboxMultipleLetterStatusTestData } from "./testCases/update-multiple-status-test-cases"; + +test.describe("Sandbox Tests To Update Multiple Letter Status", () => { + for (const { + body, + expectedStatus, + header, + testCase, + } of apiSandboxMultipleLetterStatusTestData) { + test(`Patch /Letters endpoint returns ${testCase}`, async ({ request }) => { + const response = await request.post( + `${SUPPLIER_API_URL_SANDBOX}/${SUPPLIER_LETTERS}`, + { + headers: header, + data: body, + }, + ); + expect(response.status()).toBe(expectedStatus); + }); + } +}); diff --git a/tests/sandbox/updateLetterStatus.spec.ts b/tests/sandbox/updateLetterStatus.spec.ts deleted file mode 100644 index 9e0d9a4d..00000000 --- a/tests/sandbox/updateLetterStatus.spec.ts +++ /dev/null @@ -1,22 +0,0 @@ -import { test, expect, request } from '@playwright/test'; -import { SUPPLIER_API_URL_SANDBOX, SUPPLIER_LETTERS} from '../constants/api_constants'; -import { apiSandboxUpdateLetterStatusTestData } from './testCases/updateLetterStatus_testCases'; - - -test.describe('Sandbox Tests To Update Letter Status', () => -{ - apiSandboxUpdateLetterStatusTestData.forEach(({ testCase, header, id, body, expectedStatus, expectedResponse }) => { - test(`Patch /Letters endpoint returns ${testCase}`, async ({ request }) => { - - const response = await request.patch(`${SUPPLIER_API_URL_SANDBOX}/${SUPPLIER_LETTERS}/${id}` ,{ - headers: header, - data: body - }); - - const res = await response.json(); - expect(response.status()).toBe(expectedStatus); - expect(res).toEqual(expectedResponse); - - }); - }); -}); diff --git a/tests/sandbox/updateMultipleLetterStatus.spec.ts b/tests/sandbox/updateMultipleLetterStatus.spec.ts deleted file mode 100644 index 524229bf..00000000 --- a/tests/sandbox/updateMultipleLetterStatus.spec.ts +++ /dev/null @@ -1,18 +0,0 @@ -import { test, expect, request } from '@playwright/test'; -import { SUPPLIER_API_URL_SANDBOX, SUPPLIER_LETTERS} from '../constants/api_constants'; -import { apiSandboxMultipleLetterStatusTestData } from './testCases/updateMultipleStatus_testCases'; - - -test.describe('Sandbox Tests To Update Multiple Letter Status', () => -{ - apiSandboxMultipleLetterStatusTestData.forEach(({ testCase, header, body, expectedStatus }) => { - test(`Patch /Letters endpoint returns ${testCase}`, async ({ request }) => { - - const response = await request.post(`${SUPPLIER_API_URL_SANDBOX}/${SUPPLIER_LETTERS}` ,{ - headers: header, - data: body - }); - expect(response.status()).toBe(expectedStatus); - }); - }); -}); diff --git a/tests/tsconfig.json b/tests/tsconfig.json new file mode 100644 index 00000000..81891186 --- /dev/null +++ b/tests/tsconfig.json @@ -0,0 +1,6 @@ +{ + "compilerOptions": { + "resolveJsonModule": true + }, + "extends": "../tsconfig.base.json" +}