diff --git a/.github/actions/install-playwright/action.yml b/.github/actions/install-playwright/action.yml new file mode 100644 index 00000000..30b33b91 --- /dev/null +++ b/.github/actions/install-playwright/action.yml @@ -0,0 +1,37 @@ +name: 'Install playwright' +description: 'Install Playwright browser binaries and OS dependencies' +runs: + using: 'composite' + steps: + - name: Get installed Playwright version + id: playwright-version + shell: bash + working-directory: ./packages/integration-tests + run: | + PLAYWRIGHT_VERSION=$(npm ls --json "@playwright/test" | jq --raw-output '.dependencies["@playwright/test"].version') + echo "PLAYWRIGHT_VERSION=$PLAYWRIGHT_VERSION" >> $GITHUB_ENV + + - name: Set up path for Playwright cache + shell: bash + run: echo "PLAYWRIGHT_CACHE_PATH=$(if [[ $RUNNER_OS == 'macOS' ]]; then echo '~/Library/Caches/ms-playwright'; else echo '~/.cache/ms-playwright'; fi)" >> $GITHUB_ENV + + - name: Cache Playwright binaries + uses: actions/cache@0c45773b623bea8c8e75f6c82b208c3cf94ea4f9 + id: playwright-cache + with: + path: ${{ env.PLAYWRIGHT_CACHE_PATH }} + key: '${{ runner.os }}-playwright-cache-${{ env.PLAYWRIGHT_VERSION }}-splunk-otel-js-web-artifacts' + + - name: Install Playwright browser binaries + if: steps.playwright-cache.outputs.cache-hit != 'true' + shell: bash + run: | + npx playwright install --with-deps + npx playwright install chrome + npx playwright install msedge + npx playwright install webkit + + - name: Install Playwright OS dependencies + if: steps.playwright-cache.outputs.cache-hit != 'true' + shell: bash + run: npx playwright install-deps diff --git a/.github/workflows/ci-main.yml b/.github/workflows/ci-main.yml index a7fa6831..48baca63 100644 --- a/.github/workflows/ci-main.yml +++ b/.github/workflows/ci-main.yml @@ -38,3 +38,7 @@ jobs: uses: ./.github/actions/setup - name: Local integration tests using ${{ matrix.browser }} run: npm run test:integration:local:${{ matrix.browser }}:_execute + + playwright: + uses: ./.github/workflows/tests.yml + secrets: inherit diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index beaa0aed..5ff32eac 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -26,59 +26,6 @@ jobs: - name: Unit test (nodejs) run: npm run test:unit:ci-node - saucelabs-integration-tests: - runs-on: ubuntu-latest - permissions: read-all - environment: integration - env: - SAUCE_USERNAME: ${{ secrets.SAUCE_USERNAME }} - SAUCE_ACCESS_KEY: ${{ secrets.SAUCE_ACCESS_KEY }} - SAUCE_TUNNEL_ID: ${{ secrets.SAUCE_TUNNEL_ID }} - strategy: - fail-fast: false - matrix: - browser: [edge] - steps: - - name: Setup sauce connect - uses: saucelabs/sauce-connect-action@v2.3.4 - with: - username: ${{ secrets.SAUCE_USERNAME }} - accessKey: ${{ secrets.SAUCE_ACCESS_KEY }} - tunnelName: ${{ secrets.SAUCE_TUNNEL_ID }} - - name: Checkout - uses: actions/checkout@v4.2.2 - - - name: Setup - uses: ./.github/actions/setup - - - name: Add to hosts file - run: echo "127.0.0.1 local.test" | sudo tee -a /etc/hosts - - name: Run integration tests in Saucelabs - run: npm run test:integration:remote -- --env ${{ matrix.browser }} - timeout-minutes: 15 - - local-integration-tests: - runs-on: ubuntu-latest - permissions: read-all - strategy: - fail-fast: false - matrix: - browser: [headlessChrome, headlessFirefox] - steps: - - name: Checkout - uses: actions/checkout@v4.2.2 - - - name: Setup - uses: ./.github/actions/setup - - - name: Local integration tests using ${{ matrix.browser }} - run: npm run test:integration:local:${{ matrix.browser }}:_execute - - - name: Upload test logs - uses: actions/upload-artifact@v3.1.2 - if: ${{ failure() }} - with: - name: test-results ${{ matrix.browser }} - path: | - packages/web/tests_output/ - packages/web/logs/ + playwright: + uses: ./.github/workflows/tests.yml + secrets: inherit diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml new file mode 100644 index 00000000..a3bc3909 --- /dev/null +++ b/.github/workflows/tests.yml @@ -0,0 +1,118 @@ +name: Playwright tests +on: + workflow_call: + +jobs: + playwright: + timeout-minutes: 60 + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + browser: [chromium, firefox, chrome, edge] + name: Playwright Tests - ${{ matrix.browser }} + steps: + - name: Checkout + uses: actions/checkout@v4.2.1 + - uses: actions/setup-node@v4.0.3 + with: + node-version: '18' + cache: 'npm' + - run: npm ci + - run: npm run compile + + - name: Install playwright + uses: ./.github/actions/install-playwright + + - name: Run Playwright tests + run: | + npx playwright test --project=${{ matrix.browser }} + working-directory: ./packages/integration-tests + + - name: Upload blob report to GitHub Actions Artifacts + if: ${{ !cancelled() }} + uses: actions/upload-artifact@834a144ee995460fba8ed112a2fc961b36a5ec5a + with: + name: blob-report-${{ matrix.browser }}-attempt-${{ github.run_attempt }} + path: packages/integration-tests/blob-report + retention-days: 1 + + playwright-macos: + timeout-minutes: 60 + runs-on: macos-latest + strategy: + fail-fast: false + matrix: + browser: [webkit] + steps: + - name: Checkout + uses: actions/checkout@v4.2.1 + - uses: actions/setup-node@v4.0.3 + with: + node-version: '18' + cache: 'npm' + - run: npm ci + - run: npm run compile + + - name: Install playwright + uses: ./.github/actions/install-playwright + + - name: Run Playwright tests + run: | + npx playwright test --project=${{ matrix.browser }} + working-directory: ./packages/integration-tests + + - name: Upload blob report to GitHub Actions Artifacts + if: ${{ !cancelled() }} + uses: actions/upload-artifact@834a144ee995460fba8ed112a2fc961b36a5ec5a + with: + name: blob-report-${{ matrix.browser }}-attempt-${{ github.run_attempt }} + path: packages/integration-tests/blob-report + retention-days: 1 + + merge-reports: + if: ${{ !cancelled() }} + needs: [playwright, playwright-macos] + + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v4.2.1 + - uses: actions/setup-node@v4.0.3 + with: + node-version: '18' + cache: 'npm' + - run: npm ci + - run: npm run compile + + - name: Download blob reports from GitHub Actions Artifacts + uses: actions/download-artifact@fa0a91b85d4f404e444e00e005971372dc801d16 + with: + path: packages/integration-tests/all-blob-reports + pattern: blob-report-* + merge-multiple: true + + - name: Merge into HTML Report + run: npx playwright merge-reports --reporter html ./all-blob-reports -c playwright.config.ts + working-directory: ./packages/integration-tests + + - name: Upload HTML report + uses: actions/upload-artifact@834a144ee995460fba8ed112a2fc961b36a5ec5a + with: + name: playwright-report--attempt-${{ github.run_attempt }} + path: packages/integration-tests/playwright-report + retention-days: 14 + + check-failure: + needs: [playwright, playwright-macos, merge-reports] + runs-on: ubuntu-latest + if: ${{ always() }} + steps: + - name: Check if any playwright tests failed + run: | + if [ "${{ needs.playwright.result }}" != "success" ] || [ "${{ needs['playwright-macos'].result }}" != "success" ]; then + echo "One or more tests failed." + exit 1 + else + echo "All tests passed." + fi diff --git a/package-lock.json b/package-lock.json index 5c5ca6d4..5dea2b10 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,12 +9,14 @@ "version": "0.20.0-beta.0", "workspaces": [ "packages/web", - "packages/session-recorder" + "packages/session-recorder", + "packages/integration-tests" ], "devDependencies": { "@aws-sdk/client-cloudfront": "^3.171.0", "@aws-sdk/client-s3": "^3.171.0", "@eslint/js": "^9.13.0", + "@fastify/cors": "^10.0.1", "@octokit/graphql": "^7.0.1", "@octokit/request": "^8.1.1", "@size-limit/file": "^11.1.6", @@ -27,6 +29,7 @@ "eslint-config-prettier": "^9.1.0", "eslint-plugin-headers": "^1.2.0", "eslint-plugin-prettier": "^5.2.1", + "fastify": "^5.1.0", "globals": "^15.11.0", "prettier": "^3.3.3", "size-limit": "^11.1.6", @@ -2909,14 +2912,388 @@ "@jridgewell/sourcemap-codec": "^1.4.10" } }, - "node_modules/@eggjs/yauzl": { - "version": "2.11.0", - "resolved": "https://registry.npmjs.org/@eggjs/yauzl/-/yauzl-2.11.0.tgz", - "integrity": "sha512-Jq+k2fCZJ3i3HShb0nxLUiAgq5pwo8JTT1TrH22JoehZQ0Nm2dvByGIja1NYfNyuE4Tx5/Dns5nVsBN/mlC8yg==", - "dev": true, - "dependencies": { - "buffer-crc32": "~0.2.3", - "fd-slicer2": "^1.2.0" + "node_modules/@esbuild/aix-ppc64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.23.1.tgz", + "integrity": "sha512-6VhYk1diRqrhBAqpJEdjASR/+WVRtfjpqKuNw11cLiaWpAT/Uu+nokB+UJnevzy/P9C/ty6AOe0dwueMrGh/iQ==", + "cpu": [ + "ppc64" + ], + "license": "MIT", + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.23.1.tgz", + "integrity": "sha512-uz6/tEy2IFm9RYOyvKl88zdzZfwEfKZmnX9Cj1BHjeSGNuGLuMD1kR8y5bteYmwqKm1tj8m4cb/aKEorr6fHWQ==", + "cpu": [ + "arm" + ], + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.23.1.tgz", + "integrity": "sha512-xw50ipykXcLstLeWH7WRdQuysJqejuAGPd30vd1i5zSyKK3WE+ijzHmLKxdiCMtH1pHz78rOg0BKSYOSB/2Khw==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.23.1.tgz", + "integrity": "sha512-nlN9B69St9BwUoB+jkyU090bru8L0NA3yFvAd7k8dNsVH8bi9a8cUAUSEcEEgTp2z3dbEDGJGfP6VUnkQnlReg==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.23.1.tgz", + "integrity": "sha512-YsS2e3Wtgnw7Wq53XXBLcV6JhRsEq8hkfg91ESVadIrzr9wO6jJDMZnCQbHm1Guc5t/CdDiFSSfWP58FNuvT3Q==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.23.1.tgz", + "integrity": "sha512-aClqdgTDVPSEGgoCS8QDG37Gu8yc9lTHNAQlsztQ6ENetKEO//b8y31MMu2ZaPbn4kVsIABzVLXYLhCGekGDqw==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.23.1.tgz", + "integrity": "sha512-h1k6yS8/pN/NHlMl5+v4XPfikhJulk4G+tKGFIOwURBSFzE8bixw1ebjluLOjfwtLqY0kewfjLSrO6tN2MgIhA==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.23.1.tgz", + "integrity": "sha512-lK1eJeyk1ZX8UklqFd/3A60UuZ/6UVfGT2LuGo3Wp4/z7eRTRYY+0xOu2kpClP+vMTi9wKOfXi2vjUpO1Ro76g==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.23.1.tgz", + "integrity": "sha512-CXXkzgn+dXAPs3WBwE+Kvnrf4WECwBdfjfeYHpMeVxWE0EceB6vhWGShs6wi0IYEqMSIzdOF1XjQ/Mkm5d7ZdQ==", + "cpu": [ + "arm" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.23.1.tgz", + "integrity": "sha512-/93bf2yxencYDnItMYV/v116zff6UyTjo4EtEQjUBeGiVpMmffDNUyD9UN2zV+V3LRV3/on4xdZ26NKzn6754g==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.23.1.tgz", + "integrity": "sha512-VTN4EuOHwXEkXzX5nTvVY4s7E/Krz7COC8xkftbbKRYAl96vPiUssGkeMELQMOnLOJ8k3BY1+ZY52tttZnHcXQ==", + "cpu": [ + "ia32" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.23.1.tgz", + "integrity": "sha512-Vx09LzEoBa5zDnieH8LSMRToj7ir/Jeq0Gu6qJ/1GcBq9GkfoEAoXvLiW1U9J1qE/Y/Oyaq33w5p2ZWrNNHNEw==", + "cpu": [ + "loong64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.23.1.tgz", + "integrity": "sha512-nrFzzMQ7W4WRLNUOU5dlWAqa6yVeI0P78WKGUo7lg2HShq/yx+UYkeNSE0SSfSure0SqgnsxPvmAUu/vu0E+3Q==", + "cpu": [ + "mips64el" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.23.1.tgz", + "integrity": "sha512-dKN8fgVqd0vUIjxuJI6P/9SSSe/mB9rvA98CSH2sJnlZ/OCZWO1DJvxj8jvKTfYUdGfcq2dDxoKaC6bHuTlgcw==", + "cpu": [ + "ppc64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.23.1.tgz", + "integrity": "sha512-5AV4Pzp80fhHL83JM6LoA6pTQVWgB1HovMBsLQ9OZWLDqVY8MVobBXNSmAJi//Csh6tcY7e7Lny2Hg1tElMjIA==", + "cpu": [ + "riscv64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.23.1.tgz", + "integrity": "sha512-9ygs73tuFCe6f6m/Tb+9LtYxWR4c9yg7zjt2cYkjDbDpV/xVn+68cQxMXCjUpYwEkze2RcU/rMnfIXNRFmSoDw==", + "cpu": [ + "s390x" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.23.1.tgz", + "integrity": "sha512-EV6+ovTsEXCPAp58g2dD68LxoP/wK5pRvgy0J/HxPGB009omFPv3Yet0HiaqvrIrgPTBuC6wCH1LTOY91EO5hQ==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.23.1.tgz", + "integrity": "sha512-aevEkCNu7KlPRpYLjwmdcuNz6bDFiE7Z8XC4CPqExjTvrHugh28QzUXVOZtiYghciKUacNktqxdpymplil1beA==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-arm64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.23.1.tgz", + "integrity": "sha512-3x37szhLexNA4bXhLrCC/LImN/YtWis6WXr1VESlfVtVeoFJBRINPJ3f0a/6LV8zpikqoUg4hyXw0sFBt5Cr+Q==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.23.1.tgz", + "integrity": "sha512-aY2gMmKmPhxfU+0EdnN+XNtGbjfQgwZj43k8G3fyrDM/UdZww6xrWxmDkuz2eCZchqVeABjV5BpildOrUbBTqA==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.23.1.tgz", + "integrity": "sha512-RBRT2gqEl0IKQABT4XTj78tpk9v7ehp+mazn2HbUeZl1YMdaGAQqhapjGTCe7uw7y0frDi4gS0uHzhvpFuI1sA==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.23.1.tgz", + "integrity": "sha512-4O+gPR5rEBe2FpKOVyiJ7wNDPA8nGzDuJ6gN4okSA1gEOYZ67N8JPk58tkWtdtPeLz7lBnY6I5L3jdsr3S+A6A==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.23.1.tgz", + "integrity": "sha512-BcaL0Vn6QwCwre3Y717nVHZbAa4UBEigzFm6VdsVdT/MbZ38xoj1X9HPkZhbmaBGUD1W8vxAfffbDe8bA6AKnQ==", + "cpu": [ + "ia32" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.23.1.tgz", + "integrity": "sha512-BHpFFeslkWrXWyUPnbKm+xYYVYruCinGcftSBaa8zoF9hZO4BcSCFUvHVTtzpIY6YzUnYtuEhZ+C9iEXjxnasg==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" } }, "node_modules/@eslint-community/eslint-utils": { @@ -3118,6 +3495,273 @@ "node": "^18.18.0 || ^20.9.0 || >=21.1.0" } }, + "node_modules/@fastify/accept-negotiator": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@fastify/accept-negotiator/-/accept-negotiator-2.0.0.tgz", + "integrity": "sha512-/Sce/kBzuTxIq5tJh85nVNOq9wKD8s+viIgX0fFMDBdw95gnpf53qmF1oBgJym3cPFliWUuSloVg/1w/rH0FcQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@fastify/ajv-compiler": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@fastify/ajv-compiler/-/ajv-compiler-4.0.1.tgz", + "integrity": "sha512-DxrBdgsjNLP0YM6W5Hd6/Fmj43S8zMKiFJYgi+Ri3htTGAowPVG/tG1wpnWLMjufEnehRivUCKZ1pLDIoZdTuw==", + "dev": true, + "license": "MIT", + "dependencies": { + "ajv": "^8.12.0", + "ajv-formats": "^3.0.1", + "fast-uri": "^3.0.0" + } + }, + "node_modules/@fastify/ajv-compiler/node_modules/ajv": { + "version": "8.17.1", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz", + "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.3", + "fast-uri": "^3.0.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/@fastify/ajv-compiler/node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "dev": true, + "license": "MIT" + }, + "node_modules/@fastify/cors": { + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/@fastify/cors/-/cors-10.0.1.tgz", + "integrity": "sha512-O8JIf6448uQbOgzSkCqhClw6gFTAqrdfeA6R3fc/3gwTJGUp7gl8/3tbNB+6INuu4RmgVOq99BmvdGbtu5pgOA==", + "dev": true, + "license": "MIT", + "dependencies": { + "fastify-plugin": "^5.0.0", + "mnemonist": "0.39.8" + } + }, + "node_modules/@fastify/error": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@fastify/error/-/error-4.0.0.tgz", + "integrity": "sha512-OO/SA8As24JtT1usTUTKgGH7uLvhfwZPwlptRi2Dp5P4KKmJI3gvsZ8MIHnNwDs4sLf/aai5LzTyl66xr7qMxA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@fastify/fast-json-stringify-compiler": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/@fastify/fast-json-stringify-compiler/-/fast-json-stringify-compiler-5.0.1.tgz", + "integrity": "sha512-f2d3JExJgFE3UbdFcpPwqNUEoHWmt8pAKf8f+9YuLESdefA0WgqxeT6DrGL4Yrf/9ihXNSKOqpjEmurV405meA==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-json-stringify": "^6.0.0" + } + }, + "node_modules/@fastify/merge-json-schemas": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/@fastify/merge-json-schemas/-/merge-json-schemas-0.1.1.tgz", + "integrity": "sha512-fERDVz7topgNjtXsJTTW1JKLy0rhuLRcquYqNR9rF7OcVpCa2OVW49ZPDIhaRRCaUuvVxI+N416xUoF76HNSXA==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.3" + } + }, + "node_modules/@fastify/send": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/@fastify/send/-/send-3.1.1.tgz", + "integrity": "sha512-LdiV2mle/2tH8vh6GwGl0ubfUAgvY+9yF9oGI1iiwVyNUVOQamvw5n+OFu6iCNNoyuCY80FFURBn4TZCbTe8LA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@lukeed/ms": "^2.0.2", + "escape-html": "~1.0.3", + "fast-decode-uri-component": "^1.0.1", + "http-errors": "^2.0.0", + "mime": "^3" + } + }, + "node_modules/@fastify/send/node_modules/mime": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-3.0.0.tgz", + "integrity": "sha512-jSCU7/VB1loIWBZe14aEYHU/+1UMEHoaO7qxCOVJOw9GgH72VAWppxNcjU+x9a2k3GSIBXNKxXQFqRvvZ7vr3A==", + "dev": true, + "license": "MIT", + "bin": { + "mime": "cli.js" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/@fastify/static": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/@fastify/static/-/static-8.0.2.tgz", + "integrity": "sha512-xJ+XaZVl4Y+lKztx8jGi+BE73aByhOmjMgaTx98E4XtVZxUpiaYQIMBlwACsJz+xohm0kvzV34BZoiZ+bsJtBQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@fastify/accept-negotiator": "^2.0.0", + "@fastify/send": "^3.1.0", + "content-disposition": "^0.5.4", + "fastify-plugin": "^5.0.0", + "fastq": "^1.17.1", + "glob": "^11.0.0" + } + }, + "node_modules/@fastify/static/node_modules/foreground-child": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.0.tgz", + "integrity": "sha512-Ld2g8rrAyMYFXBhEqMz8ZAHBi4J4uS1i/CxGMDnjyFWddMXLVcDp051DZfu+t7+ab7Wv6SMqpWmyFIj5UbfFvg==", + "dev": true, + "license": "ISC", + "dependencies": { + "cross-spawn": "^7.0.0", + "signal-exit": "^4.0.1" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@fastify/static/node_modules/glob": { + "version": "11.0.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-11.0.0.tgz", + "integrity": "sha512-9UiX/Bl6J2yaBbxKoEBRm4Cipxgok8kQYcOPEhScPwebu2I0HoQOuYdIO6S3hLuWoZgpDpwQZMzTFxgpkyT76g==", + "dev": true, + "license": "ISC", + "dependencies": { + "foreground-child": "^3.1.0", + "jackspeak": "^4.0.1", + "minimatch": "^10.0.0", + "minipass": "^7.1.2", + "package-json-from-dist": "^1.0.0", + "path-scurry": "^2.0.0" + }, + "bin": { + "glob": "dist/esm/bin.mjs" + }, + "engines": { + "node": "20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@fastify/static/node_modules/jackspeak": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-4.0.2.tgz", + "integrity": "sha512-bZsjR/iRjl1Nk1UkjGpAzLNfQtzuijhn2g+pbZb98HQ1Gk8vM9hfbxeMBP+M2/UUdwj0RqGG3mlvk2MsAqwvEw==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "@isaacs/cliui": "^8.0.2" + }, + "engines": { + "node": "20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@fastify/static/node_modules/lru-cache": { + "version": "11.0.2", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.0.2.tgz", + "integrity": "sha512-123qHRfJBmo2jXDbo/a5YOQrJoHF/GNQTLzQ5+IdK5pWpceK17yRc6ozlWd25FxvGKQbIUs91fDFkXmDHTKcyA==", + "dev": true, + "license": "ISC", + "engines": { + "node": "20 || >=22" + } + }, + "node_modules/@fastify/static/node_modules/minimatch": { + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.0.1.tgz", + "integrity": "sha512-ethXTt3SGGR+95gudmqJ1eNhRO7eGEGIgYA9vnPatK4/etz2MEVDno5GMCibdMTuBMyElzIlgxMna3K94XDIDQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": "20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@fastify/static/node_modules/minipass": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", + "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, + "node_modules/@fastify/static/node_modules/path-scurry": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-2.0.0.tgz", + "integrity": "sha512-ypGJsmGtdXUOeM5u93TyeIEfEhM6s+ljAhrk5vAvSx8uyY/02OvrZnA0YNGUrPXfpJMgI1ODd3nwz8Npx4O4cg==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "lru-cache": "^11.0.0", + "minipass": "^7.1.2" + }, + "engines": { + "node": "20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@fastify/static/node_modules/signal-exit": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@fastify/view": { + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/@fastify/view/-/view-10.0.1.tgz", + "integrity": "sha512-rXtBN0oVDmoRZAS7lelrCIahf+qFtlMOOas8VPdA7JvrJ9ChcF7e36pIUPU0Vbs3KmHxESUb7XatavUZEe/k5Q==", + "license": "MIT", + "dependencies": { + "fastify-plugin": "^5.0.0", + "toad-cache": "^3.7.0" + } + }, + "node_modules/@fastify/websocket": { + "version": "11.0.1", + "resolved": "https://registry.npmjs.org/@fastify/websocket/-/websocket-11.0.1.tgz", + "integrity": "sha512-44yam5+t1I9v09hWBYO+ezV88+mb9Se2BjgERtzB/68+0mGeTfFkjBeDBe2y+ZdiPpeO2rhevhdnfrBm5mqH+Q==", + "license": "MIT", + "dependencies": { + "duplexify": "^4.1.3", + "fastify-plugin": "^5.0.0", + "ws": "^8.16.0" + } + }, "node_modules/@formatjs/ecma402-abstract": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/@formatjs/ecma402-abstract/-/ecma402-abstract-2.0.0.tgz", @@ -3507,6 +4151,16 @@ "node": ">=v12.0.0" } }, + "node_modules/@lukeed/ms": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/@lukeed/ms/-/ms-2.0.2.tgz", + "integrity": "sha512-9I2Zn6+NJLfaGoz9jN3lpwDgAYvfGeNYdbAIjJOqzs4Tpc+VU3Jqq4IofSUBKajiDS8k9fZIg18/z13mpk1bsA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, "node_modules/@nightwatch/chai": { "version": "5.0.3", "resolved": "https://registry.npmjs.org/@nightwatch/chai/-/chai-5.0.3.tgz", @@ -3960,6 +4614,22 @@ "url": "https://opencollective.com/unts" } }, + "node_modules/@playwright/test": { + "version": "1.48.2", + "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.48.2.tgz", + "integrity": "sha512-54w1xCWfXuax7dz4W2M9uw0gDyh+ti/0K/MxcCUxChFh37kkdxPdfZDw5QBbuPUJHr1CiHJ1hXgSs+GgeQc5Zw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "playwright": "1.48.2" + }, + "bin": { + "playwright": "cli.js" + }, + "engines": { + "node": ">=18" + } + }, "node_modules/@protobufjs/aspromise": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/@protobufjs/aspromise/-/aspromise-1.1.2.tgz", @@ -4656,18 +5326,6 @@ "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", "dev": true }, - "node_modules/@sindresorhus/is": { - "version": "4.6.0", - "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-4.6.0.tgz", - "integrity": "sha512-t09vSN3MdfsyCHoFcTRCH/iUtG7OJ0CsjzB8cjAmKc/va/kIgeDI/TxsigdncE/4be734m0cvIYwNaV4i2XqAw==", - "dev": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sindresorhus/is?sponsor=1" - } - }, "node_modules/@sinonjs/commons": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-3.0.1.tgz", @@ -5415,8 +6073,11 @@ "node_modules/@socket.io/component-emitter": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/@socket.io/component-emitter/-/component-emitter-3.1.2.tgz", - "integrity": "sha512-9BCxFwvbGg/RsZK9tjXd8s4UcwR0MWeFQ1XEKIQVVvAGJyINdrqKMcTRyLoK8Rse1GjzLV9cwjWV1olXRWEXVA==", - "dev": true + "integrity": "sha512-9BCxFwvbGg/RsZK9tjXd8s4UcwR0MWeFQ1XEKIQVVvAGJyINdrqKMcTRyLoK8Rse1GjzLV9cwjWV1olXRWEXVA==" + }, + "node_modules/@splunk/otel-js-web-integration-tests": { + "resolved": "packages/integration-tests", + "link": true }, "node_modules/@splunk/otel-web": { "resolved": "packages/web", @@ -5523,18 +6184,6 @@ "url": "https://opencollective.com/eslint" } }, - "node_modules/@szmarczak/http-timer": { - "version": "4.0.6", - "resolved": "https://registry.npmjs.org/@szmarczak/http-timer/-/http-timer-4.0.6.tgz", - "integrity": "sha512-4BAffykYOgO+5nzBWYwE3W90sBgLJoUPRWWcL8wlyiM8IB8ipJz3UMJ9KXQd1RKQXpKp8Tutn80HZtWsu2u76w==", - "dev": true, - "dependencies": { - "defer-to-connect": "^2.0.0" - }, - "engines": { - "node": ">=10" - } - }, "node_modules/@testim/chrome-version": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/@testim/chrome-version/-/chrome-version-1.1.4.tgz", @@ -5580,16 +6229,15 @@ "integrity": "sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==", "dev": true }, - "node_modules/@types/cacheable-request": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/@types/cacheable-request/-/cacheable-request-6.0.3.tgz", - "integrity": "sha512-IQ3EbTzGxIigb1I3qPZc1rWJnH0BmSKv5QYTalEwweFvyBDLSAe24zP0le/hyi7ecGfZVlIVAg4BZqb8WBwKqw==", + "node_modules/@types/body-parser": { + "version": "1.19.5", + "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.5.tgz", + "integrity": "sha512-fB3Zu92ucau0iQ0JMCFQE7b/dv8Ot07NI3KaZIkIUNXq82k4eBAqUaneXfleGY9JWskeS9y+u0nXMyspcuQrCg==", "dev": true, + "license": "MIT", "dependencies": { - "@types/http-cache-semantics": "*", - "@types/keyv": "^3.1.4", - "@types/node": "*", - "@types/responselike": "^1.0.0" + "@types/connect": "*", + "@types/node": "*" } }, "node_modules/@types/chai": { @@ -5598,17 +6246,27 @@ "integrity": "sha512-PatH4iOdyh3MyWtmHVFXLWCCIhUbopaltqddG9BzB+gMIzee2MJrvd+jouii9Z3wzQJruGWAm7WOMjgfG8hQlQ==", "dev": true }, + "node_modules/@types/connect": { + "version": "3.4.38", + "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.38.tgz", + "integrity": "sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, "node_modules/@types/cookie": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/@types/cookie/-/cookie-0.4.1.tgz", "integrity": "sha512-XW/Aa8APYr6jSVVA1y/DEIZX0/GMKLEVekNG727R8cs56ahETkRAy/3DR7+fJyh7oUgGwNQaRfXCun0+KbWY7Q==", - "dev": true + "license": "MIT" }, "node_modules/@types/cors": { "version": "2.8.17", "resolved": "https://registry.npmjs.org/@types/cors/-/cors-2.8.17.tgz", "integrity": "sha512-8CGDvrBj1zgo2qE+oS3pOCyYNqCPryMWY2bGfwA0dcfopWGgxs+78df0Rs3rc9THP4JkOhLsAa+15VdpAqkcUA==", - "dev": true, + "license": "MIT", "dependencies": { "@types/node": "*" } @@ -5618,17 +6276,51 @@ "resolved": "https://registry.npmjs.org/@types/css-font-loading-module/-/css-font-loading-module-0.0.7.tgz", "integrity": "sha512-nl09VhutdjINdWyXxHWN/w9zlNCfr60JUqJbd24YXUuCwgeL0TpFSdElCwb6cxfB6ybE19Gjj4g0jsgkXxKv1Q==" }, + "node_modules/@types/ejs": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/@types/ejs/-/ejs-3.1.5.tgz", + "integrity": "sha512-nv+GSx77ZtXiJzwKdsASqi+YQ5Z7vwHsTP0JY2SiQgjGckkBRKZnk8nIM+7oUZ1VCtuTz0+By4qVR7fqzp/Dfg==", + "dev": true, + "license": "MIT" + }, "node_modules/@types/estree": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.5.tgz", "integrity": "sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==", "dev": true }, - "node_modules/@types/http-cache-semantics": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/@types/http-cache-semantics/-/http-cache-semantics-4.0.4.tgz", - "integrity": "sha512-1m0bIFVc7eJWyve9S0RnuRgcQqF/Xd5QsUZAZeQFr1Q3/p9JWoQQEqmVy+DPTNpGXwhgIetAoYF8JSc33q29QA==", - "dev": true + "node_modules/@types/express": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/@types/express/-/express-5.0.0.tgz", + "integrity": "sha512-DvZriSMehGHL1ZNLzi6MidnsDhUZM/x2pRdDIKdwbUNqqwHxMlRdkxtn6/EPKyqKpHqTl/4nRZsRNLpZxZRpPQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/body-parser": "*", + "@types/express-serve-static-core": "^5.0.0", + "@types/qs": "*", + "@types/serve-static": "*" + } + }, + "node_modules/@types/express-serve-static-core": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-5.0.1.tgz", + "integrity": "sha512-CRICJIl0N5cXDONAdlTv5ShATZ4HEwk6kDDIW2/w9qOWKg+NU/5F8wYRWCrONad0/UKkloNSmmyN/wX4rtpbVA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*", + "@types/qs": "*", + "@types/range-parser": "*", + "@types/send": "*" + } + }, + "node_modules/@types/http-errors": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/@types/http-errors/-/http-errors-2.0.4.tgz", + "integrity": "sha512-D0CFMMtydbJAegzOyHjtiKPLlvnm3iTZyZRSZoLq2mRhDdmLfIWOCYPfQJ4cu2erKghU++QvjcUjp/5h7hESpA==", + "dev": true, + "license": "MIT" }, "node_modules/@types/json-schema": { "version": "7.0.15", @@ -5637,15 +6329,6 @@ "dev": true, "license": "MIT" }, - "node_modules/@types/keyv": { - "version": "3.1.4", - "resolved": "https://registry.npmjs.org/@types/keyv/-/keyv-3.1.4.tgz", - "integrity": "sha512-BQ5aZNSCpj7D6K2ksrRCTmKRLEpnPvWDiLPfoGyhZ++8YtiK9d/3DBKPJgry359X/P1PfruyYwvnvwFjuEiEIg==", - "dev": true, - "dependencies": { - "@types/node": "*" - } - }, "node_modules/@types/linkify-it": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/@types/linkify-it/-/linkify-it-5.0.0.tgz", @@ -5668,6 +6351,13 @@ "integrity": "sha512-RGdgjQUZba5p6QEFAVx2OGb8rQDL/cPRG7GiedRzMcJ1tYnUANBncjbSB1NRGwbvjcPeikRABz2nshyPk1bhWg==", "dev": true }, + "node_modules/@types/mime": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.5.tgz", + "integrity": "sha512-/pyBZWSLD2n0dcHE3hq8s8ZvcETHtEuF+3E7XVt0Ig2nvsVQXdghHVcEkIWjy9A0wKfTn97a/PSDYohKIlnP/w==", + "dev": true, + "license": "MIT" + }, "node_modules/@types/mocha": { "version": "10.0.6", "resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-10.0.6.tgz", @@ -5687,28 +6377,34 @@ } }, "node_modules/@types/node": { - "version": "20.14.6", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.14.6.tgz", - "integrity": "sha512-JbA0XIJPL1IiNnU7PFxDXyfAwcwVVrOoqyzzyQTyMeVhBzkJVMSkC1LlVsRQ2lpqiY4n6Bb9oCS6lzDKVQxbZw==", + "version": "22.9.0", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.9.0.tgz", + "integrity": "sha512-vuyHg81vvWA1Z1ELfvLko2c8f34gyA0zaic0+Rllc5lbCnbSyuvb2Oxpm6TAUAC/2xZN3QGqxBNggD1nNR2AfQ==", + "license": "MIT", "dependencies": { - "undici-types": "~5.26.4" + "undici-types": "~6.19.8" } }, + "node_modules/@types/qs": { + "version": "6.9.17", + "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.17.tgz", + "integrity": "sha512-rX4/bPcfmvxHDv0XjfJELTTr+iB+tn032nPILqHm5wbthUUUuVtNGGqzhya9XUxjTP8Fpr0qYgSZZKxGY++svQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/range-parser": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.7.tgz", + "integrity": "sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ==", + "dev": true, + "license": "MIT" + }, "node_modules/@types/resolve": { "version": "1.20.2", "resolved": "https://registry.npmjs.org/@types/resolve/-/resolve-1.20.2.tgz", "integrity": "sha512-60BCwRFOZCQhDncwQdxxeOEEkbc5dIMccYLwbxsS4TUNeVECQ/pBJ0j09mrHOl/JJvpRPGwO9SvE4nR2Nb/a4Q==", "dev": true }, - "node_modules/@types/responselike": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@types/responselike/-/responselike-1.0.3.tgz", - "integrity": "sha512-H/+L+UkTV33uf49PH5pCAUBVPNj2nDBXTN+qS1dOwyyg24l3CcicicCA7ca+HMvJBZcFgl5r8e+RR6elsb4Lyw==", - "dev": true, - "dependencies": { - "@types/node": "*" - } - }, "node_modules/@types/selenium-webdriver": { "version": "4.1.23", "resolved": "https://registry.npmjs.org/@types/selenium-webdriver/-/selenium-webdriver-4.1.23.tgz", @@ -5719,6 +6415,29 @@ "@types/ws": "*" } }, + "node_modules/@types/send": { + "version": "0.17.4", + "resolved": "https://registry.npmjs.org/@types/send/-/send-0.17.4.tgz", + "integrity": "sha512-x2EM6TJOybec7c52BX0ZspPodMsQUd5L6PRwOunVyVUhXiBSKf3AezDL8Dgvgt5o0UfKNfuA0eMLr2wLT4AiBA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/mime": "^1", + "@types/node": "*" + } + }, + "node_modules/@types/serve-static": { + "version": "1.15.7", + "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.15.7.tgz", + "integrity": "sha512-W8Ym+h8nhuRwaKPaDw34QUkwsGi6Rc4yYqvKFo5rm2FUEhCFbzVWrxXUxuKK8TASjWsysJY0nsmNCGhCOIsrOw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/http-errors": "*", + "@types/node": "*", + "@types/send": "*" + } + }, "node_modules/@types/shimmer": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/@types/shimmer/-/shimmer-1.0.5.tgz", @@ -6222,11 +6941,17 @@ "node": ">=16.5.0" } }, + "node_modules/abstract-logging": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/abstract-logging/-/abstract-logging-2.0.1.tgz", + "integrity": "sha512-2BjRTZxTPvheOvGbBslFSYOUkr+SjPtOnrLP33f+VIWLzezQpZcqVg7ja3L4dBXmzzgwT+a029jRx5PCi3JuiA==", + "dev": true, + "license": "MIT" + }, "node_modules/accepts": { "version": "1.3.8", "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==", - "dev": true, "dependencies": { "mime-types": "~2.1.34", "negotiator": "0.6.3" @@ -6318,6 +7043,48 @@ "url": "https://github.com/sponsors/epoberezkin" } }, + "node_modules/ajv-formats": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-3.0.1.tgz", + "integrity": "sha512-8iUql50EUR+uUcdRQ3HDqa6EVyo3docL8g5WJ3FNcWmu62IbkGUue/pEyLBW8VGKKucTPgqeks4fIU1DA4yowQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ajv": "^8.0.0" + }, + "peerDependencies": { + "ajv": "^8.0.0" + }, + "peerDependenciesMeta": { + "ajv": { + "optional": true + } + } + }, + "node_modules/ajv-formats/node_modules/ajv": { + "version": "8.17.1", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz", + "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.3", + "fast-uri": "^3.0.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/ajv-formats/node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "dev": true, + "license": "MIT" + }, "node_modules/amdefine": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/amdefine/-/amdefine-1.0.1.tgz", @@ -6577,7 +7344,6 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/array-buffer-byte-length/-/array-buffer-byte-length-1.0.1.tgz", "integrity": "sha512-ahC5W1xgou+KTXix4sAO8Ki12Q+jf4i0+tmk3sC+zgcynshkHxzpXdImBehiUYKKKDwvfFiJl1tZt6ewscS1Mg==", - "dev": true, "dependencies": { "call-bind": "^1.0.5", "is-array-buffer": "^3.0.4" @@ -6599,7 +7365,6 @@ "version": "1.0.3", "resolved": "https://registry.npmjs.org/arraybuffer.prototype.slice/-/arraybuffer.prototype.slice-1.0.3.tgz", "integrity": "sha512-bMxMKAjg13EBSVscxTaYA4mRc5t1UAXa2kXiGTNfZ079HIWXEkKmkgFrh/nJqamaLSrXO5H4WFFkPEaLJWbs3A==", - "dev": true, "dependencies": { "array-buffer-byte-length": "^1.0.1", "call-bind": "^1.0.5", @@ -6650,11 +7415,19 @@ "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", "dev": true }, + "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": ">=8.0.0" + } + }, "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==", - "dev": true, "dependencies": { "possible-typed-array-names": "^1.0.0" }, @@ -6665,6 +7438,17 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/avvio": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/avvio/-/avvio-9.1.0.tgz", + "integrity": "sha512-fYASnYi600CsH/j9EQov7lECAniYiBFiiAtBNuZYLA2leLe9qOvZzqYHFjtIj6gD2VMoMLP14834LFWvr4IfDw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@fastify/error": "^4.0.0", + "fastq": "^1.17.1" + } + }, "node_modules/axe-core": { "version": "4.9.1", "resolved": "https://registry.npmjs.org/axe-core/-/axe-core-4.9.1.tgz", @@ -6742,8 +7526,7 @@ "node_modules/balanced-match": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", - "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", - "dev": true + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" }, "node_modules/bare-events": { "version": "2.4.2", @@ -6823,7 +7606,7 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/base64id/-/base64id-2.0.0.tgz", "integrity": "sha512-lGe34o6EHj9y3Kts9R4ZYs/Gr+6N7MCaMlIFA3F1R2O5/m7K06AxfSeO5530PEERE6/WyEg3lsuyw4GHlPZHog==", - "dev": true, + "license": "MIT", "engines": { "node": "^4.5.0 || >= 5.9" } @@ -6876,10 +7659,11 @@ "dev": true }, "node_modules/body-parser": { - "version": "1.20.2", - "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.2.tgz", - "integrity": "sha512-ml9pReCu3M61kGlqoTm2umSXTlRTuGTx0bfYj+uIUKKYycG5NtSbeetV3faSU6R7ajOPw0g/J1PvK4qNy7s5bA==", + "version": "1.20.3", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.3.tgz", + "integrity": "sha512-7rAxByjUMqQ3/bHJy7D6OGXvx/MMc4IqBn/X0fcM1QUcAItpZrBEYhWGem+tzXH90c+G01ypMcYJBO9Y30203g==", "dev": true, + "license": "MIT", "dependencies": { "bytes": "3.1.2", "content-type": "~1.0.5", @@ -6889,7 +7673,7 @@ "http-errors": "2.0.0", "iconv-lite": "0.4.24", "on-finished": "2.4.1", - "qs": "6.11.0", + "qs": "6.13.0", "raw-body": "2.5.2", "type-is": "~1.6.18", "unpipe": "1.0.0" @@ -7025,22 +7809,6 @@ "ieee754": "^1.1.13" } }, - "node_modules/buffer-alloc": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/buffer-alloc/-/buffer-alloc-1.2.0.tgz", - "integrity": "sha512-CFsHQgjtW1UChdXgbyJGtnm+O/uLQeZdtbDo8mfUgYXCHSM1wgrVxXm6bSyrUuErEb+4sYVGCzASBRot7zyrow==", - "dev": true, - "dependencies": { - "buffer-alloc-unsafe": "^1.1.0", - "buffer-fill": "^1.0.0" - } - }, - "node_modules/buffer-alloc-unsafe": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/buffer-alloc-unsafe/-/buffer-alloc-unsafe-1.1.0.tgz", - "integrity": "sha512-TEM2iMIEQdJ2yjPJoSIsldnleVaAk1oW3DBVUykyOLsEsFmEc9kn+SFFPz+gl54KQNxlDnAwCXosOS9Okx2xAg==", - "dev": true - }, "node_modules/buffer-crc32": { "version": "0.2.13", "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.13.tgz", @@ -7050,12 +7818,6 @@ "node": "*" } }, - "node_modules/buffer-fill": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/buffer-fill/-/buffer-fill-1.0.0.tgz", - "integrity": "sha512-T7zexNBwiiaCOGDg9xNX9PBmjrubblRkENuptryuI64URkXDFum9il/JGL8Lm8wYfAXpredVXXZz7eMHilimiQ==", - "dev": true - }, "node_modules/buffer-from": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", @@ -7066,7 +7828,7 @@ "version": "4.0.8", "resolved": "https://registry.npmjs.org/bufferutil/-/bufferutil-4.0.8.tgz", "integrity": "sha512-4T53u4PdgsXqKaIctwF8ifXlRTTmEPJ8iEPWFdGZvcf7sbwYo6FKFEX9eNNAnzFZ7EzJAQ3CJeOtCRA4rDp7Pw==", - "dev": true, + "devOptional": true, "hasInstallScript": true, "dependencies": { "node-gyp-build": "^4.3.0" @@ -7106,33 +7868,6 @@ "node": ">= 0.8" } }, - "node_modules/cacheable-lookup": { - "version": "5.0.4", - "resolved": "https://registry.npmjs.org/cacheable-lookup/-/cacheable-lookup-5.0.4.tgz", - "integrity": "sha512-2/kNscPhpcxrOigMZzbiWF7dz8ilhb/nIHU3EyZiXWXpeq/au8qJ8VhdftMkty3n7Gj6HIGalQG8oiBNB3AJgA==", - "dev": true, - "engines": { - "node": ">=10.6.0" - } - }, - "node_modules/cacheable-request": { - "version": "7.0.4", - "resolved": "https://registry.npmjs.org/cacheable-request/-/cacheable-request-7.0.4.tgz", - "integrity": "sha512-v+p6ongsrp0yTGbJXjgxPow2+DL93DASP4kXCDKb8/bwRtt9OEF3whggkkDkGNzgcWy2XaF4a8nZglC7uElscg==", - "dev": true, - "dependencies": { - "clone-response": "^1.0.2", - "get-stream": "^5.1.0", - "http-cache-semantics": "^4.0.0", - "keyv": "^4.0.0", - "lowercase-keys": "^2.0.0", - "normalize-url": "^6.0.1", - "responselike": "^2.0.0" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/caching-transform": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/caching-transform/-/caching-transform-4.0.0.tgz", @@ -7176,7 +7911,6 @@ "version": "1.0.7", "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.7.tgz", "integrity": "sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w==", - "dev": true, "dependencies": { "es-define-property": "^1.0.0", "es-errors": "^1.3.0", @@ -7200,16 +7934,6 @@ "node": ">=6" } }, - "node_modules/camel-case": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/camel-case/-/camel-case-4.1.2.tgz", - "integrity": "sha512-gxGWBrTT1JuMx6R+o5PTXMmUnhnVzLQ9SNutD4YqKtI6ap897t3tKECYla6gCWEkplXnlNybEkZg9GEGxKFCgw==", - "dev": true, - "dependencies": { - "pascal-case": "^3.1.2", - "tslib": "^2.0.3" - } - }, "node_modules/camelcase": { "version": "6.3.0", "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", @@ -7242,17 +7966,6 @@ } ] }, - "node_modules/capital-case": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/capital-case/-/capital-case-1.0.4.tgz", - "integrity": "sha512-ds37W8CytHgwnhGGTi88pcPyR15qoNkOpYwmMMfnWqqWgESapLqvDx6huFjQ5vqWSn2Z06173XNA7LtMOeUh1A==", - "dev": true, - "dependencies": { - "no-case": "^3.0.4", - "tslib": "^2.0.3", - "upper-case-first": "^2.0.2" - } - }, "node_modules/catharsis": { "version": "0.9.0", "resolved": "https://registry.npmjs.org/catharsis/-/catharsis-0.9.0.tgz", @@ -7311,26 +8024,6 @@ "url": "https://github.com/chalk/chalk?sponsor=1" } }, - "node_modules/change-case": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/change-case/-/change-case-4.1.2.tgz", - "integrity": "sha512-bSxY2ws9OtviILG1EiY5K7NNxkqg/JnRnFxLtKQ96JaviiIxi7djMrSd0ECT9AC+lttClmYwKw53BWpOMblo7A==", - "dev": true, - "dependencies": { - "camel-case": "^4.1.2", - "capital-case": "^1.0.4", - "constant-case": "^3.0.4", - "dot-case": "^3.0.4", - "header-case": "^2.0.4", - "no-case": "^3.0.4", - "param-case": "^3.0.4", - "pascal-case": "^3.1.2", - "path-case": "^3.0.4", - "sentence-case": "^3.0.4", - "snake-case": "^3.0.4", - "tslib": "^2.0.3" - } - }, "node_modules/check-error": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/check-error/-/check-error-1.0.3.tgz", @@ -7534,18 +8227,6 @@ "node": ">=0.8" } }, - "node_modules/clone-response": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/clone-response/-/clone-response-1.0.3.tgz", - "integrity": "sha512-ROoL94jJH2dUVML2Y/5PEDNaSHgeOdSDicUyS7izcF63G6sTc/FTjLub4b8Il9S8S0beOfYt0TaA5qvFK+w0wA==", - "dev": true, - "dependencies": { - "mimic-response": "^1.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/codecov": { "version": "3.8.3", "resolved": "https://registry.npmjs.org/codecov/-/codecov-3.8.3.tgz", @@ -7612,6 +8293,12 @@ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", "dev": true }, + "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/colors": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/colors/-/colors-1.4.0.tgz", @@ -7622,168 +8309,60 @@ } }, "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==", - "dev": true, - "dependencies": { - "delayed-stream": "~1.0.0" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/commander": { - "version": "2.20.3", - "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", - "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", - "dev": true - }, - "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 - }, - "node_modules/compare-versions": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/compare-versions/-/compare-versions-6.1.0.tgz", - "integrity": "sha512-LNZQXhqUvqUTotpZ00qLSaify3b4VFD588aRr8MKFw4CMUr98ytzCW5wDH5qx/DEY5kCDXcbcRuCqL0szEf2tg==", - "dev": true - }, - "node_modules/compress-commons": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/compress-commons/-/compress-commons-4.1.2.tgz", - "integrity": "sha512-D3uMHtGc/fcO1Gt1/L7i1e33VOvD4A9hfQLP+6ewd+BvG/gQ84Yh4oftEhAdjSMgBgwGL+jsppT7JYNpo6MHHg==", - "dev": true, - "dependencies": { - "buffer-crc32": "^0.2.13", - "crc32-stream": "^4.0.2", - "normalize-path": "^3.0.0", - "readable-stream": "^3.6.0" - }, - "engines": { - "node": ">= 10" - } - }, - "node_modules/compressible": { - "version": "2.0.18", - "resolved": "https://registry.npmjs.org/compressible/-/compressible-2.0.18.tgz", - "integrity": "sha512-AF3r7P5dWxL8MxyITRMlORQNaOA2IkAFaTr4k7BUumjPtRpGDTZpl0Pb1XCO6JeDCBdp126Cgs9sMxqSjgYyRg==", - "dev": true, - "dependencies": { - "mime-db": ">= 1.43.0 < 2" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/compressing": { - "version": "1.10.1", - "resolved": "https://registry.npmjs.org/compressing/-/compressing-1.10.1.tgz", - "integrity": "sha512-XXwUffcVjqv8NGSQu1ttp6eMmuZ3zZEAec28Rt30o/vkXE20jXhowRQ9LXLY4uOgFkxXrNzApLobpam53Dc1AA==", - "dev": true, - "dependencies": { - "@eggjs/yauzl": "^2.11.0", - "flushwritable": "^1.0.0", - "get-ready": "^1.0.0", - "iconv-lite": "^0.5.0", - "mkdirp": "^0.5.1", - "pump": "^3.0.0", - "streamifier": "^0.1.1", - "tar-stream": "^1.5.2", - "yazl": "^2.4.2" - }, - "engines": { - "node": ">= 4.0.0" - } - }, - "node_modules/compressing/node_modules/bl": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/bl/-/bl-1.2.3.tgz", - "integrity": "sha512-pvcNpa0UU69UT341rO6AYy4FVAIkUHuZXRIWbq+zHnsVcRzDDjIAhGuuYoi0d//cwIwtt4pkpKycWEfjdV+vww==", - "dev": true, - "dependencies": { - "readable-stream": "^2.3.5", - "safe-buffer": "^5.1.1" - } - }, - "node_modules/compressing/node_modules/iconv-lite": { - "version": "0.5.2", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.5.2.tgz", - "integrity": "sha512-kERHXvpSaB4aU3eANwidg79K8FlrN77m8G9V+0vOR3HYaRifrlwMEpT7ZBJqLSEIHnEgJTHcWK82wwLwwKwtag==", - "dev": true, - "dependencies": { - "safer-buffer": ">= 2.1.2 < 3" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/compressing/node_modules/isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", - "dev": true - }, - "node_modules/compressing/node_modules/mkdirp": { - "version": "0.5.6", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz", - "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==", - "dev": true, - "dependencies": { - "minimist": "^1.2.6" - }, - "bin": { - "mkdirp": "bin/cmd.js" - } - }, - "node_modules/compressing/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==", + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", "dev": true, "dependencies": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" + "delayed-stream": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" } }, - "node_modules/compressing/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==", + "node_modules/commander": { + "version": "2.20.3", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", + "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", "dev": true }, - "node_modules/compressing/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==", + "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 + }, + "node_modules/compare-versions": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/compare-versions/-/compare-versions-6.1.0.tgz", + "integrity": "sha512-LNZQXhqUvqUTotpZ00qLSaify3b4VFD588aRr8MKFw4CMUr98ytzCW5wDH5qx/DEY5kCDXcbcRuCqL0szEf2tg==", + "dev": true + }, + "node_modules/compress-commons": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/compress-commons/-/compress-commons-4.1.2.tgz", + "integrity": "sha512-D3uMHtGc/fcO1Gt1/L7i1e33VOvD4A9hfQLP+6ewd+BvG/gQ84Yh4oftEhAdjSMgBgwGL+jsppT7JYNpo6MHHg==", "dev": true, "dependencies": { - "safe-buffer": "~5.1.0" + "buffer-crc32": "^0.2.13", + "crc32-stream": "^4.0.2", + "normalize-path": "^3.0.0", + "readable-stream": "^3.6.0" + }, + "engines": { + "node": ">= 10" } }, - "node_modules/compressing/node_modules/tar-stream": { - "version": "1.6.2", - "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-1.6.2.tgz", - "integrity": "sha512-rzS0heiNf8Xn7/mpdSVVSMAWAoy9bfb1WOTYC78Z0UQKeKa/CWS8FOq0lKGNa8DWKAn9gxjCvMLYc5PGXYlK2A==", + "node_modules/compressible": { + "version": "2.0.18", + "resolved": "https://registry.npmjs.org/compressible/-/compressible-2.0.18.tgz", + "integrity": "sha512-AF3r7P5dWxL8MxyITRMlORQNaOA2IkAFaTr4k7BUumjPtRpGDTZpl0Pb1XCO6JeDCBdp126Cgs9sMxqSjgYyRg==", "dev": true, "dependencies": { - "bl": "^1.0.0", - "buffer-alloc": "^1.2.0", - "end-of-stream": "^1.0.0", - "fs-constants": "^1.0.0", - "readable-stream": "^2.3.0", - "to-buffer": "^1.1.1", - "xtend": "^4.0.0" + "mime-db": ">= 1.43.0 < 2" }, "engines": { - "node": ">= 0.8.0" + "node": ">= 0.6" } }, "node_modules/compression": { @@ -7837,8 +8416,7 @@ "node_modules/concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", - "dev": true + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==" }, "node_modules/configstore": { "version": "5.0.1", @@ -7950,17 +8528,6 @@ "node": ">= 0.6" } }, - "node_modules/constant-case": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/constant-case/-/constant-case-3.0.4.tgz", - "integrity": "sha512-I2hSBi7Vvs7BEuJDr5dDHfzb/Ruj3FyvFyh7KLilAjNQw3Be+xgqUBA2W6scVEcL0hL1dwPRtIqEPVUCKkSsyQ==", - "dev": true, - "dependencies": { - "no-case": "^3.0.4", - "tslib": "^2.0.3", - "upper-case": "^2.0.2" - } - }, "node_modules/content-disposition": { "version": "0.5.4", "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", @@ -7989,10 +8556,11 @@ "dev": true }, "node_modules/cookie": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.6.0.tgz", - "integrity": "sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw==", + "version": "0.7.1", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.1.tgz", + "integrity": "sha512-6DnInpx7SJ2AK3+CTUE/ZM0vWTUboZCegxhC2xiIydHR9jNuTAASBrfEpHhiGOZw/nX51bHt6YQl8jsGo4y/0w==", "dev": true, + "license": "MIT", "engines": { "node": ">= 0.6" } @@ -8037,7 +8605,7 @@ "version": "2.8.5", "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz", "integrity": "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==", - "dev": true, + "license": "MIT", "dependencies": { "object-assign": "^4", "vary": "^1" @@ -8220,7 +8788,6 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/data-view-buffer/-/data-view-buffer-1.0.1.tgz", "integrity": "sha512-0lht7OugA5x3iJLOWFhWK/5ehONdprk0ISXqVFn/NFrDu+cuc8iADFrGQz5BnRK7LLU3JmkbXSxaqX+/mXYtUA==", - "dev": true, "dependencies": { "call-bind": "^1.0.6", "es-errors": "^1.3.0", @@ -8237,7 +8804,6 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/data-view-byte-length/-/data-view-byte-length-1.0.1.tgz", "integrity": "sha512-4J7wRJD3ABAzr8wP+OcIcqq2dlUKp4DVflx++hs5h5ZKydWMI6/D/fAot+yh6g2tHh8fLFTvNOaVN357NvSrOQ==", - "dev": true, "dependencies": { "call-bind": "^1.0.7", "es-errors": "^1.3.0", @@ -8254,7 +8820,6 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/data-view-byte-offset/-/data-view-byte-offset-1.0.0.tgz", "integrity": "sha512-t/Ygsytq+R995EJ5PZlD4Cu56sWa8InXySaViRzw9apusqsOO2bQP+SbYzAhR0pFKoB+43lYy8rWban9JSuXnA==", - "dev": true, "dependencies": { "call-bind": "^1.0.6", "es-errors": "^1.3.0", @@ -8276,6 +8841,15 @@ "node": ">=4.0" } }, + "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/debounce": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/debounce/-/debounce-1.2.1.tgz", @@ -8316,42 +8890,6 @@ "integrity": "sha512-VBBaLc1MgL5XpzgIP7ny5Z6Nx3UrRkIViUkPUdtl9aya5amy3De1gsUUSB1g3+3sExYNjCAsAznmukyxCb1GRA==", "dev": true }, - "node_modules/decode-uri-component": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.2.tgz", - "integrity": "sha512-FqUYQ+8o158GyGTrMFJms9qh3CqTKvAqgqsTnkLI8sKu0028orqBhxNMFkFen0zGyg6epACD32pjVk58ngIErQ==", - "dev": true, - "engines": { - "node": ">=0.10" - } - }, - "node_modules/decompress-response": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-6.0.0.tgz", - "integrity": "sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==", - "dev": true, - "dependencies": { - "mimic-response": "^3.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/decompress-response/node_modules/mimic-response": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-3.1.0.tgz", - "integrity": "sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==", - "dev": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/deep-eql": { "version": "4.1.4", "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-4.1.4.tgz", @@ -8456,20 +8994,10 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/defer-to-connect": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/defer-to-connect/-/defer-to-connect-2.0.1.tgz", - "integrity": "sha512-4tvttepXG1VaYGrRibk5EwJd1t4udunSOVMdLSAL6mId1ix438oPwPZMALY41FCijukO1L0twNcGsdzS7dHgDg==", - "dev": true, - "engines": { - "node": ">=10" - } - }, "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==", - "dev": true, "dependencies": { "es-define-property": "^1.0.0", "es-errors": "^1.3.0", @@ -8495,7 +9023,6 @@ "version": "1.2.1", "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.2.1.tgz", "integrity": "sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==", - "dev": true, "dependencies": { "define-data-property": "^1.0.1", "has-property-descriptors": "^1.0.0", @@ -8616,16 +9143,6 @@ "void-elements": "^2.0.0" } }, - "node_modules/dot-case": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/dot-case/-/dot-case-3.0.4.tgz", - "integrity": "sha512-Kv5nKlh6yRrdrGvxeJ2e5y2eRUpkUosIW4A2AS38zwSz27zu7ufDwQPi5Jhs3XAlGNetl3bmnGhQsMtkKJnj3w==", - "dev": true, - "dependencies": { - "no-case": "^3.0.4", - "tslib": "^2.0.3" - } - }, "node_modules/dot-prop": { "version": "5.3.0", "resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-5.3.0.tgz", @@ -8650,6 +9167,18 @@ "url": "https://dotenvx.com" } }, + "node_modules/duplexify": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/duplexify/-/duplexify-4.1.3.tgz", + "integrity": "sha512-M3BmBhwJRZsSx38lZyhE53Csddgzl5R7xGJNk7CVddZD6CcmwMCH8J+7AprIrQKH7TonKxaCjcv27Qmf+sQ+oA==", + "license": "MIT", + "dependencies": { + "end-of-stream": "^1.4.1", + "inherits": "^2.0.3", + "readable-stream": "^3.1.1", + "stream-shift": "^1.0.2" + } + }, "node_modules/eastasianwidth": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", @@ -8702,23 +9231,22 @@ "version": "1.4.4", "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==", - "dev": true, "dependencies": { "once": "^1.4.0" } }, "node_modules/engine.io": { - "version": "6.5.5", - "resolved": "https://registry.npmjs.org/engine.io/-/engine.io-6.5.5.tgz", - "integrity": "sha512-C5Pn8Wk+1vKBoHghJODM63yk8MvrO9EWZUfkAt5HAqIgPE4/8FF0PEGHXtEd40l223+cE5ABWuPzm38PHFXfMA==", - "dev": true, + "version": "6.6.2", + "resolved": "https://registry.npmjs.org/engine.io/-/engine.io-6.6.2.tgz", + "integrity": "sha512-gmNvsYi9C8iErnZdVcJnvCpSKbWTt1E8+JZo8b+daLninywUWi5NQ5STSHZ9rFjFO7imNcvb8Pc5pe/wMR5xEw==", + "license": "MIT", "dependencies": { "@types/cookie": "^0.4.1", "@types/cors": "^2.8.12", "@types/node": ">=10.0.0", "accepts": "~1.3.4", "base64id": "2.0.0", - "cookie": "~0.4.1", + "cookie": "~0.7.2", "cors": "~2.8.5", "debug": "~4.3.1", "engine.io-parser": "~5.2.1", @@ -8745,16 +9273,15 @@ "version": "5.2.2", "resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-5.2.2.tgz", "integrity": "sha512-RcyUFKA93/CXH20l4SoVvzZfrSDMOTUS3bWVpTt2FuFP+XYrL8i8oonHP7WInRyVHXh0n/ORtoeiE1os+8qkSw==", - "dev": true, "engines": { "node": ">=10.0.0" } }, "node_modules/engine.io/node_modules/cookie": { - "version": "0.4.2", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.2.tgz", - "integrity": "sha512-aSWTXFzaKWkvHO1Ny/s+ePFpvKsPnjc551iI41v3ny/ow6tBG5Vd+FuqGNhh1LxOmVzOlGUriIlOaokOvhaStA==", - "dev": true, + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.2.tgz", + "integrity": "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==", + "license": "MIT", "engines": { "node": ">= 0.6" } @@ -8815,7 +9342,6 @@ "version": "1.3.2", "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", - "dev": true, "dependencies": { "is-arrayish": "^0.2.1" } @@ -8824,7 +9350,6 @@ "version": "1.23.3", "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.23.3.tgz", "integrity": "sha512-e+HfNH61Bj1X9/jLc5v1owaLYuHdeHHSQlkhCBiTK8rBvKaULl/beGMxwrMXjpYrv4pz22BlY570vVePA2ho4A==", - "dev": true, "dependencies": { "array-buffer-byte-length": "^1.0.1", "arraybuffer.prototype.slice": "^1.0.3", @@ -8884,7 +9409,6 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.0.tgz", "integrity": "sha512-jxayLKShrEqqzJ0eumQbVhTYQM27CfT1T35+gCgDFoL82JLsXqTJ76zv6A0YLOgEnLUMvLzsDsGIrl8NFpT2gQ==", - "dev": true, "dependencies": { "get-intrinsic": "^1.2.4" }, @@ -8896,7 +9420,6 @@ "version": "1.3.0", "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", - "dev": true, "engines": { "node": ">= 0.4" } @@ -8925,7 +9448,6 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.0.0.tgz", "integrity": "sha512-MZ4iQ6JwHOBQjahnjwaC1ZtIBH+2ohjamzAO3oaHcXYup7qxjF2fixyH+Q71voWHeOkI2q/TnJao/KfXYIZWbw==", - "dev": true, "dependencies": { "es-errors": "^1.3.0" }, @@ -8937,7 +9459,6 @@ "version": "2.0.3", "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.0.3.tgz", "integrity": "sha512-3T8uNMC3OQTHkFUsFq8r/BwAXLHvU/9O9mE0fBc/MY5iq/8H7ncvO947LmYA6ldWw9Uh8Yhf25zu6n7nML5QWQ==", - "dev": true, "dependencies": { "get-intrinsic": "^1.2.4", "has-tostringtag": "^1.0.2", @@ -8951,7 +9472,6 @@ "version": "1.2.1", "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz", "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==", - "dev": true, "dependencies": { "is-callable": "^1.1.4", "is-date-object": "^1.0.1", @@ -9010,6 +9530,45 @@ "node": ">=0.12" } }, + "node_modules/esbuild": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.23.1.tgz", + "integrity": "sha512-VVNz/9Sa0bs5SELtn3f7qhJCDPCF5oMEl5cO9/SSinpE9hbPVvxbd572HH5AKiP7WD8INO53GgfDDhRjkylHEg==", + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.23.1", + "@esbuild/android-arm": "0.23.1", + "@esbuild/android-arm64": "0.23.1", + "@esbuild/android-x64": "0.23.1", + "@esbuild/darwin-arm64": "0.23.1", + "@esbuild/darwin-x64": "0.23.1", + "@esbuild/freebsd-arm64": "0.23.1", + "@esbuild/freebsd-x64": "0.23.1", + "@esbuild/linux-arm": "0.23.1", + "@esbuild/linux-arm64": "0.23.1", + "@esbuild/linux-ia32": "0.23.1", + "@esbuild/linux-loong64": "0.23.1", + "@esbuild/linux-mips64el": "0.23.1", + "@esbuild/linux-ppc64": "0.23.1", + "@esbuild/linux-riscv64": "0.23.1", + "@esbuild/linux-s390x": "0.23.1", + "@esbuild/linux-x64": "0.23.1", + "@esbuild/netbsd-x64": "0.23.1", + "@esbuild/openbsd-arm64": "0.23.1", + "@esbuild/openbsd-x64": "0.23.1", + "@esbuild/sunos-x64": "0.23.1", + "@esbuild/win32-arm64": "0.23.1", + "@esbuild/win32-ia32": "0.23.1", + "@esbuild/win32-x64": "0.23.1" + } + }, "node_modules/escalade": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.2.tgz", @@ -9439,6 +9998,7 @@ "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", "dev": true, + "license": "MIT", "engines": { "node": ">= 0.6" } @@ -9460,37 +10020,38 @@ "dev": true }, "node_modules/express": { - "version": "4.19.2", - "resolved": "https://registry.npmjs.org/express/-/express-4.19.2.tgz", - "integrity": "sha512-5T6nhjsT+EOMzuck8JjBHARTHfMht0POzlA60WV2pMD3gyXw2LZnZ+ueGdNxG+0calOJcWKbpFcuzLZ91YWq9Q==", + "version": "4.21.1", + "resolved": "https://registry.npmjs.org/express/-/express-4.21.1.tgz", + "integrity": "sha512-YSFlK1Ee0/GC8QaO91tHcDxJiE/X4FbpAyQWkxAvG6AXCuR65YzK8ua6D9hvi/TzUfZMpc+BwuM1IPw8fmQBiQ==", "dev": true, + "license": "MIT", "dependencies": { "accepts": "~1.3.8", "array-flatten": "1.1.1", - "body-parser": "1.20.2", + "body-parser": "1.20.3", "content-disposition": "0.5.4", "content-type": "~1.0.4", - "cookie": "0.6.0", + "cookie": "0.7.1", "cookie-signature": "1.0.6", "debug": "2.6.9", "depd": "2.0.0", - "encodeurl": "~1.0.2", + "encodeurl": "~2.0.0", "escape-html": "~1.0.3", "etag": "~1.8.1", - "finalhandler": "1.2.0", + "finalhandler": "1.3.1", "fresh": "0.5.2", "http-errors": "2.0.0", - "merge-descriptors": "1.0.1", + "merge-descriptors": "1.0.3", "methods": "~1.1.2", "on-finished": "2.4.1", "parseurl": "~1.3.3", - "path-to-regexp": "0.1.7", + "path-to-regexp": "0.1.10", "proxy-addr": "~2.0.7", - "qs": "6.11.0", + "qs": "6.13.0", "range-parser": "~1.2.1", "safe-buffer": "5.2.1", - "send": "0.18.0", - "serve-static": "1.15.0", + "send": "0.19.0", + "serve-static": "1.16.2", "setprototypeof": "1.2.0", "statuses": "2.0.1", "type-is": "~1.6.18", @@ -9510,6 +10071,16 @@ "ms": "2.0.0" } }, + "node_modules/express/node_modules/encodeurl": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz", + "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, "node_modules/express/node_modules/ms": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", @@ -9551,6 +10122,19 @@ "@types/yauzl": "^2.9.1" } }, + "node_modules/fast-copy": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/fast-copy/-/fast-copy-3.0.2.tgz", + "integrity": "sha512-dl0O9Vhju8IrcLndv2eU4ldt1ftXMqqfgN4H1cpmGV7P6jeB9FwpN9a2c8DPGE1Ys88rNUJVYDHq73CGAGOPfQ==", + "license": "MIT" + }, + "node_modules/fast-decode-uri-component": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/fast-decode-uri-component/-/fast-decode-uri-component-1.0.1.tgz", + "integrity": "sha512-WKgKWg5eUxvRZGwW8FvfbaH7AXSh2cL+3j5fMGzUMCxWBJ3dV3a7Wz8y2f/uQ0e3B6WmodD3oS54jTQ9HVTIIg==", + "dev": true, + "license": "MIT" + }, "node_modules/fast-deep-equal": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", @@ -9608,12 +10192,99 @@ "dev": true, "license": "MIT" }, + "node_modules/fast-json-stringify": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/fast-json-stringify/-/fast-json-stringify-6.0.0.tgz", + "integrity": "sha512-FGMKZwniMTgZh7zQp9b6XnBVxUmKVahQLQeRQHqwYmPDqDhcEKZ3BaQsxelFFI5PY7nN71OEeiL47/zUWcYe1A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@fastify/merge-json-schemas": "^0.1.1", + "ajv": "^8.12.0", + "ajv-formats": "^3.0.1", + "fast-deep-equal": "^3.1.3", + "fast-uri": "^2.3.0", + "json-schema-ref-resolver": "^1.0.1", + "rfdc": "^1.2.0" + } + }, + "node_modules/fast-json-stringify/node_modules/ajv": { + "version": "8.17.1", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz", + "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.3", + "fast-uri": "^3.0.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/fast-json-stringify/node_modules/ajv/node_modules/fast-uri": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.0.3.tgz", + "integrity": "sha512-aLrHthzCjH5He4Z2H9YZ+v6Ujb9ocRuW6ZzkJQOrTxleEijANq4v1TsaPaVG1PZcuurEzrLcWRyYBYXD5cEiaw==", + "dev": true, + "license": "BSD-3-Clause" + }, + "node_modules/fast-json-stringify/node_modules/fast-uri": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-2.4.0.tgz", + "integrity": "sha512-ypuAmmMKInk5q7XcepxlnUWDLWv4GFtaJqAzWKqn62IpQ3pejtr5dTVbt3vwqVaMKmkNR55sTT+CqUKIaT21BA==", + "dev": true, + "license": "MIT" + }, + "node_modules/fast-json-stringify/node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "dev": true, + "license": "MIT" + }, "node_modules/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 }, + "node_modules/fast-querystring": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/fast-querystring/-/fast-querystring-1.1.2.tgz", + "integrity": "sha512-g6KuKWmFXc0fID8WWH0jit4g0AGBoJhCkJMb1RmbsSEUNvQ+ZC8D6CUZ+GtF8nMzSPXnhiePyyqqipzNNEnHjg==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-decode-uri-component": "^1.0.1" + } + }, + "node_modules/fast-redact": { + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/fast-redact/-/fast-redact-3.5.0.tgz", + "integrity": "sha512-dwsoQlS7h9hMeYUq1W++23NDcBLV4KqONnITDV9DjfS3q1SgDGVrBdvvTLUotWtPSD7asWDV9/CmsZPy8Hf70A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "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.0.3", + "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.0.3.tgz", + "integrity": "sha512-aLrHthzCjH5He4Z2H9YZ+v6Ujb9ocRuW6ZzkJQOrTxleEijANq4v1TsaPaVG1PZcuurEzrLcWRyYBYXD5cEiaw==", + "dev": true, + "license": "BSD-3-Clause" + }, "node_modules/fast-url-parser": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/fast-url-parser/-/fast-url-parser-1.1.3.tgz", @@ -9651,6 +10322,46 @@ "fxparser": "src/cli/cli.js" } }, + "node_modules/fastify": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/fastify/-/fastify-5.1.0.tgz", + "integrity": "sha512-0SdUC5AoiSgMSc2Vxwv3WyKzyGMDJRAW/PgNsK1kZrnkO6MeqUIW9ovVg9F2UGIqtIcclYMyeJa4rK6OZc7Jxg==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fastify" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fastify" + } + ], + "license": "MIT", + "dependencies": { + "@fastify/ajv-compiler": "^4.0.0", + "@fastify/error": "^4.0.0", + "@fastify/fast-json-stringify-compiler": "^5.0.0", + "abstract-logging": "^2.0.1", + "avvio": "^9.0.0", + "fast-json-stringify": "^6.0.0", + "find-my-way": "^9.0.0", + "light-my-request": "^6.0.0", + "pino": "^9.0.0", + "process-warning": "^4.0.0", + "proxy-addr": "^2.0.7", + "rfdc": "^1.3.1", + "secure-json-parse": "^2.7.0", + "semver": "^7.6.0", + "toad-cache": "^3.7.0" + } + }, + "node_modules/fastify-plugin": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/fastify-plugin/-/fastify-plugin-5.0.1.tgz", + "integrity": "sha512-HCxs+YnRaWzCl+cWRYFnHmeRFyR5GVnJTAaCJQiYzQSDwK9MgJdyAsuL3nh0EWRCYMgQ5MeziymvmAhUHYHDUQ==", + "license": "MIT" + }, "node_modules/fastq": { "version": "1.17.1", "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.17.1.tgz", @@ -9670,15 +10381,6 @@ "pend": "~1.2.0" } }, - "node_modules/fd-slicer2": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/fd-slicer2/-/fd-slicer2-1.2.0.tgz", - "integrity": "sha512-3lBUNUckhMZduCc4g+Pw4Ve16LD9vpX9b8qUkkKq2mgDRLYWzblszZH2luADnJqjJe+cypngjCuKRm/IW12rRw==", - "dev": true, - "dependencies": { - "pend": "^1.2.0" - } - }, "node_modules/fetch-blob": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/fetch-blob/-/fetch-blob-3.2.0.tgz", @@ -9753,23 +10455,15 @@ "node": ">=8" } }, - "node_modules/filter-obj": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/filter-obj/-/filter-obj-1.1.0.tgz", - "integrity": "sha512-8rXg1ZnX7xzy2NGDVkBVaAy+lSlPNwad13BtgSlLuxfIslyt5Vg64U7tFcCt4WS1R0hvtnQybT/IyCkGZ3DpXQ==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/finalhandler": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.2.0.tgz", - "integrity": "sha512-5uXcUVftlQMFnWC9qu/svkWv3GTd2PfUhK/3PLkYNAe7FbqJMt3515HaxE6eRL74GdsriiwujiawdaB1BpEISg==", + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.3.1.tgz", + "integrity": "sha512-6BN9trH7bp3qvnrRyzsBz+g3lZxTNZTbVO2EV1CS0WIcDbawYVdYvGflME/9QP0h0pYlCDBCTjYa9nZzMDpyxQ==", "dev": true, + "license": "MIT", "dependencies": { "debug": "2.6.9", - "encodeurl": "~1.0.2", + "encodeurl": "~2.0.0", "escape-html": "~1.0.3", "on-finished": "2.4.1", "parseurl": "~1.3.3", @@ -9785,15 +10479,27 @@ "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", "dev": true, + "license": "MIT", "dependencies": { "ms": "2.0.0" } }, + "node_modules/finalhandler/node_modules/encodeurl": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz", + "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, "node_modules/finalhandler/node_modules/ms": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/find-cache-dir": { "version": "3.3.2", @@ -9836,6 +10542,21 @@ "semver": "bin/semver.js" } }, + "node_modules/find-my-way": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/find-my-way/-/find-my-way-9.1.0.tgz", + "integrity": "sha512-Y5jIsuYR4BwWDYYQ2A/RWWE6gD8a0FMgtU+HOq1WKku+Cwdz8M1v8wcAmRXXM1/iqtoqg06v+LjAxMYbCjViMw==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.3", + "fast-querystring": "^1.0.0", + "safe-regex2": "^4.0.0" + }, + "engines": { + "node": ">=14" + } + }, "node_modules/find-up": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", @@ -9881,12 +10602,6 @@ "integrity": "sha512-X8cqMLLie7KsNUDSdzeN8FYK9rEt4Dt67OsG/DNGnYTSDBG4uFAJFBnUeiV+zCVAvwFy56IjM9sH51jVaEhNxw==", "dev": true }, - "node_modules/flushwritable": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/flushwritable/-/flushwritable-1.0.0.tgz", - "integrity": "sha512-3VELfuWCLVzt5d2Gblk8qcqFro6nuwvxwMzHaENVDHI7rxcBRtMCwTk/E9FXcgh+82DSpavPNDueA9+RxXJoFg==", - "dev": true - }, "node_modules/follow-redirects": { "version": "1.15.6", "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.6.tgz", @@ -9911,7 +10626,6 @@ "version": "0.3.3", "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.3.tgz", "integrity": "sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==", - "dev": true, "dependencies": { "is-callable": "^1.1.3" } @@ -9969,6 +10683,7 @@ "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==", "dev": true, + "license": "MIT", "engines": { "node": ">= 0.6" } @@ -10023,7 +10738,6 @@ "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, "optional": true, "os": [ @@ -10045,7 +10759,6 @@ "version": "1.1.6", "resolved": "https://registry.npmjs.org/function.prototype.name/-/function.prototype.name-1.1.6.tgz", "integrity": "sha512-Z5kx79swU5P27WEayXM1tBi5Ze/lbIyiNgU3qyXUOf9b2rgXYyF9Dy9Cx+IQv/Lc8WCG6L82zwUPpSS9hGehIg==", - "dev": true, "dependencies": { "call-bind": "^1.0.2", "define-properties": "^1.2.0", @@ -10063,7 +10776,6 @@ "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, "funding": { "url": "https://github.com/sponsors/ljharb" } @@ -10181,7 +10893,6 @@ "version": "1.2.4", "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.4.tgz", "integrity": "sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ==", - "dev": true, "dependencies": { "es-errors": "^1.3.0", "function-bind": "^1.1.2", @@ -10205,12 +10916,6 @@ "node": ">=8.0.0" } }, - "node_modules/get-ready": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/get-ready/-/get-ready-1.0.0.tgz", - "integrity": "sha512-mFXCZPJIlcYcth+N8267+mghfYN9h3EhsDa6JSnbA3Wrhh/XFpuowviFcsDeYZtKspQyWyJqfs4O6P8CHeTwzw==", - "dev": true - }, "node_modules/get-stdin": { "version": "9.0.0", "resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-9.0.0.tgz", @@ -10242,7 +10947,6 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.0.2.tgz", "integrity": "sha512-g0QYk1dZBxGwk+Ngc+ltRH2IBp2f7zBkBMBJZCDerh6EhlhSR6+9irMCuT/09zD6qkarHUSn529sK/yL4S27mg==", - "dev": true, "dependencies": { "call-bind": "^1.0.5", "es-errors": "^1.3.0", @@ -10255,6 +10959,18 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/get-tsconfig": { + "version": "4.8.1", + "resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.8.1.tgz", + "integrity": "sha512-k9PN+cFBmaLWtVz29SkUoqU5O0slLuHJXt/2P+tMVFT+phsSGXGkp9t3rQIqdz0e+06EHNGs3oM6ZX1s2zHxRg==", + "license": "MIT", + "dependencies": { + "resolve-pkg-maps": "^1.0.0" + }, + "funding": { + "url": "https://github.com/privatenumber/get-tsconfig?sponsor=1" + } + }, "node_modules/get-uri": { "version": "6.0.3", "resolved": "https://registry.npmjs.org/get-uri/-/get-uri-6.0.3.tgz", @@ -10340,7 +11056,6 @@ "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, "dependencies": { "define-properties": "^1.2.1", "gopd": "^1.0.1" @@ -10356,7 +11071,6 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz", "integrity": "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==", - "dev": true, "dependencies": { "get-intrinsic": "^1.1.3" }, @@ -10364,36 +11078,10 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/got": { - "version": "11.8.6", - "resolved": "https://registry.npmjs.org/got/-/got-11.8.6.tgz", - "integrity": "sha512-6tfZ91bOr7bOXnK7PRDCGBLa1H4U080YHNaAQ2KsMGlLEzRbk44nsZF2E1IeRc3vtJHPVbKCYgdFbaGO2ljd8g==", - "dev": true, - "dependencies": { - "@sindresorhus/is": "^4.0.0", - "@szmarczak/http-timer": "^4.0.5", - "@types/cacheable-request": "^6.0.1", - "@types/responselike": "^1.0.0", - "cacheable-lookup": "^5.0.3", - "cacheable-request": "^7.0.2", - "decompress-response": "^6.0.0", - "http2-wrapper": "^1.0.0-beta.5.2", - "lowercase-keys": "^2.0.0", - "p-cancelable": "^2.0.0", - "responselike": "^2.0.0" - }, - "engines": { - "node": ">=10.19.0" - }, - "funding": { - "url": "https://github.com/sindresorhus/got?sponsor=1" - } - }, "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==", - "dev": true + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==" }, "node_modules/graphemer": { "version": "1.4.0", @@ -10405,7 +11093,6 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.2.tgz", "integrity": "sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ==", - "dev": true, "funding": { "url": "https://github.com/sponsors/ljharb" } @@ -10423,7 +11110,6 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz", "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==", - "dev": true, "dependencies": { "es-define-property": "^1.0.0" }, @@ -10435,7 +11121,6 @@ "version": "1.0.3", "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.3.tgz", "integrity": "sha512-SJ1amZAJUiZS+PhsVLf5tGydlaVB8EdFpaSO4gmiUKUOxk8qzn5AIy4ZeJUmh22znIdk/uMAUT2pl3FxzVUH+Q==", - "dev": true, "engines": { "node": ">= 0.4" }, @@ -10447,7 +11132,6 @@ "version": "1.0.3", "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==", - "dev": true, "engines": { "node": ">= 0.4" }, @@ -10459,7 +11143,6 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", - "dev": true, "dependencies": { "has-symbols": "^1.0.3" }, @@ -10470,16 +11153,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/hash.js": { - "version": "1.1.7", - "resolved": "https://registry.npmjs.org/hash.js/-/hash.js-1.1.7.tgz", - "integrity": "sha512-taOaskGt4z4SOANNseOviYDvjEJinIkRgmp7LbKP2YTTmVxWBl87s/uzK9r+44BclBSp2X7K1hqeNfz9JbBeXA==", - "dev": true, - "dependencies": { - "inherits": "^2.0.3", - "minimalistic-assert": "^1.0.1" - } - }, "node_modules/hasha": { "version": "5.2.2", "resolved": "https://registry.npmjs.org/hasha/-/hasha-5.2.2.tgz", @@ -10525,21 +11198,16 @@ "he": "bin/he" } }, - "node_modules/header-case": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/header-case/-/header-case-2.0.4.tgz", - "integrity": "sha512-H/vuk5TEEVZwrR0lp2zed9OCo1uAILMlx0JEMgC26rzyJJ3N1v6XkwHHXJQdR2doSjcGPM6OKPYoJgf0plJ11Q==", - "dev": true, - "dependencies": { - "capital-case": "^1.0.4", - "tslib": "^2.0.3" - } + "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/hosted-git-info": { "version": "2.8.9", "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.9.tgz", - "integrity": "sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==", - "dev": true + "integrity": "sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==" }, "node_modules/html-encoding-sniffer": { "version": "4.0.0", @@ -10559,12 +11227,6 @@ "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==", "dev": true }, - "node_modules/http-cache-semantics": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.1.1.tgz", - "integrity": "sha512-er295DKPVsV82j5kw1Gjt+ADA/XYHsajl82cGNQG2eyoPkvgUhX+nDIyelzhIWbbsXP39EHcI6l5tYs2FYqYXQ==", - "dev": true - }, "node_modules/http-errors": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", @@ -10639,19 +11301,6 @@ "node": ">= 14" } }, - "node_modules/http2-wrapper": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/http2-wrapper/-/http2-wrapper-1.0.3.tgz", - "integrity": "sha512-V+23sDMr12Wnz7iTcDeJr3O6AIxlnvT/bmaAAAP/Xda35C90p9599p0F1eHR/N1KILWSoWVAiOMFjBBXaXSMxg==", - "dev": true, - "dependencies": { - "quick-lru": "^5.1.1", - "resolve-alpn": "^1.0.0" - }, - "engines": { - "node": ">=10.19.0" - } - }, "node_modules/https-proxy-agent": { "version": "7.0.4", "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.4.tgz", @@ -10808,8 +11457,7 @@ "node_modules/inherits": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", - "dev": true + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" }, "node_modules/ini": { "version": "3.0.1", @@ -10824,7 +11472,6 @@ "version": "1.0.7", "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.7.tgz", "integrity": "sha512-NGnrKwXzSms2qUUih/ILZ5JBqNTSa1+ZmP6flaIp6KmSElgE9qdndzS3cqjrDovwFdmwsGsLdeFgB6suw+1e9g==", - "dev": true, "dependencies": { "es-errors": "^1.3.0", "hasown": "^2.0.0", @@ -10897,7 +11544,6 @@ "version": "3.0.4", "resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.4.tgz", "integrity": "sha512-wcjaerHw0ydZwfhiKbXJWLDY8A7yV7KhjQOpb83hGgGfId/aQa4TOvwyzn2PuswW2gPCYEL/nEAiSVpdOj1lXw==", - "dev": true, "dependencies": { "call-bind": "^1.0.2", "get-intrinsic": "^1.2.1" @@ -10912,14 +11558,12 @@ "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 + "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==" }, "node_modules/is-bigint": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.0.4.tgz", "integrity": "sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg==", - "dev": true, "dependencies": { "has-bigints": "^1.0.1" }, @@ -10943,7 +11587,6 @@ "version": "1.1.2", "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.1.2.tgz", "integrity": "sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA==", - "dev": true, "dependencies": { "call-bind": "^1.0.2", "has-tostringtag": "^1.0.0" @@ -10974,7 +11617,6 @@ "version": "1.2.7", "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz", "integrity": "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==", - "dev": true, "engines": { "node": ">= 0.4" }, @@ -10997,7 +11639,6 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/is-data-view/-/is-data-view-1.0.1.tgz", "integrity": "sha512-AHkaJrsUVW6wq6JS8y3JnM/GJF/9cf+k20+iDzlSaJrinEo5+7vRiteOSwBhHRiAyQATN1AmY4hwzxJKPmYf+w==", - "dev": true, "dependencies": { "is-typed-array": "^1.1.13" }, @@ -11012,7 +11653,6 @@ "version": "1.0.5", "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.5.tgz", "integrity": "sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ==", - "dev": true, "dependencies": { "has-tostringtag": "^1.0.0" }, @@ -11099,7 +11739,6 @@ "version": "2.0.3", "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.3.tgz", "integrity": "sha512-5KoIu2Ngpyek75jXodFvnafB6DJgr3u8uuK0LEZJjrU19DrMD3EVERaR8sjz8CCGgpZvxPl9SuE1GMVPFHx1mw==", - "dev": true, "engines": { "node": ">= 0.4" }, @@ -11120,7 +11759,6 @@ "version": "1.0.7", "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.0.7.tgz", "integrity": "sha512-k1U0IRzLMo7ZlYIfzRu23Oh6MiIFasgpb9X76eqfFZAqwH44UI4KTBvBYIZ1dSL9ZzChTB9ShHfLkR4pdW5krQ==", - "dev": true, "dependencies": { "has-tostringtag": "^1.0.0" }, @@ -11168,7 +11806,6 @@ "version": "1.1.4", "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.4.tgz", "integrity": "sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==", - "dev": true, "dependencies": { "call-bind": "^1.0.2", "has-tostringtag": "^1.0.0" @@ -11196,7 +11833,6 @@ "version": "1.0.3", "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.3.tgz", "integrity": "sha512-nA2hv5XIhLR3uVzDDfCIknerhx8XUKnstuOERPNNIinXG7v9u+ohXF67vxm4TPTEPU6lm61ZkwP3c9PCB97rhg==", - "dev": true, "dependencies": { "call-bind": "^1.0.7" }, @@ -11223,7 +11859,6 @@ "version": "1.0.7", "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.7.tgz", "integrity": "sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg==", - "dev": true, "dependencies": { "has-tostringtag": "^1.0.0" }, @@ -11238,7 +11873,6 @@ "version": "1.0.4", "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.4.tgz", "integrity": "sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg==", - "dev": true, "dependencies": { "has-symbols": "^1.0.2" }, @@ -11253,7 +11887,6 @@ "version": "1.1.13", "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.13.tgz", "integrity": "sha512-uZ25/bUAlUY5fR4OKT4rZQEBrzQWYV9ZJYGGsUmEJ6thodVJ1HX64ePQ6Z0qPWP+m+Uq6e9UugrE38jeYsDSMw==", - "dev": true, "dependencies": { "which-typed-array": "^1.1.14" }, @@ -11304,7 +11937,6 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.0.2.tgz", "integrity": "sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ==", - "dev": true, "dependencies": { "call-bind": "^1.0.2" }, @@ -11366,8 +11998,7 @@ "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 + "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==" }, "node_modules/isbinaryfile": { "version": "4.0.10", @@ -11384,8 +12015,7 @@ "node_modules/isexe": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", - "dev": true + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==" }, "node_modules/istanbul-lib-coverage": { "version": "3.2.2", @@ -11666,6 +12296,15 @@ "jiti": "lib/jiti-cli.mjs" } }, + "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", + "engines": { + "node": ">=10" + } + }, "node_modules/jpeg-js": { "version": "0.4.4", "resolved": "https://registry.npmjs.org/jpeg-js/-/jpeg-js-0.4.4.tgz", @@ -11813,8 +12452,7 @@ "node_modules/json-parse-better-errors": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz", - "integrity": "sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==", - "dev": true + "integrity": "sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==" }, "node_modules/json-parse-even-better-errors": { "version": "2.3.1", @@ -11822,6 +12460,16 @@ "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", "dev": true }, + "node_modules/json-schema-ref-resolver": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-schema-ref-resolver/-/json-schema-ref-resolver-1.0.1.tgz", + "integrity": "sha512-EJAj1pgHc1hxF6vo2Z3s69fMjO1INq6eGHXZ8Z6wCQeldCuwxGK9Sxf4/cScGn3FZubCVUehfWtcDM/PLteCQw==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.3" + } + }, "node_modules/json-schema-traverse": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", @@ -12268,6 +12916,28 @@ "immediate": "~3.0.5" } }, + "node_modules/light-my-request": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/light-my-request/-/light-my-request-6.3.0.tgz", + "integrity": "sha512-bWTAPJmeWQH5suJNYwG0f5cs0p6ho9e6f1Ppoxv5qMosY+s9Ir2+ZLvvHcgA7VTDop4zl/NCHhOVVqU+kd++Ow==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "cookie": "^1.0.1", + "process-warning": "^4.0.0", + "set-cookie-parser": "^2.6.0" + } + }, + "node_modules/light-my-request/node_modules/cookie": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-1.0.1.tgz", + "integrity": "sha512-Xd8lFX4LM9QEEwxQpF9J9NTUh8pmdJO0cyRJhFiDoLTk2eH8FXlRv2IFGYVadZpqI3j8fhNrSdKCeYPxiAhLXw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + } + }, "node_modules/lighthouse": { "version": "12.1.0", "resolved": "https://registry.npmjs.org/lighthouse/-/lighthouse-12.1.0.tgz", @@ -12411,7 +13081,6 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-4.0.0.tgz", "integrity": "sha512-Kx8hMakjX03tiGTLAIdJ+lL0htKnXjEZN6hk/tozf/WOuYGdZBJrZ+rCJRbVCugsjB3jMLn9746NsQIf5VjBMw==", - "dev": true, "dependencies": { "graceful-fs": "^4.1.2", "parse-json": "^4.0.0", @@ -12567,24 +13236,6 @@ "get-func-name": "^2.0.1" } }, - "node_modules/lower-case": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/lower-case/-/lower-case-2.0.2.tgz", - "integrity": "sha512-7fm3l3NAF9WfN6W3JOmf5drwpVqX78JtoGJ3A6W0a6ZnldM41w2fV5D490psKFTpMds8TJse/eHLFFsNHHjHgg==", - "dev": true, - "dependencies": { - "tslib": "^2.0.3" - } - }, - "node_modules/lowercase-keys": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-2.0.0.tgz", - "integrity": "sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA==", - "dev": true, - "engines": { - "node": ">=8" - } - }, "node_modules/lru_map": { "version": "0.3.3", "resolved": "https://registry.npmjs.org/lru_map/-/lru_map-0.3.3.tgz", @@ -12862,16 +13513,19 @@ "version": "0.3.1", "resolved": "https://registry.npmjs.org/memorystream/-/memorystream-0.3.1.tgz", "integrity": "sha512-S3UwM3yj5mtUSEfP41UZmt/0SCoVYUcU1rkXv+BQ5Ig8ndL4sPoJNBUJERafdPb5jjHJGuMgytgKvKIf58XNBw==", - "dev": true, "engines": { "node": ">= 0.10.0" } }, "node_modules/merge-descriptors": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", - "integrity": "sha512-cCi6g3/Zr1iqQi6ySbseM1Xvooa98N0w31jzUYrXPX2xqObmFGHJ0tQ5u74H3mVh7wLouTseZyYIq39g8cNp1w==", - "dev": true + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.3.tgz", + "integrity": "sha512-gaNvAS7TZ897/rVaZ0nMtAyxNyi/pdbjbAwUpFQpN70GqnVfOiXpeUUMKRBmzXaSQ8DdTX4/0ms62r2K+hE6mQ==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } }, "node_modules/merge2": { "version": "1.4.1", @@ -12928,7 +13582,6 @@ "version": "1.52.0", "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", - "dev": true, "engines": { "node": ">= 0.6" } @@ -12937,7 +13590,6 @@ "version": "2.1.35", "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", - "dev": true, "dependencies": { "mime-db": "1.52.0" }, @@ -12954,21 +13606,6 @@ "node": ">=6" } }, - "node_modules/mimic-response": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-1.0.1.tgz", - "integrity": "sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ==", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/minimalistic-assert": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz", - "integrity": "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==", - "dev": true - }, "node_modules/minimatch": { "version": "9.0.5", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", @@ -12989,7 +13626,6 @@ "version": "1.2.8", "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", - "dev": true, "funding": { "url": "https://github.com/sponsors/ljharb" } @@ -13020,6 +13656,16 @@ "node": ">=10" } }, + "node_modules/mnemonist": { + "version": "0.39.8", + "resolved": "https://registry.npmjs.org/mnemonist/-/mnemonist-0.39.8.tgz", + "integrity": "sha512-vyWo2K3fjrUw8YeeZ1zF0fy6Mu59RHokURlld8ymdUPjMlD9EC9ov1/YPqTgqRvUN9nTr3Gqfz29LYAmu0PHPQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "obliterator": "^2.0.1" + } + }, "node_modules/mocha": { "version": "10.4.0", "resolved": "https://registry.npmjs.org/mocha/-/mocha-10.4.0.tgz", @@ -13236,7 +13882,6 @@ "version": "0.6.3", "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==", - "dev": true, "engines": { "node": ">= 0.6" } @@ -13274,8 +13919,7 @@ "node_modules/nice-try": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz", - "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==", - "dev": true + "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==" }, "node_modules/nightwatch": { "version": "3.6.3", @@ -13694,16 +14338,6 @@ "integrity": "sha512-GQX3SSMokngb36+whdpRXE+3f9V8UzyAorlYvOGx87ufGHehNTn5lCxrKtLyZ4Yl/wEKnNnr98ZzOwwDZV5ogw==", "dev": true }, - "node_modules/no-case": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/no-case/-/no-case-3.0.4.tgz", - "integrity": "sha512-fgAN3jGAh+RoxUGZHTSOLJIqUc2wmoBwGR4tbpNAKmmovFoWq0OdRkb0VkldReO2a2iBT/OEulG9XSUc10r3zg==", - "dev": true, - "dependencies": { - "lower-case": "^2.0.2", - "tslib": "^2.0.3" - } - }, "node_modules/node-addon-api": { "version": "3.2.1", "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-3.2.1.tgz", @@ -13761,7 +14395,7 @@ "version": "4.8.1", "resolved": "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-4.8.1.tgz", "integrity": "sha512-OSs33Z9yWr148JZcbZd5WiAXhh/n9z8TxQcdMhIOlpN9AhWpLfvVFO73+m77bBABQMaY9XSvIa+qk0jlI7Gcaw==", - "dev": true, + "devOptional": true, "bin": { "node-gyp-build": "bin.js", "node-gyp-build-optional": "optional.js", @@ -13790,7 +14424,6 @@ "version": "2.5.0", "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz", "integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==", - "dev": true, "dependencies": { "hosted-git-info": "^2.1.4", "resolve": "^1.10.0", @@ -13802,7 +14435,6 @@ "version": "5.7.2", "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", - "dev": true, "bin": { "semver": "bin/semver" } @@ -13816,23 +14448,11 @@ "node": ">=0.10.0" } }, - "node_modules/normalize-url": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-6.1.0.tgz", - "integrity": "sha512-DlL+XwOy3NxAQ8xuC0okPgK46iuVNAK01YN7RueYBqqFeGsBjV9XmCAzAdgt+667bCl5kPh9EqKKDwnaPG1I7A==", - "dev": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/npm-run-all": { "version": "4.1.5", "resolved": "https://registry.npmjs.org/npm-run-all/-/npm-run-all-4.1.5.tgz", "integrity": "sha512-Oo82gJDAVcaMdi3nuoKFavkIHBRVqQ1qvMb+9LHk/cF4P6B2m8aP04hGf7oL6wZ9BuGwX1onlLhpuoofSyoQDQ==", - "dev": true, + "license": "MIT", "dependencies": { "ansi-styles": "^3.2.1", "chalk": "^2.4.1", @@ -13857,7 +14477,6 @@ "version": "3.2.1", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dev": true, "dependencies": { "color-convert": "^1.9.0" }, @@ -13869,7 +14488,6 @@ "version": "1.1.11", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "dev": true, "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" @@ -13879,7 +14497,6 @@ "version": "2.4.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "dev": true, "dependencies": { "ansi-styles": "^3.2.1", "escape-string-regexp": "^1.0.5", @@ -13893,7 +14510,6 @@ "version": "1.9.3", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "dev": true, "dependencies": { "color-name": "1.1.3" } @@ -13901,14 +14517,12 @@ "node_modules/npm-run-all/node_modules/color-name": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", - "dev": true + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==" }, "node_modules/npm-run-all/node_modules/cross-spawn": { "version": "6.0.5", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz", "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==", - "dev": true, "dependencies": { "nice-try": "^1.0.4", "path-key": "^2.0.1", @@ -13924,7 +14538,6 @@ "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, "engines": { "node": ">=0.8.0" } @@ -13933,7 +14546,6 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", - "dev": true, "engines": { "node": ">=4" } @@ -13942,7 +14554,6 @@ "version": "3.1.2", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dev": true, "dependencies": { "brace-expansion": "^1.1.7" }, @@ -13954,7 +14565,6 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz", "integrity": "sha512-fEHGKCSmUSDPv4uoj8AlD+joPlq3peND+HRYyxFz4KPw4z926S/b8rIuFs2FYJg3BwsxJf6A9/3eIdLaYC+9Dw==", - "dev": true, "engines": { "node": ">=4" } @@ -13963,7 +14573,6 @@ "version": "5.7.2", "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", - "dev": true, "bin": { "semver": "bin/semver" } @@ -13972,7 +14581,6 @@ "version": "1.2.0", "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz", "integrity": "sha512-EV3L1+UQWGor21OmnvojK36mhg+TyIKDh3iFBKBohr5xeXIhNBcx8oWdgkTEEQ+BEFFYdLRuqMfd5L84N1V5Vg==", - "dev": true, "dependencies": { "shebang-regex": "^1.0.0" }, @@ -13984,7 +14592,6 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz", "integrity": "sha512-wpoSFAxys6b2a2wHZ1XpDSgD7N9iVjg29Ph9uV/uaP9Ex/KXlkTZTeddxDPSYQpgvzKLGJke2UU0AzoGCjNIvQ==", - "dev": true, "engines": { "node": ">=0.10.0" } @@ -13993,7 +14600,6 @@ "version": "5.5.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "dev": true, "dependencies": { "has-flag": "^3.0.0" }, @@ -14005,7 +14611,6 @@ "version": "1.3.1", "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", - "dev": true, "dependencies": { "isexe": "^2.0.0" }, @@ -14305,7 +14910,6 @@ "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, "engines": { "node": ">=0.10.0" } @@ -14314,7 +14918,6 @@ "version": "1.13.1", "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.1.tgz", "integrity": "sha512-5qoj1RUiKOMsCCNLV1CBiPYE10sziTsnmNxkAI/rZhiD63CF7IqdFGC/XzjWjpSgLf0LxXX3bDFIh0E18f6UhQ==", - "dev": true, "funding": { "url": "https://github.com/sponsors/ljharb" } @@ -14339,7 +14942,6 @@ "version": "1.1.1", "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", - "dev": true, "engines": { "node": ">= 0.4" } @@ -14348,7 +14950,6 @@ "version": "4.1.5", "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.5.tgz", "integrity": "sha512-byy+U7gp+FVwmyzKPYhW2h5l3crpmGsxl7X2s8y43IgxvG4g3QZ6CffDtsNQy1WsmZpQbO+ybo0AlW7TY6DcBQ==", - "dev": true, "dependencies": { "call-bind": "^1.0.5", "define-properties": "^1.2.1", @@ -14362,6 +14963,22 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/obliterator": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/obliterator/-/obliterator-2.0.4.tgz", + "integrity": "sha512-lgHwxlxV1qIg1Eap7LgIeoBWIMFibOjbrYPIPJZcI1mmGAI2m3lNYpK12Y+GBdPQ0U1hRwSord7GIaawz962qQ==", + "dev": true, + "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", @@ -14379,6 +14996,7 @@ "resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.0.2.tgz", "integrity": "sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA==", "dev": true, + "license": "MIT", "engines": { "node": ">= 0.8" } @@ -14387,7 +15005,6 @@ "version": "1.4.0", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", - "dev": true, "dependencies": { "wrappy": "1" } @@ -14464,15 +15081,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/p-cancelable": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-2.1.1.tgz", - "integrity": "sha512-BZOr3nRQHOntUjTrH8+Lh54smKHoHyur8We1V8DSMVrl5A2malOOwuJRnKRDjSnkoeBh4at6BwEnb5I7Jl31wg==", - "dev": true, - "engines": { - "node": ">=8" - } - }, "node_modules/p-limit": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", @@ -14571,22 +15179,19 @@ "node": ">=8" } }, + "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/pako": { "version": "1.0.11", "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz", "integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==", "dev": true }, - "node_modules/param-case": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/param-case/-/param-case-3.0.4.tgz", - "integrity": "sha512-RXlj7zCYokReqWpOPH9oYivUzLYZ5vAPIfEmCTNViosC78F8F0H9y7T7gG2M39ymgutxF5gcFEsyZQSph9Bp3A==", - "dev": true, - "dependencies": { - "dot-case": "^3.0.4", - "tslib": "^2.0.3" - } - }, "node_modules/parent-module": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", @@ -14609,7 +15214,6 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz", "integrity": "sha512-aOIos8bujGN93/8Ox/jPLh7RwVnPEysynVFE+fQZyg6jKELEHwzgKdLRFHUgXJL6kylijVSBC4BvN9OmsB48Rw==", - "dev": true, "dependencies": { "error-ex": "^1.3.1", "json-parse-better-errors": "^1.0.1" @@ -14639,26 +15243,6 @@ "node": ">= 0.8" } }, - "node_modules/pascal-case": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/pascal-case/-/pascal-case-3.1.2.tgz", - "integrity": "sha512-uWlGT3YSnK9x3BQJaOdcZwrnV6hPpd8jFH1/ucpiLRPh/2zCVJKS19E4GvYHvaCcACn3foXZ0cLB9Wrx1KGe5g==", - "dev": true, - "dependencies": { - "no-case": "^3.0.4", - "tslib": "^2.0.3" - } - }, - "node_modules/path-case": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/path-case/-/path-case-3.0.4.tgz", - "integrity": "sha512-qO4qCFjXqVTrcbPt/hQfhTQ+VhFsqNKOPtytgNKkKxSoEp3XPUQ8ObFuePylOIok5gjn69ry8XiULxCwot3Wfg==", - "dev": true, - "dependencies": { - "dot-case": "^3.0.4", - "tslib": "^2.0.3" - } - }, "node_modules/path-exists": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", @@ -14717,10 +15301,11 @@ } }, "node_modules/path-to-regexp": { - "version": "0.1.7", - "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", - "integrity": "sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ==", - "dev": true + "version": "0.1.10", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.10.tgz", + "integrity": "sha512-7lf7qcQidTku0Gu3YDPc8DJ1q7OOucfa/BSsIwjuh56VU7katFvuM8hULfkwB3Fns/rsVF7PwPKVw1sl5KQS9w==", + "dev": true, + "license": "MIT" }, "node_modules/pathval": { "version": "1.1.1", @@ -14760,7 +15345,6 @@ "version": "0.3.1", "resolved": "https://registry.npmjs.org/pidtree/-/pidtree-0.3.1.tgz", "integrity": "sha512-qQbW94hLHEqCg7nhby4yRC7G2+jYHY4Rguc2bjw7Uug4GIJuu1tvf2uHaZv5Q8zdt+WKJ6qK1FOI6amaWUo5FA==", - "dev": true, "bin": { "pidtree": "bin/pidtree.js" }, @@ -14772,11 +15356,73 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", "integrity": "sha512-C3FsVNH1udSEX48gGX1xfvwTWfsYWj5U+8/uK15BGzIGrKoUpghX8hWZwa/OFnakBiiVNmBvemTJR5mcy7iPcg==", - "dev": true, "engines": { "node": ">=4" } }, + "node_modules/pino": { + "version": "9.5.0", + "resolved": "https://registry.npmjs.org/pino/-/pino-9.5.0.tgz", + "integrity": "sha512-xSEmD4pLnV54t0NOUN16yCl7RIB1c5UUOse5HSyEXtBp+FgFQyPeDutc+Q2ZO7/22vImV7VfEjH/1zV2QuqvYw==", + "dev": true, + "license": "MIT", + "dependencies": { + "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", + "process-warning": "^4.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" + } + }, + "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.0.0", + "resolved": "https://registry.npmjs.org/pino-pretty/-/pino-pretty-13.0.0.tgz", + "integrity": "sha512-cQBBIVG3YajgoUjo1FdKVRX6t9XPxwB9lcNJVD5GCnNM4Y6T12YYx8c6zEejxQsU0wrg9TwmDulcE9LR7qcJqA==", + "license": "MIT", + "dependencies": { + "colorette": "^2.0.7", + "dateformat": "^4.6.3", + "fast-copy": "^3.0.2", + "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": "^2.0.0", + "pump": "^3.0.0", + "secure-json-parse": "^2.4.0", + "sonic-boom": "^4.0.1", + "strip-json-comments": "^3.1.1" + }, + "bin": { + "pino-pretty": "bin.js" + } + }, + "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==", + "dev": true, + "license": "MIT" + }, "node_modules/piscina": { "version": "4.6.0", "resolved": "https://registry.npmjs.org/piscina/-/piscina-4.6.0.tgz", @@ -14850,11 +15496,57 @@ "node": ">=8" } }, + "node_modules/playwright": { + "version": "1.48.2", + "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.48.2.tgz", + "integrity": "sha512-NjYvYgp4BPmiwfe31j4gHLa3J7bD2WiBz8Lk2RoSsmX38SVIARZ18VYjxLjAcDsAhA+F4iSEXTSGgjua0rrlgQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "playwright-core": "1.48.2" + }, + "bin": { + "playwright": "cli.js" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "fsevents": "2.3.2" + } + }, + "node_modules/playwright-core": { + "version": "1.48.2", + "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.48.2.tgz", + "integrity": "sha512-sjjw+qrLFlriJo64du+EK0kJgZzoQPsabGF4lBvsid+3CNIZIYLgnMj9V6JY5VhM2Peh20DJWIVpVljLLnlawA==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "playwright-core": "cli.js" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/playwright/node_modules/fsevents": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", + "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, "node_modules/possible-typed-array-names": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/possible-typed-array-names/-/possible-typed-array-names-1.0.0.tgz", "integrity": "sha512-d7Uw+eZoloe0EHDIYoe+bQ5WXnGMOpmiZFTuMWCwpjzzkL2nTjcKiAk4hh8TjnGye2TwWOk3UXucZ+3rbmBa8Q==", - "dev": true, "engines": { "node": ">= 0.4" } @@ -14915,6 +15607,13 @@ "node": ">=8" } }, + "node_modules/process-warning": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/process-warning/-/process-warning-4.0.0.tgz", + "integrity": "sha512-/MyYDxttz7DfGMMHiysAsFE4qF+pQYAA8ziO/3NcRVrQ5fSk+Mns4QZA/oRPFzvcqNoVJXQNWNAsdwBXLUkQKw==", + "dev": true, + "license": "MIT" + }, "node_modules/progress": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz", @@ -15032,7 +15731,6 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", - "dev": true, "dependencies": { "end-of-stream": "^1.1.0", "once": "^1.3.1" @@ -15113,12 +15811,13 @@ } }, "node_modules/qs": { - "version": "6.11.0", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.11.0.tgz", - "integrity": "sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q==", + "version": "6.13.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.13.0.tgz", + "integrity": "sha512-+38qI9SOr8tfZ4QmJNplMUxqjbe7LKvvZgWdExBOmd+egZTtjLB67Gu0HRX3u/XOq7UU2Nx6nsjvS16Z9uwfpg==", "dev": true, + "license": "BSD-3-Clause", "dependencies": { - "side-channel": "^1.0.4" + "side-channel": "^1.0.6" }, "engines": { "node": ">=0.6" @@ -15127,24 +15826,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/query-string": { - "version": "7.1.3", - "resolved": "https://registry.npmjs.org/query-string/-/query-string-7.1.3.tgz", - "integrity": "sha512-hh2WYhq4fi8+b+/2Kg9CEge4fDPvHS534aOOvOZeQ3+Vf2mCFsaFBYj0i+iXcAq6I9Vzp5fjMFBlONvayDC1qg==", - "dev": true, - "dependencies": { - "decode-uri-component": "^0.2.2", - "filter-obj": "^1.1.0", - "split-on-first": "^1.0.0", - "strict-uri-encode": "^2.0.0" - }, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/querystringify": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/querystringify/-/querystringify-2.2.0.tgz", @@ -15178,17 +15859,12 @@ "integrity": "sha512-kJt5qhMxoszgU/62PLP1CJytzd2NKetjSRnyuj31fDd3Rlcz3fzlFdFLD1SItunPwyqEOkca6GbV612BWfaBag==", "dev": true }, - "node_modules/quick-lru": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-5.1.1.tgz", - "integrity": "sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA==", + "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==", "dev": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } + "license": "MIT" }, "node_modules/randombytes": { "version": "2.1.0", @@ -15227,7 +15903,6 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-3.0.0.tgz", "integrity": "sha512-BLq/cCO9two+lBgiTYNqD6GdtK8s4NpaWrl6/rCO9w0TUS8oJl7cmToOZfRYllKTISY6nt1U7jQ53brmKqY6BA==", - "dev": true, "dependencies": { "load-json-file": "^4.0.0", "normalize-package-data": "^2.3.2", @@ -15241,7 +15916,6 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/path-type/-/path-type-3.0.0.tgz", "integrity": "sha512-T2ZUsdZFHgA3u4e5PfPbjd7HDDpxPnQb5jN0SrDsjNSuVXHJqtwTnWqG0B1jZrgmJ/7lj1EmVIByWt1gxGkWvg==", - "dev": true, "dependencies": { "pify": "^3.0.0" }, @@ -15253,7 +15927,6 @@ "version": "3.6.2", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", - "dev": true, "dependencies": { "inherits": "^2.0.3", "string_decoder": "^1.1.1", @@ -15296,6 +15969,16 @@ "node": ">=8.10.0" } }, + "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==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 12.13.0" + } + }, "node_modules/regenerate": { "version": "1.4.2", "resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.4.2.tgz", @@ -15332,7 +16015,6 @@ "version": "1.5.2", "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.2.tgz", "integrity": "sha512-NcDiDkTLuPR+++OCKB0nWafEmhg/Da8aUPLPMQbK+bxKKCm1/S5he+AqYa4PlMCVBalb4/yxIRub6qkEx5yJbw==", - "dev": true, "dependencies": { "call-bind": "^1.0.6", "define-properties": "^1.2.1", @@ -15464,12 +16146,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/resolve-alpn": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/resolve-alpn/-/resolve-alpn-1.2.1.tgz", - "integrity": "sha512-0a1F4l73/ZFZOakJnQ3FvkJ2+gSTQWz/r2KE5OdDY0TxPm5h4GkqkWWfM47T7HsbnOtcJVEF4epCVy6u7Q3K+g==", - "dev": true - }, "node_modules/resolve-from": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", @@ -15479,16 +16155,13 @@ "node": ">=4" } }, - "node_modules/responselike": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/responselike/-/responselike-2.0.1.tgz", - "integrity": "sha512-4gl03wn3hj1HP3yzgdI7d3lCkF95F21Pz4BPGvKHinyQzALR5CapwC8yIi0Rh58DEMQ/SguC03wFj2k0M/mHhw==", - "dev": true, - "dependencies": { - "lowercase-keys": "^2.0.0" - }, + "node_modules/resolve-pkg-maps": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/resolve-pkg-maps/-/resolve-pkg-maps-1.0.0.tgz", + "integrity": "sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==", + "license": "MIT", "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "url": "https://github.com/privatenumber/resolve-pkg-maps?sponsor=1" } }, "node_modules/restore-cursor": { @@ -15504,6 +16177,16 @@ "node": ">=8" } }, + "node_modules/ret": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/ret/-/ret-0.5.0.tgz", + "integrity": "sha512-I1XxrZSQ+oErkRR4jYbAyEEu2I0avBvvMM5JN+6EBprOGRCs63ENqZ3vjavq8fBw2+62G5LF5XelKwuJpcvcxw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + } + }, "node_modules/reusify": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", @@ -15707,7 +16390,6 @@ "version": "1.1.2", "resolved": "https://registry.npmjs.org/safe-array-concat/-/safe-array-concat-1.1.2.tgz", "integrity": "sha512-vj6RsCsWBCf19jIeHEfkRMw8DPiBb+DMXklQ/1SGDHOMlHdPUkZXFQ2YdplS23zESTijAcurb1aSgJA3AgMu1Q==", - "dev": true, "dependencies": { "call-bind": "^1.0.7", "get-intrinsic": "^1.2.4", @@ -15725,7 +16407,6 @@ "version": "5.2.1", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", - "dev": true, "funding": [ { "type": "github", @@ -15745,7 +16426,6 @@ "version": "1.0.3", "resolved": "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.0.3.tgz", "integrity": "sha512-CdASjNJPvRa7roO6Ra/gLYBTzYzzPyyBXxIMdGW3USQLyjWEls2RgW5UBTXaQVp+OrpeCK3bLem8smtmheoRuw==", - "dev": true, "dependencies": { "call-bind": "^1.0.6", "es-errors": "^1.3.0", @@ -15758,31 +16438,32 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/safe-regex2": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/safe-regex2/-/safe-regex2-4.0.0.tgz", + "integrity": "sha512-Hvjfv25jPDVr3U+4LDzBuZPPOymELG3PYcSk5hcevooo1yxxamQL/bHs/GrEPGmMoMEwRrHVGiCA1pXi97B8Ew==", + "dev": true, + "license": "MIT", + "dependencies": { + "ret": "~0.5.0" + } + }, + "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==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + } + }, "node_modules/safer-buffer": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", "dev": true }, - "node_modules/saucelabs": { - "version": "7.5.0", - "resolved": "https://registry.npmjs.org/saucelabs/-/saucelabs-7.5.0.tgz", - "integrity": "sha512-wq89BtE7xb4ns7ApbgAshaUgXHlPoseytPTNwaVQNPwAaD+0klYpBrsCy/Lj77EJ+kf/vKvX1tjhRT67eDyCXg==", - "dev": true, - "dependencies": { - "change-case": "^4.1.2", - "compressing": "^1.10.0", - "form-data": "^4.0.0", - "got": "^11.8.6", - "hash.js": "^1.1.7", - "query-string": "^7.1.3", - "tunnel": "^0.0.6", - "yargs": "^17.2.1" - }, - "bin": { - "sl": "bin/sl" - } - }, "node_modules/saxes": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/saxes/-/saxes-6.0.0.tgz", @@ -15795,6 +16476,12 @@ "node": ">=v12.22.7" } }, + "node_modules/secure-json-parse": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/secure-json-parse/-/secure-json-parse-2.7.0.tgz", + "integrity": "sha512-6aU+Rwsezw7VR8/nyvKTx8QpWH9FrcYiXXlqC4z5d5XQBDRqtbfsRjnwGyqbi3gddNtWHuEk9OANUotL26qKUw==", + "license": "BSD-3-Clause" + }, "node_modules/selenium-webdriver": { "version": "4.21.0", "resolved": "https://registry.npmjs.org/selenium-webdriver/-/selenium-webdriver-4.21.0.tgz", @@ -15830,10 +16517,11 @@ } }, "node_modules/send": { - "version": "0.18.0", - "resolved": "https://registry.npmjs.org/send/-/send-0.18.0.tgz", - "integrity": "sha512-qqWzuOjSFOuqPjFe4NOsMLafToQQwBSOEpS+FwEt3A2V3vKubTquT3vmLTQpFgMXp8AlFWFuP1qKaJZOtPpVXg==", + "version": "0.19.0", + "resolved": "https://registry.npmjs.org/send/-/send-0.19.0.tgz", + "integrity": "sha512-dW41u5VfLXu8SJh5bwRmyYUbAoSB3c9uQh6L8h/KtsFREPWpbX1lrljJo186Jc4nmci/sGUZ9a0a0J2zgfq2hw==", "dev": true, + "license": "MIT", "dependencies": { "debug": "2.6.9", "depd": "2.0.0", @@ -15858,6 +16546,7 @@ "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", "dev": true, + "license": "MIT", "dependencies": { "ms": "2.0.0" } @@ -15866,13 +16555,15 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/send/node_modules/mime": { "version": "1.6.0", "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", "dev": true, + "license": "MIT", "bin": { "mime": "cli.js" }, @@ -15884,18 +16575,8 @@ "version": "2.1.3", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "dev": true - }, - "node_modules/sentence-case": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/sentence-case/-/sentence-case-3.0.4.tgz", - "integrity": "sha512-8LS0JInaQMCRoQ7YUytAo/xUu5W2XnQxV2HI/6uM6U7CITS1RqPElr30V6uIqyMKM9lJGRVFy5/4CuzcixNYSg==", "dev": true, - "dependencies": { - "no-case": "^3.0.4", - "tslib": "^2.0.3", - "upper-case-first": "^2.0.2" - } + "license": "MIT" }, "node_modules/serialize-javascript": { "version": "6.0.2", @@ -15907,31 +16588,48 @@ } }, "node_modules/serve-static": { - "version": "1.15.0", - "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.15.0.tgz", - "integrity": "sha512-XGuRDNjXUijsUL0vl6nSD7cwURuzEgglbOaFuZM9g3kwDXOWVTck0jLzjPzGD+TazWbboZYu52/9/XPdUgne9g==", + "version": "1.16.2", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.16.2.tgz", + "integrity": "sha512-VqpjJZKadQB/PEbEwvFdO43Ax5dFBZ2UECszz8bQ7pi7wt//PWe1P6MN7eCnjsatYtBT6EuiClbjSWP2WrIoTw==", "dev": true, + "license": "MIT", "dependencies": { - "encodeurl": "~1.0.2", + "encodeurl": "~2.0.0", "escape-html": "~1.0.3", "parseurl": "~1.3.3", - "send": "0.18.0" + "send": "0.19.0" }, "engines": { "node": ">= 0.8.0" } }, + "node_modules/serve-static/node_modules/encodeurl": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz", + "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, "node_modules/set-blocking": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", "integrity": "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==", "dev": true }, + "node_modules/set-cookie-parser": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/set-cookie-parser/-/set-cookie-parser-2.7.1.tgz", + "integrity": "sha512-IOc8uWeOZgnb3ptbCURJWNjWUPcO3ZnTTdzsurqERrP6nPyv+paC55vJM0LpOlT2ne+Ix+9+CRG1MNLlyZ4GjQ==", + "dev": true, + "license": "MIT" + }, "node_modules/set-function-length": { "version": "1.2.2", "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz", "integrity": "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==", - "dev": true, "dependencies": { "define-data-property": "^1.1.4", "es-errors": "^1.3.0", @@ -15948,7 +16646,6 @@ "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, "dependencies": { "define-data-property": "^1.1.4", "es-errors": "^1.3.0", @@ -15996,7 +16693,6 @@ "version": "1.8.1", "resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.8.1.tgz", "integrity": "sha512-6j1W9l1iAs/4xYBI1SYOVZyFcCis9b4KCLQ8fgAGG07QvzaRLVVRQvAy85yNmmZSjYjg4MWh4gNvlPujU/5LpA==", - "dev": true, "funding": { "url": "https://github.com/sponsors/ljharb" } @@ -16010,7 +16706,6 @@ "version": "1.0.6", "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.6.tgz", "integrity": "sha512-fDW/EZ6Q9RiO8eFG8Hj+7u/oW+XrPTIChwCOM2+th2A6OblDtYYIpve9m+KvI9Z4C9qSEXlaGR6bTEYHReuglA==", - "dev": true, "dependencies": { "call-bind": "^1.0.7", "es-errors": "^1.3.0", @@ -16126,27 +16821,17 @@ "integrity": "sha512-g6T+p7QO8npa+/hNx9ohv1E5pVCmWrVCUzUXJyLdMmftX6ER0oiWY/w9knEonLpnOp6b6FenKnMfR8gqwWdwig==", "dev": true }, - "node_modules/snake-case": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/snake-case/-/snake-case-3.0.4.tgz", - "integrity": "sha512-LAOh4z89bGQvl9pFfNF8V146i7o7/CqFPbqzYgP+yYzDIDeS9HaNFtXABamRW+AQzEVODcvE79ljJ+8a9YSdMg==", - "dev": true, - "dependencies": { - "dot-case": "^3.0.4", - "tslib": "^2.0.3" - } - }, "node_modules/socket.io": { - "version": "4.7.5", - "resolved": "https://registry.npmjs.org/socket.io/-/socket.io-4.7.5.tgz", - "integrity": "sha512-DmeAkF6cwM9jSfmp6Dr/5/mfMwb5Z5qRrSXLpo3Fq5SqyU8CMF15jIN4ZhfSwu35ksM1qmHZDQ/DK5XTccSTvA==", - "dev": true, + "version": "4.8.1", + "resolved": "https://registry.npmjs.org/socket.io/-/socket.io-4.8.1.tgz", + "integrity": "sha512-oZ7iUCxph8WYRHHcjBEc9unw3adt5CmSNlppj/5Q4k2RIrhl8Z5yY2Xr4j9zj0+wzVZ0bxmYoGSzKJnRl6A4yg==", + "license": "MIT", "dependencies": { "accepts": "~1.3.4", "base64id": "~2.0.0", "cors": "~2.8.5", "debug": "~4.3.2", - "engine.io": "~6.5.2", + "engine.io": "~6.6.0", "socket.io-adapter": "~2.5.2", "socket.io-parser": "~4.2.4" }, @@ -16158,7 +16843,6 @@ "version": "2.5.5", "resolved": "https://registry.npmjs.org/socket.io-adapter/-/socket.io-adapter-2.5.5.tgz", "integrity": "sha512-eLDQas5dzPgOWCk9GuuJC2lBqItuhKI4uxGgo9aIV7MYbk2h9Q6uULEh8WBzThoI7l+qU9Ast9fVUmkqPP9wYg==", - "dev": true, "dependencies": { "debug": "~4.3.4", "ws": "~8.17.1" @@ -16183,7 +16867,6 @@ "version": "4.2.4", "resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-4.2.4.tgz", "integrity": "sha512-/GbIKmo8ioc+NIWIhwdecY0ge+qVBSMdgxGygevmdHj24bsfgtCmcUUcQ5ZzcylGFHsN3k4HB4Cgkl96KVnuew==", - "dev": true, "dependencies": { "@socket.io/component-emitter": "~3.1.0", "debug": "~4.3.1" @@ -16220,6 +16903,15 @@ "node": ">= 14" } }, + "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" + } + }, "node_modules/source-map": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", @@ -16293,7 +16985,6 @@ "version": "3.2.0", "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.2.0.tgz", "integrity": "sha512-kN9dJbvnySHULIluDHy32WHRUu3Og7B9sbY7tsFLctQkIqnMh3hErYgdMjTYuqmcXX+lK5T1lnUt3G7zNswmZA==", - "dev": true, "dependencies": { "spdx-expression-parse": "^3.0.0", "spdx-license-ids": "^3.0.0" @@ -16302,14 +16993,12 @@ "node_modules/spdx-exceptions": { "version": "2.5.0", "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.5.0.tgz", - "integrity": "sha512-PiU42r+xO4UbUS1buo3LPJkjlO7430Xn5SVAhdpzzsPHsjbYVflnnFdATgabnLude+Cqu25p6N+g2lw/PFsa4w==", - "dev": true + "integrity": "sha512-PiU42r+xO4UbUS1buo3LPJkjlO7430Xn5SVAhdpzzsPHsjbYVflnnFdATgabnLude+Cqu25p6N+g2lw/PFsa4w==" }, "node_modules/spdx-expression-parse": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz", "integrity": "sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==", - "dev": true, "dependencies": { "spdx-exceptions": "^2.1.0", "spdx-license-ids": "^3.0.0" @@ -16318,8 +17007,7 @@ "node_modules/spdx-license-ids": { "version": "3.0.18", "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.18.tgz", - "integrity": "sha512-xxRs31BqRYHwiMzudOrpSiHtZ8i/GeionCBDSilhYRj+9gIcI8wCZTlXZKu9vZIVqViP3dcp9qE5G6AlIaD+TQ==", - "dev": true + "integrity": "sha512-xxRs31BqRYHwiMzudOrpSiHtZ8i/GeionCBDSilhYRj+9gIcI8wCZTlXZKu9vZIVqViP3dcp9qE5G6AlIaD+TQ==" }, "node_modules/speedline-core": { "version": "1.4.3", @@ -16335,13 +17023,13 @@ "node": ">=8.0" } }, - "node_modules/split-on-first": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/split-on-first/-/split-on-first-1.1.0.tgz", - "integrity": "sha512-43ZssAJaMusuKWL8sKUBQXHWOpq8d6CfN/u1p4gUzfJkM05C8rxTmYrkIPTXapZpORA6LkkzcUulJ8FqA7Uudw==", - "dev": true, + "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": ">=6" + "node": ">= 10.x" } }, "node_modules/sprintf-js": { @@ -16401,14 +17089,11 @@ "stubs": "^3.0.0" } }, - "node_modules/streamifier": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/streamifier/-/streamifier-0.1.1.tgz", - "integrity": "sha512-zDgl+muIlWzXNsXeyUfOk9dChMjlpkq0DRsxujtYPgyJ676yQ8jEm6zzaaWHFDg5BNcLuif0eD2MTyJdZqXpdg==", - "dev": true, - "engines": { - "node": ">=0.10" - } + "node_modules/stream-shift": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/stream-shift/-/stream-shift-1.0.3.tgz", + "integrity": "sha512-76ORR0DO1o1hlKwTbi/DM3EXWGf3ZJYO8cXX5RJwnul2DEg2oyoZyjLNoQM8WsvZiFKCRfC1O0J7iCvie3RZmQ==", + "license": "MIT" }, "node_modules/streamroller": { "version": "3.1.5", @@ -16470,20 +17155,10 @@ "bare-events": "^2.2.0" } }, - "node_modules/strict-uri-encode": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/strict-uri-encode/-/strict-uri-encode-2.0.0.tgz", - "integrity": "sha512-QwiXZgpRcKkhTj2Scnn++4PKtWsH0kpzZ62L2R6c/LUVYv7hVnZqcg2+sMuT6R7Jusu1vviK/MFsu6kNJfWlEQ==", - "dev": true, - "engines": { - "node": ">=4" - } - }, "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, "dependencies": { "safe-buffer": "~5.2.0" } @@ -16521,7 +17196,6 @@ "version": "3.1.6", "resolved": "https://registry.npmjs.org/string.prototype.padend/-/string.prototype.padend-3.1.6.tgz", "integrity": "sha512-XZpspuSB7vJWhvJc9DLSlrXl1mcA2BdoY5jjnS135ydXqLoqhs96JjDtCkjJEQHvfqZIp9hBuBMgI589peyx9Q==", - "dev": true, "dependencies": { "call-bind": "^1.0.7", "define-properties": "^1.2.1", @@ -16539,7 +17213,6 @@ "version": "1.2.9", "resolved": "https://registry.npmjs.org/string.prototype.trim/-/string.prototype.trim-1.2.9.tgz", "integrity": "sha512-klHuCNxiMZ8MlsOihJhJEBJAiMVqU3Z2nEXWfWnIqjN0gEFS9J9+IxKozWWtQGcgoa1WUZzLjKPTr4ZHNFTFxw==", - "dev": true, "dependencies": { "call-bind": "^1.0.7", "define-properties": "^1.2.1", @@ -16557,7 +17230,6 @@ "version": "1.0.8", "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.8.tgz", "integrity": "sha512-p73uL5VCHCO2BZZ6krwwQE3kCzM7NKmis8S//xEC6fQonchbum4eP6kR4DLEjQFO3Wnj3Fuo8NM0kOSjVdHjZQ==", - "dev": true, "dependencies": { "call-bind": "^1.0.7", "define-properties": "^1.2.1", @@ -16571,7 +17243,6 @@ "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, "dependencies": { "call-bind": "^1.0.7", "define-properties": "^1.2.1", @@ -16613,7 +17284,6 @@ "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, "engines": { "node": ">=4" } @@ -16622,7 +17292,6 @@ "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, "engines": { "node": ">=8" }, @@ -16958,6 +17627,16 @@ "integrity": "sha512-imE6hXZyaCeGinGFCvpWsv0oelsEaufSG39qYBQhp3urGq4OLOtsuEddf3XgKxmAAczBD/I1Tnp8L3gJ3ksTuQ==", "dev": true }, + "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==", + "dev": true, + "license": "MIT", + "dependencies": { + "real-require": "^0.2.0" + } + }, "node_modules/through": { "version": "2.3.8", "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", @@ -17030,12 +17709,6 @@ "node": ">=14.14" } }, - "node_modules/to-buffer": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/to-buffer/-/to-buffer-1.1.1.tgz", - "integrity": "sha512-lx9B5iv7msuFYE3dytT+KE5tap+rNYw+K4jVkb9R/asAb+pbBSM17jtunHplhBe6RRJdZx3Pn2Jph24O32mOVg==", - "dev": true - }, "node_modules/to-fast-properties": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", @@ -17057,6 +17730,15 @@ "node": ">=8.0" } }, + "node_modules/toad-cache": { + "version": "3.7.0", + "resolved": "https://registry.npmjs.org/toad-cache/-/toad-cache-3.7.0.tgz", + "integrity": "sha512-/m8M+2BJUpoJdgAHoG+baCwBT+tf2VraSfkBgl0Y00qIWt41DJ8R5B8nsEw0I58YwF5IZH6z24/2TobDKnqSWw==", + "license": "MIT", + "engines": { + "node": ">=12" + } + }, "node_modules/toidentifier": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", @@ -17173,13 +17855,23 @@ "integrity": "sha512-xNvxJEOUiWPGhUuUdQgAJPKOOJfGnIyKySOc09XkKsgdUV/3E2zvwZYdejjmRgPCgcym1juLH3226yA7sEFJKQ==", "dev": true }, - "node_modules/tunnel": { - "version": "0.0.6", - "resolved": "https://registry.npmjs.org/tunnel/-/tunnel-0.0.6.tgz", - "integrity": "sha512-1h/Lnq9yajKY2PEbBadPXj3VxsDDu844OnaAo52UVmIzIvwwtBPIuNvkjuzBlTWpfJyUbG3ez0KSBibQkj4ojg==", - "dev": true, + "node_modules/tsx": { + "version": "4.19.2", + "resolved": "https://registry.npmjs.org/tsx/-/tsx-4.19.2.tgz", + "integrity": "sha512-pOUl6Vo2LUq/bSa8S5q7b91cgNSjctn9ugq/+Mvow99qW6x/UZYwzxy/3NmqoT66eHYfCVvFvACC58UBPFf28g==", + "license": "MIT", + "dependencies": { + "esbuild": "~0.23.0", + "get-tsconfig": "^4.7.5" + }, + "bin": { + "tsx": "dist/cli.mjs" + }, "engines": { - "node": ">=0.6.11 <=0.7.0 || >=0.7.3" + "node": ">=18.0.0" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" } }, "node_modules/type": { @@ -17238,7 +17930,6 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/typed-array-buffer/-/typed-array-buffer-1.0.2.tgz", "integrity": "sha512-gEymJYKZtKXzzBzM4jqa9w6Q1Jjm7x2d+sh19AdsD4wqnMPDYyvwpsIc2Q/835kHuo3BEQ7CjelGhfTsoBb2MQ==", - "dev": true, "dependencies": { "call-bind": "^1.0.7", "es-errors": "^1.3.0", @@ -17252,7 +17943,6 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/typed-array-byte-length/-/typed-array-byte-length-1.0.1.tgz", "integrity": "sha512-3iMJ9q0ao7WE9tWcaYKIptkNBuOIcZCCT0d4MRvuuH88fEoEH62IuQe0OtraD3ebQEoTRk8XCBoknUNc1Y67pw==", - "dev": true, "dependencies": { "call-bind": "^1.0.7", "for-each": "^0.3.3", @@ -17271,7 +17961,6 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/typed-array-byte-offset/-/typed-array-byte-offset-1.0.2.tgz", "integrity": "sha512-Ous0vodHa56FviZucS2E63zkgtgrACj7omjwd/8lTEMEPFFyjfixMZ1ZXenpgCFBBt4EC1J2XsyVS2gkG0eTFA==", - "dev": true, "dependencies": { "available-typed-arrays": "^1.0.7", "call-bind": "^1.0.7", @@ -17291,7 +17980,6 @@ "version": "1.0.6", "resolved": "https://registry.npmjs.org/typed-array-length/-/typed-array-length-1.0.6.tgz", "integrity": "sha512-/OxDN6OtAk5KBpGb28T+HZc2M+ADtvRxXrKKbUwtsLgdoxgX13hyy7ek6bFRl5+aBs2yZzB0c4CnQfAtVypW/g==", - "dev": true, "dependencies": { "call-bind": "^1.0.7", "for-each": "^0.3.3", @@ -17457,7 +18145,6 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.2.tgz", "integrity": "sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw==", - "dev": true, "dependencies": { "call-bind": "^1.0.2", "has-bigints": "^1.0.2", @@ -17485,9 +18172,10 @@ "dev": true }, "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==" + "version": "6.19.8", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.19.8.tgz", + "integrity": "sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw==", + "license": "MIT" }, "node_modules/unicode-canonical-property-names-ecmascript": { "version": "2.0.0", @@ -17604,24 +18292,6 @@ "browserslist": ">= 4.21.0" } }, - "node_modules/upper-case": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/upper-case/-/upper-case-2.0.2.tgz", - "integrity": "sha512-KgdgDGJt2TpuwBUIjgG6lzw2GWFRCW9Qkfkiv0DxqHHLYJHmtmdUIKcZd8rHgFSjopVTlw6ggzCm1b8MFQwikg==", - "dev": true, - "dependencies": { - "tslib": "^2.0.3" - } - }, - "node_modules/upper-case-first": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/upper-case-first/-/upper-case-first-2.0.2.tgz", - "integrity": "sha512-514ppYHBaKwfJRK/pNC6c/OxfGa0obSnAl106u97Ed0I625Nin96KAjttZF6ZL3e1XLtphxnqrOi9iWgm+u+bg==", - "dev": true, - "dependencies": { - "tslib": "^2.0.3" - } - }, "node_modules/uri-js": { "version": "4.4.1", "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", @@ -17661,7 +18331,7 @@ "version": "5.0.10", "resolved": "https://registry.npmjs.org/utf-8-validate/-/utf-8-validate-5.0.10.tgz", "integrity": "sha512-Z6czzLq4u8fPOyx7TU6X3dvUZVvoJmxSQ+IcrlmagKhilxlhZgxPK6C5Jqbkw1IDUmFTM+cz9QDnnLTwDz/2gQ==", - "dev": true, + "devOptional": true, "hasInstallScript": true, "dependencies": { "node-gyp-build": "^4.3.0" @@ -17673,8 +18343,7 @@ "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 + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==" }, "node_modules/utils-merge": { "version": "1.0.1", @@ -17708,7 +18377,6 @@ "version": "3.0.4", "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==", - "dev": true, "dependencies": { "spdx-correct": "^3.0.0", "spdx-expression-parse": "^3.0.0" @@ -17718,7 +18386,6 @@ "version": "1.1.2", "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==", - "dev": true, "engines": { "node": ">= 0.8" } @@ -17873,7 +18540,6 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz", "integrity": "sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==", - "dev": true, "dependencies": { "is-bigint": "^1.0.1", "is-boolean-object": "^1.1.0", @@ -17913,7 +18579,6 @@ "version": "1.1.15", "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.15.tgz", "integrity": "sha512-oV0jmFtUky6CXfkqehVvBP/LSWJ2sy4vWMioiENyJLePrBO/yKyV9OyJySfAKosh+RYkIl5zJCNZ8/4JncrpdA==", - "dev": true, "dependencies": { "available-typed-arrays": "^1.0.7", "call-bind": "^1.0.7", @@ -17993,8 +18658,7 @@ "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==", - "dev": true + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==" }, "node_modules/write-file-atomic": { "version": "3.0.3", @@ -18012,7 +18676,6 @@ "version": "8.17.1", "resolved": "https://registry.npmjs.org/ws/-/ws-8.17.1.tgz", "integrity": "sha512-6XQFvXTkbfUOZOKKILFG1PDK2NDQs4azKQl26T0YS5CxqWLgXajbPZ+h4gZekJyRqFU8pvnbAbbs/3TgRPy+GQ==", - "dev": true, "engines": { "node": ">=10.0.0" }, @@ -18068,15 +18731,6 @@ "node": ">=0.4.0" } }, - "node_modules/xtend": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", - "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==", - "dev": true, - "engines": { - "node": ">=0.4" - } - }, "node_modules/y18n": { "version": "5.0.8", "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", @@ -18165,15 +18819,6 @@ "fd-slicer": "~1.1.0" } }, - "node_modules/yazl": { - "version": "2.5.1", - "resolved": "https://registry.npmjs.org/yazl/-/yazl-2.5.1.tgz", - "integrity": "sha512-phENi2PLiHnHb6QBVot+dJnaAZ0xosj7p3fWl+znIjBDlnMI2PsZCJZ306BPTFOaHf5qdDEI8x5qFrSOBN5vrw==", - "dev": true, - "dependencies": { - "buffer-crc32": "~0.2.3" - } - }, "node_modules/yn": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", @@ -18282,6 +18927,28 @@ "url": "https://github.com/sponsors/colinhacks" } }, + "packages/integration-tests": { + "name": "@splunk/otel-js-web-integration-tests", + "version": "0.20.0-beta.0", + "license": "Apache-2.0", + "dependencies": { + "@fastify/view": "^10.0.1", + "@fastify/websocket": "^11.0.1", + "npm-run-all": "^4.1.5", + "pino-pretty": "^13.0.0", + "socket.io": "^4.8.1", + "tsx": "^4.19.2" + }, + "devDependencies": { + "@fastify/static": "^8.0.2", + "@playwright/test": "^1.48.2", + "@types/ejs": "^3.1.5", + "@types/express": "^5.0.0", + "@types/node": "^22.9.0", + "cors": "^2.8.5", + "ejs": "^3.1.10" + } + }, "packages/session-recorder": { "name": "@splunk/otel-web-session-recorder", "version": "0.20.0-beta.0", @@ -18391,7 +19058,6 @@ "rollup": "^4.18.0", "rollup-plugin-istanbul": "^4.0.0", "rollup-plugin-polyfill-node": "^0.12.0", - "saucelabs": "^7.2.2", "sinon": "^15.2.0", "socket.io": "^4.7.1", "socket.io-client": "^4.7.1", diff --git a/package.json b/package.json index 691f5754..d8263b08 100644 --- a/package.json +++ b/package.json @@ -5,7 +5,8 @@ "--workspaces": "Hardcoded so npm runs workspaces commands in order", "workspaces": [ "packages/web", - "packages/session-recorder" + "packages/session-recorder", + "packages/integration-tests" ], "engines": { "--": "Versions required for development only (recent enough to use npm workspaces)", @@ -47,6 +48,7 @@ "@aws-sdk/client-cloudfront": "^3.171.0", "@aws-sdk/client-s3": "^3.171.0", "@eslint/js": "^9.13.0", + "@fastify/cors": "^10.0.1", "@octokit/graphql": "^7.0.1", "@octokit/request": "^8.1.1", "@size-limit/file": "^11.1.6", @@ -59,6 +61,7 @@ "eslint-config-prettier": "^9.1.0", "eslint-plugin-headers": "^1.2.0", "eslint-plugin-prettier": "^5.2.1", + "fastify": "^5.1.0", "globals": "^15.11.0", "prettier": "^3.3.3", "size-limit": "^11.1.6", diff --git a/packages/integration-tests/.gitignore b/packages/integration-tests/.gitignore new file mode 100644 index 00000000..68c5d18f --- /dev/null +++ b/packages/integration-tests/.gitignore @@ -0,0 +1,5 @@ +node_modules/ +/test-results/ +/playwright-report/ +/blob-report/ +/playwright/.cache/ diff --git a/packages/integration-tests/package-lock.json b/packages/integration-tests/package-lock.json new file mode 100644 index 00000000..77c4445a --- /dev/null +++ b/packages/integration-tests/package-lock.json @@ -0,0 +1,1697 @@ +{ + "name": "@splunk/otel-js-web-integration-tests", + "version": "0.19.1", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "@splunk/otel-js-web-integration-tests", + "version": "0.19.1", + "license": "Apache-2.0", + "dependencies": { + "tsx": "^4.19.2" + }, + "devDependencies": { + "@playwright/test": "^1.48.2", + "@types/ejs": "^3.1.5", + "@types/express": "^5.0.0", + "@types/node": "^22.9.0", + "ejs": "^3.1.10", + "express": "^4.21.1" + } + }, + "node_modules/@esbuild/aix-ppc64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.23.1.tgz", + "integrity": "sha512-6VhYk1diRqrhBAqpJEdjASR/+WVRtfjpqKuNw11cLiaWpAT/Uu+nokB+UJnevzy/P9C/ty6AOe0dwueMrGh/iQ==", + "cpu": [ + "ppc64" + ], + "license": "MIT", + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.23.1.tgz", + "integrity": "sha512-uz6/tEy2IFm9RYOyvKl88zdzZfwEfKZmnX9Cj1BHjeSGNuGLuMD1kR8y5bteYmwqKm1tj8m4cb/aKEorr6fHWQ==", + "cpu": [ + "arm" + ], + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.23.1.tgz", + "integrity": "sha512-xw50ipykXcLstLeWH7WRdQuysJqejuAGPd30vd1i5zSyKK3WE+ijzHmLKxdiCMtH1pHz78rOg0BKSYOSB/2Khw==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.23.1.tgz", + "integrity": "sha512-nlN9B69St9BwUoB+jkyU090bru8L0NA3yFvAd7k8dNsVH8bi9a8cUAUSEcEEgTp2z3dbEDGJGfP6VUnkQnlReg==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.23.1.tgz", + "integrity": "sha512-YsS2e3Wtgnw7Wq53XXBLcV6JhRsEq8hkfg91ESVadIrzr9wO6jJDMZnCQbHm1Guc5t/CdDiFSSfWP58FNuvT3Q==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.23.1.tgz", + "integrity": "sha512-aClqdgTDVPSEGgoCS8QDG37Gu8yc9lTHNAQlsztQ6ENetKEO//b8y31MMu2ZaPbn4kVsIABzVLXYLhCGekGDqw==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.23.1.tgz", + "integrity": "sha512-h1k6yS8/pN/NHlMl5+v4XPfikhJulk4G+tKGFIOwURBSFzE8bixw1ebjluLOjfwtLqY0kewfjLSrO6tN2MgIhA==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.23.1.tgz", + "integrity": "sha512-lK1eJeyk1ZX8UklqFd/3A60UuZ/6UVfGT2LuGo3Wp4/z7eRTRYY+0xOu2kpClP+vMTi9wKOfXi2vjUpO1Ro76g==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.23.1.tgz", + "integrity": "sha512-CXXkzgn+dXAPs3WBwE+Kvnrf4WECwBdfjfeYHpMeVxWE0EceB6vhWGShs6wi0IYEqMSIzdOF1XjQ/Mkm5d7ZdQ==", + "cpu": [ + "arm" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.23.1.tgz", + "integrity": "sha512-/93bf2yxencYDnItMYV/v116zff6UyTjo4EtEQjUBeGiVpMmffDNUyD9UN2zV+V3LRV3/on4xdZ26NKzn6754g==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.23.1.tgz", + "integrity": "sha512-VTN4EuOHwXEkXzX5nTvVY4s7E/Krz7COC8xkftbbKRYAl96vPiUssGkeMELQMOnLOJ8k3BY1+ZY52tttZnHcXQ==", + "cpu": [ + "ia32" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.23.1.tgz", + "integrity": "sha512-Vx09LzEoBa5zDnieH8LSMRToj7ir/Jeq0Gu6qJ/1GcBq9GkfoEAoXvLiW1U9J1qE/Y/Oyaq33w5p2ZWrNNHNEw==", + "cpu": [ + "loong64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.23.1.tgz", + "integrity": "sha512-nrFzzMQ7W4WRLNUOU5dlWAqa6yVeI0P78WKGUo7lg2HShq/yx+UYkeNSE0SSfSure0SqgnsxPvmAUu/vu0E+3Q==", + "cpu": [ + "mips64el" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.23.1.tgz", + "integrity": "sha512-dKN8fgVqd0vUIjxuJI6P/9SSSe/mB9rvA98CSH2sJnlZ/OCZWO1DJvxj8jvKTfYUdGfcq2dDxoKaC6bHuTlgcw==", + "cpu": [ + "ppc64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.23.1.tgz", + "integrity": "sha512-5AV4Pzp80fhHL83JM6LoA6pTQVWgB1HovMBsLQ9OZWLDqVY8MVobBXNSmAJi//Csh6tcY7e7Lny2Hg1tElMjIA==", + "cpu": [ + "riscv64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.23.1.tgz", + "integrity": "sha512-9ygs73tuFCe6f6m/Tb+9LtYxWR4c9yg7zjt2cYkjDbDpV/xVn+68cQxMXCjUpYwEkze2RcU/rMnfIXNRFmSoDw==", + "cpu": [ + "s390x" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.23.1.tgz", + "integrity": "sha512-EV6+ovTsEXCPAp58g2dD68LxoP/wK5pRvgy0J/HxPGB009omFPv3Yet0HiaqvrIrgPTBuC6wCH1LTOY91EO5hQ==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.23.1.tgz", + "integrity": "sha512-aevEkCNu7KlPRpYLjwmdcuNz6bDFiE7Z8XC4CPqExjTvrHugh28QzUXVOZtiYghciKUacNktqxdpymplil1beA==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-arm64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.23.1.tgz", + "integrity": "sha512-3x37szhLexNA4bXhLrCC/LImN/YtWis6WXr1VESlfVtVeoFJBRINPJ3f0a/6LV8zpikqoUg4hyXw0sFBt5Cr+Q==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.23.1.tgz", + "integrity": "sha512-aY2gMmKmPhxfU+0EdnN+XNtGbjfQgwZj43k8G3fyrDM/UdZww6xrWxmDkuz2eCZchqVeABjV5BpildOrUbBTqA==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.23.1.tgz", + "integrity": "sha512-RBRT2gqEl0IKQABT4XTj78tpk9v7ehp+mazn2HbUeZl1YMdaGAQqhapjGTCe7uw7y0frDi4gS0uHzhvpFuI1sA==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.23.1.tgz", + "integrity": "sha512-4O+gPR5rEBe2FpKOVyiJ7wNDPA8nGzDuJ6gN4okSA1gEOYZ67N8JPk58tkWtdtPeLz7lBnY6I5L3jdsr3S+A6A==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.23.1.tgz", + "integrity": "sha512-BcaL0Vn6QwCwre3Y717nVHZbAa4UBEigzFm6VdsVdT/MbZ38xoj1X9HPkZhbmaBGUD1W8vxAfffbDe8bA6AKnQ==", + "cpu": [ + "ia32" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.23.1.tgz", + "integrity": "sha512-BHpFFeslkWrXWyUPnbKm+xYYVYruCinGcftSBaa8zoF9hZO4BcSCFUvHVTtzpIY6YzUnYtuEhZ+C9iEXjxnasg==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@playwright/test": { + "version": "1.48.2", + "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.48.2.tgz", + "integrity": "sha512-54w1xCWfXuax7dz4W2M9uw0gDyh+ti/0K/MxcCUxChFh37kkdxPdfZDw5QBbuPUJHr1CiHJ1hXgSs+GgeQc5Zw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "playwright": "1.48.2" + }, + "bin": { + "playwright": "cli.js" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@types/body-parser": { + "version": "1.19.5", + "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.5.tgz", + "integrity": "sha512-fB3Zu92ucau0iQ0JMCFQE7b/dv8Ot07NI3KaZIkIUNXq82k4eBAqUaneXfleGY9JWskeS9y+u0nXMyspcuQrCg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/connect": "*", + "@types/node": "*" + } + }, + "node_modules/@types/connect": { + "version": "3.4.38", + "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.38.tgz", + "integrity": "sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/ejs": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/@types/ejs/-/ejs-3.1.5.tgz", + "integrity": "sha512-nv+GSx77ZtXiJzwKdsASqi+YQ5Z7vwHsTP0JY2SiQgjGckkBRKZnk8nIM+7oUZ1VCtuTz0+By4qVR7fqzp/Dfg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/express": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/@types/express/-/express-5.0.0.tgz", + "integrity": "sha512-DvZriSMehGHL1ZNLzi6MidnsDhUZM/x2pRdDIKdwbUNqqwHxMlRdkxtn6/EPKyqKpHqTl/4nRZsRNLpZxZRpPQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/body-parser": "*", + "@types/express-serve-static-core": "^5.0.0", + "@types/qs": "*", + "@types/serve-static": "*" + } + }, + "node_modules/@types/express-serve-static-core": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-5.0.1.tgz", + "integrity": "sha512-CRICJIl0N5cXDONAdlTv5ShATZ4HEwk6kDDIW2/w9qOWKg+NU/5F8wYRWCrONad0/UKkloNSmmyN/wX4rtpbVA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*", + "@types/qs": "*", + "@types/range-parser": "*", + "@types/send": "*" + } + }, + "node_modules/@types/http-errors": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/@types/http-errors/-/http-errors-2.0.4.tgz", + "integrity": "sha512-D0CFMMtydbJAegzOyHjtiKPLlvnm3iTZyZRSZoLq2mRhDdmLfIWOCYPfQJ4cu2erKghU++QvjcUjp/5h7hESpA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/mime": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.5.tgz", + "integrity": "sha512-/pyBZWSLD2n0dcHE3hq8s8ZvcETHtEuF+3E7XVt0Ig2nvsVQXdghHVcEkIWjy9A0wKfTn97a/PSDYohKIlnP/w==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/node": { + "version": "22.9.0", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.9.0.tgz", + "integrity": "sha512-vuyHg81vvWA1Z1ELfvLko2c8f34gyA0zaic0+Rllc5lbCnbSyuvb2Oxpm6TAUAC/2xZN3QGqxBNggD1nNR2AfQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "undici-types": "~6.19.8" + } + }, + "node_modules/@types/qs": { + "version": "6.9.17", + "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.17.tgz", + "integrity": "sha512-rX4/bPcfmvxHDv0XjfJELTTr+iB+tn032nPILqHm5wbthUUUuVtNGGqzhya9XUxjTP8Fpr0qYgSZZKxGY++svQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/range-parser": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.7.tgz", + "integrity": "sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/send": { + "version": "0.17.4", + "resolved": "https://registry.npmjs.org/@types/send/-/send-0.17.4.tgz", + "integrity": "sha512-x2EM6TJOybec7c52BX0ZspPodMsQUd5L6PRwOunVyVUhXiBSKf3AezDL8Dgvgt5o0UfKNfuA0eMLr2wLT4AiBA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/mime": "^1", + "@types/node": "*" + } + }, + "node_modules/@types/serve-static": { + "version": "1.15.7", + "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.15.7.tgz", + "integrity": "sha512-W8Ym+h8nhuRwaKPaDw34QUkwsGi6Rc4yYqvKFo5rm2FUEhCFbzVWrxXUxuKK8TASjWsysJY0nsmNCGhCOIsrOw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/http-errors": "*", + "@types/node": "*", + "@types/send": "*" + } + }, + "node_modules/accepts": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", + "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==", + "dev": true, + "license": "MIT", + "dependencies": { + "mime-types": "~2.1.34", + "negotiator": "0.6.3" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/array-flatten": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", + "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==", + "dev": true, + "license": "MIT" + }, + "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/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true, + "license": "MIT" + }, + "node_modules/body-parser": { + "version": "1.20.3", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.3.tgz", + "integrity": "sha512-7rAxByjUMqQ3/bHJy7D6OGXvx/MMc4IqBn/X0fcM1QUcAItpZrBEYhWGem+tzXH90c+G01ypMcYJBO9Y30203g==", + "dev": true, + "license": "MIT", + "dependencies": { + "bytes": "3.1.2", + "content-type": "~1.0.5", + "debug": "2.6.9", + "depd": "2.0.0", + "destroy": "1.2.0", + "http-errors": "2.0.0", + "iconv-lite": "0.4.24", + "on-finished": "2.4.1", + "qs": "6.13.0", + "raw-body": "2.5.2", + "type-is": "~1.6.18", + "unpipe": "1.0.0" + }, + "engines": { + "node": ">= 0.8", + "npm": "1.2.8000 || >= 1.4.16" + } + }, + "node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/bytes": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", + "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/call-bind": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.7.tgz", + "integrity": "sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-define-property": "^1.0.0", + "es-errors": "^1.3.0", + "function-bind": "^1.1.2", + "get-intrinsic": "^1.2.4", + "set-function-length": "^1.2.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true, + "license": "MIT" + }, + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", + "dev": true, + "license": "MIT" + }, + "node_modules/content-disposition": { + "version": "0.5.4", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", + "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "safe-buffer": "5.2.1" + }, + "engines": { + "node": ">= 0.6" + } + }, + "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==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/cookie": { + "version": "0.7.1", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.1.tgz", + "integrity": "sha512-6DnInpx7SJ2AK3+CTUE/ZM0vWTUboZCegxhC2xiIydHR9jNuTAASBrfEpHhiGOZw/nX51bHt6YQl8jsGo4y/0w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/cookie-signature": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", + "integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "2.0.0" + } + }, + "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==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-define-property": "^1.0.0", + "es-errors": "^1.3.0", + "gopd": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "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==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/destroy": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz", + "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8", + "npm": "1.2.8000 || >= 1.4.16" + } + }, + "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==", + "dev": true, + "license": "MIT" + }, + "node_modules/ejs": { + "version": "3.1.10", + "resolved": "https://registry.npmjs.org/ejs/-/ejs-3.1.10.tgz", + "integrity": "sha512-UeJmFfOrAQS8OJWPZ4qtgHyWExa088/MtK5UEyoJGFH67cDEXkZSviOiKRCZ4Xij0zxI3JECgYs3oKx+AizQBA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "jake": "^10.8.5" + }, + "bin": { + "ejs": "bin/cli.js" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/encodeurl": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz", + "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/es-define-property": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.0.tgz", + "integrity": "sha512-jxayLKShrEqqzJ0eumQbVhTYQM27CfT1T35+gCgDFoL82JLsXqTJ76zv6A0YLOgEnLUMvLzsDsGIrl8NFpT2gQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "get-intrinsic": "^1.2.4" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/esbuild": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.23.1.tgz", + "integrity": "sha512-VVNz/9Sa0bs5SELtn3f7qhJCDPCF5oMEl5cO9/SSinpE9hbPVvxbd572HH5AKiP7WD8INO53GgfDDhRjkylHEg==", + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.23.1", + "@esbuild/android-arm": "0.23.1", + "@esbuild/android-arm64": "0.23.1", + "@esbuild/android-x64": "0.23.1", + "@esbuild/darwin-arm64": "0.23.1", + "@esbuild/darwin-x64": "0.23.1", + "@esbuild/freebsd-arm64": "0.23.1", + "@esbuild/freebsd-x64": "0.23.1", + "@esbuild/linux-arm": "0.23.1", + "@esbuild/linux-arm64": "0.23.1", + "@esbuild/linux-ia32": "0.23.1", + "@esbuild/linux-loong64": "0.23.1", + "@esbuild/linux-mips64el": "0.23.1", + "@esbuild/linux-ppc64": "0.23.1", + "@esbuild/linux-riscv64": "0.23.1", + "@esbuild/linux-s390x": "0.23.1", + "@esbuild/linux-x64": "0.23.1", + "@esbuild/netbsd-x64": "0.23.1", + "@esbuild/openbsd-arm64": "0.23.1", + "@esbuild/openbsd-x64": "0.23.1", + "@esbuild/sunos-x64": "0.23.1", + "@esbuild/win32-arm64": "0.23.1", + "@esbuild/win32-ia32": "0.23.1", + "@esbuild/win32-x64": "0.23.1" + } + }, + "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==", + "dev": true, + "license": "MIT" + }, + "node_modules/etag": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", + "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/express": { + "version": "4.21.1", + "resolved": "https://registry.npmjs.org/express/-/express-4.21.1.tgz", + "integrity": "sha512-YSFlK1Ee0/GC8QaO91tHcDxJiE/X4FbpAyQWkxAvG6AXCuR65YzK8ua6D9hvi/TzUfZMpc+BwuM1IPw8fmQBiQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "accepts": "~1.3.8", + "array-flatten": "1.1.1", + "body-parser": "1.20.3", + "content-disposition": "0.5.4", + "content-type": "~1.0.4", + "cookie": "0.7.1", + "cookie-signature": "1.0.6", + "debug": "2.6.9", + "depd": "2.0.0", + "encodeurl": "~2.0.0", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "finalhandler": "1.3.1", + "fresh": "0.5.2", + "http-errors": "2.0.0", + "merge-descriptors": "1.0.3", + "methods": "~1.1.2", + "on-finished": "2.4.1", + "parseurl": "~1.3.3", + "path-to-regexp": "0.1.10", + "proxy-addr": "~2.0.7", + "qs": "6.13.0", + "range-parser": "~1.2.1", + "safe-buffer": "5.2.1", + "send": "0.19.0", + "serve-static": "1.16.2", + "setprototypeof": "1.2.0", + "statuses": "2.0.1", + "type-is": "~1.6.18", + "utils-merge": "1.0.1", + "vary": "~1.1.2" + }, + "engines": { + "node": ">= 0.10.0" + } + }, + "node_modules/filelist": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/filelist/-/filelist-1.0.4.tgz", + "integrity": "sha512-w1cEuf3S+DrLCQL7ET6kz+gmlJdbq9J7yXCSjK/OZCPA+qEN1WyF4ZAf0YYJa4/shHJra2t/d/r8SV4Ji+x+8Q==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "minimatch": "^5.0.1" + } + }, + "node_modules/filelist/node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/filelist/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/finalhandler": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.3.1.tgz", + "integrity": "sha512-6BN9trH7bp3qvnrRyzsBz+g3lZxTNZTbVO2EV1CS0WIcDbawYVdYvGflME/9QP0h0pYlCDBCTjYa9nZzMDpyxQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "2.6.9", + "encodeurl": "~2.0.0", + "escape-html": "~1.0.3", + "on-finished": "2.4.1", + "parseurl": "~1.3.3", + "statuses": "2.0.1", + "unpipe": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/forwarded": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", + "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/fresh": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", + "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/fsevents": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", + "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-intrinsic": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.4.tgz", + "integrity": "sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "function-bind": "^1.1.2", + "has-proto": "^1.0.1", + "has-symbols": "^1.0.3", + "hasown": "^2.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-tsconfig": { + "version": "4.8.1", + "resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.8.1.tgz", + "integrity": "sha512-k9PN+cFBmaLWtVz29SkUoqU5O0slLuHJXt/2P+tMVFT+phsSGXGkp9t3rQIqdz0e+06EHNGs3oM6ZX1s2zHxRg==", + "license": "MIT", + "dependencies": { + "resolve-pkg-maps": "^1.0.0" + }, + "funding": { + "url": "https://github.com/privatenumber/get-tsconfig?sponsor=1" + } + }, + "node_modules/gopd": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz", + "integrity": "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==", + "dev": true, + "license": "MIT", + "dependencies": { + "get-intrinsic": "^1.1.3" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "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==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-define-property": "^1.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-proto": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.3.tgz", + "integrity": "sha512-SJ1amZAJUiZS+PhsVLf5tGydlaVB8EdFpaSO4gmiUKUOxk8qzn5AIy4ZeJUmh22znIdk/uMAUT2pl3FxzVUH+Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-symbols": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", + "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/hasown": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", + "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/http-errors": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", + "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "depd": "2.0.0", + "inherits": "2.0.4", + "setprototypeof": "1.2.0", + "statuses": "2.0.1", + "toidentifier": "1.0.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/iconv-lite": { + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "dev": true, + "license": "MIT", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3" + }, + "engines": { + "node": ">=0.10.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==", + "dev": true, + "license": "ISC" + }, + "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==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/jake": { + "version": "10.9.2", + "resolved": "https://registry.npmjs.org/jake/-/jake-10.9.2.tgz", + "integrity": "sha512-2P4SQ0HrLQ+fw6llpLnOaGAvN2Zu6778SJMrCUwns4fOoG9ayrTiZk3VV8sCPkVZF8ab0zksVpS8FDY5pRCNBA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "async": "^3.2.3", + "chalk": "^4.0.2", + "filelist": "^1.0.4", + "minimatch": "^3.1.2" + }, + "bin": { + "jake": "bin/cli.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/media-typer": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", + "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/merge-descriptors": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.3.tgz", + "integrity": "sha512-gaNvAS7TZ897/rVaZ0nMtAyxNyi/pdbjbAwUpFQpN70GqnVfOiXpeUUMKRBmzXaSQ8DdTX4/0ms62r2K+hE6mQ==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/methods": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", + "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", + "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", + "dev": true, + "license": "MIT", + "bin": { + "mime": "cli.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "dev": true, + "license": "MIT", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/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": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "dev": true, + "license": "MIT" + }, + "node_modules/negotiator": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", + "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/object-inspect": { + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.2.tgz", + "integrity": "sha512-IRZSRuzJiynemAXPYtPe5BoI/RESNYR7TYm50MC5Mqbd3Jmw5y790sErYw3V6SryFJD64b74qQQs9wn5Bg/k3g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/on-finished": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", + "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", + "dev": true, + "license": "MIT", + "dependencies": { + "ee-first": "1.1.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/parseurl": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", + "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/path-to-regexp": { + "version": "0.1.10", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.10.tgz", + "integrity": "sha512-7lf7qcQidTku0Gu3YDPc8DJ1q7OOucfa/BSsIwjuh56VU7katFvuM8hULfkwB3Fns/rsVF7PwPKVw1sl5KQS9w==", + "dev": true, + "license": "MIT" + }, + "node_modules/playwright": { + "version": "1.48.2", + "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.48.2.tgz", + "integrity": "sha512-NjYvYgp4BPmiwfe31j4gHLa3J7bD2WiBz8Lk2RoSsmX38SVIARZ18VYjxLjAcDsAhA+F4iSEXTSGgjua0rrlgQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "playwright-core": "1.48.2" + }, + "bin": { + "playwright": "cli.js" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "fsevents": "2.3.2" + } + }, + "node_modules/playwright-core": { + "version": "1.48.2", + "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.48.2.tgz", + "integrity": "sha512-sjjw+qrLFlriJo64du+EK0kJgZzoQPsabGF4lBvsid+3CNIZIYLgnMj9V6JY5VhM2Peh20DJWIVpVljLLnlawA==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "playwright-core": "cli.js" + }, + "engines": { + "node": ">=18" + } + }, + "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==", + "dev": true, + "license": "MIT", + "dependencies": { + "forwarded": "0.2.0", + "ipaddr.js": "1.9.1" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/qs": { + "version": "6.13.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.13.0.tgz", + "integrity": "sha512-+38qI9SOr8tfZ4QmJNplMUxqjbe7LKvvZgWdExBOmd+egZTtjLB67Gu0HRX3u/XOq7UU2Nx6nsjvS16Z9uwfpg==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "side-channel": "^1.0.6" + }, + "engines": { + "node": ">=0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/range-parser": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", + "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/raw-body": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.2.tgz", + "integrity": "sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA==", + "dev": true, + "license": "MIT", + "dependencies": { + "bytes": "3.1.2", + "http-errors": "2.0.0", + "iconv-lite": "0.4.24", + "unpipe": "1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/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==", + "license": "MIT", + "funding": { + "url": "https://github.com/privatenumber/resolve-pkg-maps?sponsor=1" + } + }, + "node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "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/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", + "dev": true, + "license": "MIT" + }, + "node_modules/send": { + "version": "0.19.0", + "resolved": "https://registry.npmjs.org/send/-/send-0.19.0.tgz", + "integrity": "sha512-dW41u5VfLXu8SJh5bwRmyYUbAoSB3c9uQh6L8h/KtsFREPWpbX1lrljJo186Jc4nmci/sGUZ9a0a0J2zgfq2hw==", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "2.6.9", + "depd": "2.0.0", + "destroy": "1.2.0", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "fresh": "0.5.2", + "http-errors": "2.0.0", + "mime": "1.6.0", + "ms": "2.1.3", + "on-finished": "2.4.1", + "range-parser": "~1.2.1", + "statuses": "2.0.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/send/node_modules/encodeurl": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", + "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/send/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true, + "license": "MIT" + }, + "node_modules/serve-static": { + "version": "1.16.2", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.16.2.tgz", + "integrity": "sha512-VqpjJZKadQB/PEbEwvFdO43Ax5dFBZ2UECszz8bQ7pi7wt//PWe1P6MN7eCnjsatYtBT6EuiClbjSWP2WrIoTw==", + "dev": true, + "license": "MIT", + "dependencies": { + "encodeurl": "~2.0.0", + "escape-html": "~1.0.3", + "parseurl": "~1.3.3", + "send": "0.19.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/set-function-length": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz", + "integrity": "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==", + "dev": true, + "license": "MIT", + "dependencies": { + "define-data-property": "^1.1.4", + "es-errors": "^1.3.0", + "function-bind": "^1.1.2", + "get-intrinsic": "^1.2.4", + "gopd": "^1.0.1", + "has-property-descriptors": "^1.0.2" + }, + "engines": { + "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==", + "dev": true, + "license": "ISC" + }, + "node_modules/side-channel": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.6.tgz", + "integrity": "sha512-fDW/EZ6Q9RiO8eFG8Hj+7u/oW+XrPTIChwCOM2+th2A6OblDtYYIpve9m+KvI9Z4C9qSEXlaGR6bTEYHReuglA==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.7", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.4", + "object-inspect": "^1.13.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/statuses": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", + "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "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==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "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==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.6" + } + }, + "node_modules/tsx": { + "version": "4.19.2", + "resolved": "https://registry.npmjs.org/tsx/-/tsx-4.19.2.tgz", + "integrity": "sha512-pOUl6Vo2LUq/bSa8S5q7b91cgNSjctn9ugq/+Mvow99qW6x/UZYwzxy/3NmqoT66eHYfCVvFvACC58UBPFf28g==", + "license": "MIT", + "dependencies": { + "esbuild": "~0.23.0", + "get-tsconfig": "^4.7.5" + }, + "bin": { + "tsx": "dist/cli.mjs" + }, + "engines": { + "node": ">=18.0.0" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + } + }, + "node_modules/tsx/node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/type-is": { + "version": "1.6.18", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", + "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", + "dev": true, + "license": "MIT", + "dependencies": { + "media-typer": "0.3.0", + "mime-types": "~2.1.24" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/undici-types": { + "version": "6.19.8", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.19.8.tgz", + "integrity": "sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw==", + "dev": true, + "license": "MIT" + }, + "node_modules/unpipe": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", + "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/utils-merge": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", + "integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4.0" + } + }, + "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==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + } + } +} diff --git a/packages/integration-tests/package.json b/packages/integration-tests/package.json new file mode 100644 index 00000000..9a375c9c --- /dev/null +++ b/packages/integration-tests/package.json @@ -0,0 +1,37 @@ +{ + "name": "@splunk/otel-js-web-integration-tests", + "private": true, + "version": "0.20.0-beta.0", + "repository": "github:signalfx/splunk-otel-js-browser", + "scripts": { + "server": "run-p server:*", + "server:same-origin": "PORT=3000 tsx src/server/server.ts", + "server:cross-origin": "PORT=3001 tsx src/server/server.ts", + "server:socketio": "PORT=3002 tsx src/server/server-socketio.ts", + "test": "npx playwright test", + "compile": "exit 0;" + }, + "author": "Splunk Observability Instrumentals Team ", + "license": "Apache-2.0", + "devDependencies": { + "@fastify/static": "^8.0.2", + "@playwright/test": "^1.48.2", + "@types/ejs": "^3.1.5", + "@types/express": "^5.0.0", + "@types/node": "^22.9.0", + "cors": "^2.8.5", + "ejs": "^3.1.10" + }, + "bugs": { + "url": "https://github.com/signalfx/splunk-otel-js-web/issues" + }, + "homepage": "https://github.com/signalfx/splunk-otel-js-web#readme", + "dependencies": { + "@fastify/view": "^10.0.1", + "@fastify/websocket": "^11.0.1", + "npm-run-all": "^4.1.5", + "pino-pretty": "^13.0.0", + "socket.io": "^4.8.1", + "tsx": "^4.19.2" + } +} diff --git a/packages/integration-tests/playwright.config.ts b/packages/integration-tests/playwright.config.ts new file mode 100644 index 00000000..34d0a4e2 --- /dev/null +++ b/packages/integration-tests/playwright.config.ts @@ -0,0 +1,101 @@ +/** + * + * Copyright 2024 Splunk Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ +import { defineConfig, devices } from '@playwright/test' + +const REPORTERS = [] +if (process.env.CI) { + REPORTERS.push(['blob'], ['list']) +} else { + REPORTERS.push(['list'], ['html', { open: 'never' }]) +} + +/** + * Read environment variables from file. + * https://github.com/motdotla/dotenv + */ +// import dotenv from 'dotenv'; +// import path from 'path'; +// dotenv.config({ path: path.resolve(__dirname, '.env') }); + +/** + * See https://playwright.dev/docs/test-configuration. + */ +export default defineConfig({ + testDir: './src/tests', + /* Run tests in files in parallel */ + fullyParallel: true, + /* Fail the build on CI if you accidentally left test.only in the source code. */ + forbidOnly: !!process.env.CI, + /* Retry on CI only */ + retries: process.env.CI ? 2 : 0, + /* Opt out of parallel tests on CI. */ + workers: process.env.CI ? 1 : undefined, + /* Reporter to use. See https://playwright.dev/docs/test-reporters */ + reporter: REPORTERS, + /* Shared settings for all the projects below. See https://playwright.dev/docs/api/class-testoptions. */ + use: { + /* Base URL to use in actions like `await page.goto('/')`. */ + // baseURL: 'http://127.0.0.1:3000', + + /* Collect trace when retrying the failed test. See https://playwright.dev/docs/trace-viewer */ + trace: 'on-first-retry', + }, + + /* Configure projects for major browsers */ + projects: [ + { + name: 'chromium', + use: { ...devices['Desktop Chrome'], headless: true }, + }, + + { + name: 'firefox', + use: { ...devices['Desktop Firefox'], headless: true }, + }, + + { + name: 'webkit', + use: { ...devices['Desktop Safari'], headless: true }, + }, + + { + name: 'edge', + use: { + ...devices['Desktop Edge'], + channel: 'msedge', + headless: true, + }, + }, + + { + name: 'chrome', + use: { + ...devices['Desktop Chrome'], + channel: 'chrome', + headless: true, + }, + }, + ], + + /* Run your local dev server before starting the tests */ + webServer: { + command: 'npm run server', + url: 'http://127.0.0.1:3000', + reuseExistingServer: !process.env.CI, + }, +}) diff --git a/packages/integration-tests/src/pages/record-page.ts b/packages/integration-tests/src/pages/record-page.ts new file mode 100644 index 00000000..119a90ed --- /dev/null +++ b/packages/integration-tests/src/pages/record-page.ts @@ -0,0 +1,132 @@ +/** + * + * Copyright 2024 Splunk Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ +import { BrowserContext, Page } from 'playwright' +import { Span } from '@opentelemetry/exporter-zipkin/build/src/types' + +export class RecordPage { + receivedSpans: Span[] = [] + + get receivedErrorSpans() { + return this.receivedSpans.filter((span) => span.name === 'onerror') + } + + constructor( + private readonly page: Page, + private readonly context: BrowserContext, + ) {} + + changeVisibilityInTab = async (state: 'visible' | 'hidden') => { + await this.page.evaluate((stateInner) => { + Object.defineProperty(document, 'visibilityState', { value: stateInner, writable: true }) + Object.defineProperty(document, 'hidden', { value: Boolean(stateInner === 'hidden'), writable: true }) + + window.dispatchEvent(new Event('visibilitychange')) + }, state) + } + + clearReceivedSpans() { + this.receivedSpans = [] + } + + async flushData() { + await this.page.evaluate(() => { + if (window.SplunkRum) { + window.SplunkRum._processor.forceFlush() + } + }) + } + + async getCookie(name: string) { + const cookies = await this.context.cookies() + return cookies.find((cookie) => cookie.name === name) + } + + goTo(url: string) { + const absoluteUrl = new URL(url, 'http://localhost:3000').toString() + return this.page.goto(absoluteUrl) + } + + get locator() { + return this.page.locator.bind(this.page) + } + + get keyboard() { + return this.page.keyboard + } + + get evaluate() { + return this.page.evaluate.bind(this.page) + } + + async mockNetwork() { + await this.page.route('*/**/api/v2/spans', async (route, request) => { + const spans = JSON.parse(request.postData()) + this.receivedSpans.push(...spans) + + await route.fulfill({ + status: 200, + contentType: 'application/json', + body: '', + }) + }) + } + + setOffline() { + return this.context.setOffline(true) + } + + setOnline() { + return this.context.setOffline(false) + } + + waitForSpans: (predicate: (spans: Span[]) => boolean) => Promise = async (predicate) => + new Promise((resolve) => { + const intervalId = setInterval(async () => { + await this.flushData() + if (predicate(this.receivedSpans)) { + clearInterval(intervalId) + resolve() + } + }, 50) + }) + + waitForTestToFinish = async () => { + await this.page.evaluate( + () => + new Promise((resolve) => { + const interval = setInterval(() => { + // TODO: Add d.ts file + // @ts-expect-error testing is a global variable + if (window.testing === false) { + clearInterval(interval) + resolve() + } + }, 50) + }), + ) + } + + waitForTimeout(timeout: number) { + return this.page.waitForTimeout(timeout) + } + + async waitForTimeoutAndFlushData(timeout: number) { + await this.waitForTimeout(timeout) + await this.flushData() + } +} diff --git a/packages/web/integration-tests/tests/native/native.spec.js b/packages/integration-tests/src/server/middlewares/delay-middleware.ts similarity index 56% rename from packages/web/integration-tests/tests/native/native.spec.js rename to packages/integration-tests/src/server/middlewares/delay-middleware.ts index f82c74d9..960ca292 100644 --- a/packages/web/integration-tests/tests/native/native.spec.js +++ b/packages/integration-tests/src/server/middlewares/delay-middleware.ts @@ -15,17 +15,24 @@ * limitations under the License. * */ +import { FastifyRequest } from 'fastify' -module.exports = { - 'native session id integration': async function (browser) { - const url = browser.globals.getUrl('/native/native.ejs') - await browser.url(url) +const sleep = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms)) - const anySpan = await browser.globals.findSpan((span) => span.tags['splunk.rumSessionId'] !== undefined) +export const delayMiddleware = async ( + request: FastifyRequest<{ + Querystring: { + delay?: string + } + }>, +) => { + const delayParam = request.query?.delay - await browser.assert.ok(anySpan) - - await browser.assert.strictEqual(anySpan.tags['splunk.rumSessionId'], '12341234123412341234123412341234') - await browser.globals.assertNoErrorSpans() - }, + if (delayParam) { + const delay = parseInt(delayParam, 10) + if (!isNaN(delay) && delay > 0) { + request.log.info(`Delaying request for ${delay}ms`) + await sleep(delay) + } + } } diff --git a/packages/web/utils/certs/index.js b/packages/integration-tests/src/server/middlewares/index.ts similarity index 67% rename from packages/web/utils/certs/index.js rename to packages/integration-tests/src/server/middlewares/index.ts index edee7dc2..fe10c2e4 100644 --- a/packages/web/utils/certs/index.js +++ b/packages/integration-tests/src/server/middlewares/index.ts @@ -15,17 +15,6 @@ * limitations under the License. * */ - -const path = require('path') -const fs = require('fs') - -function getCertificateConfig() { - return { - key: fs.readFileSync(path.join(__dirname, 'server.key'), 'utf8'), - cert: fs.readFileSync(path.join(__dirname, 'server.cert'), 'utf8'), - } -} - -module.exports = { - getCertificateConfig, -} +export * from './delay-middleware' +export * from './server-timing-middleware' +export * from './no-cache-middleware' diff --git a/packages/web/integration-tests/utils/buildApi.js b/packages/integration-tests/src/server/middlewares/no-cache-middleware.ts similarity index 56% rename from packages/web/integration-tests/utils/buildApi.js rename to packages/integration-tests/src/server/middlewares/no-cache-middleware.ts index b25b58eb..0b24e0e4 100644 --- a/packages/web/integration-tests/utils/buildApi.js +++ b/packages/integration-tests/src/server/middlewares/no-cache-middleware.ts @@ -16,20 +16,19 @@ * */ -const { execSync } = require('child_process') +import { FastifyRequest, FastifyReply } from 'fastify' -function sanitizeCliOut(out) { - return out.toString().replace(/\n/g, '') -} - -function generateBuildId() { - const commitId = process.env.CIRCLE_SHA1 || sanitizeCliOut(execSync('git rev-parse HEAD')) - const author = process.env.CIRCLE_PR_USERNAME || sanitizeCliOut(execSync('whoami')) - const when = new Date().toJSON() - - return `${author}-${when}-${commitId.substring(0, 8)}` -} +export const noCacheMiddleware = ( + req: FastifyRequest<{ Querystring: { noCache?: string } }>, + reply: FastifyReply, + payload: unknown, + done: (err?: Error) => void, +) => { + if (typeof req.query.noCache === 'string') { + reply.header('Cache-Control', 'no-store, max-age=0') + reply.removeHeader('Etag') + reply.removeHeader('Last-Modified') + } -module.exports = { - generateBuildId, + done() } diff --git a/packages/integration-tests/src/server/middlewares/server-timing-middleware.ts b/packages/integration-tests/src/server/middlewares/server-timing-middleware.ts new file mode 100644 index 00000000..61be816e --- /dev/null +++ b/packages/integration-tests/src/server/middlewares/server-timing-middleware.ts @@ -0,0 +1,39 @@ +/** + * + * Copyright 2024 Splunk Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ +import { FastifyRequest, FastifyReply } from 'fastify' +import { setServerTimingHeader } from '../server-timing' + +export const serverTimingMiddleware = ( + req: FastifyRequest<{ Querystring: { serverTiming?: string } }>, + reply: FastifyReply, + payload: unknown, + done: (err?: Error) => void, +) => { + if (req.url.includes('no-server-timings')) { + done() + return + } + + if (typeof req.query.serverTiming === 'string') { + reply.header('Server-Timing', req.query.serverTiming) + } else { + setServerTimingHeader(reply) + } + + done() +} diff --git a/packages/integration-tests/src/server/render-agent.ts b/packages/integration-tests/src/server/render-agent.ts new file mode 100644 index 00000000..953af2dc --- /dev/null +++ b/packages/integration-tests/src/server/render-agent.ts @@ -0,0 +1,73 @@ +/** + * + * Copyright 2024 Splunk Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ +export const RENDER_AGENT_TEMPLATE = ` + + + +` diff --git a/packages/web/integration-tests/tests/redacting-attributes/index.spec.js b/packages/integration-tests/src/server/server-socketio.ts similarity index 58% rename from packages/web/integration-tests/tests/redacting-attributes/index.spec.js rename to packages/integration-tests/src/server/server-socketio.ts index cffd1b40..509667d3 100644 --- a/packages/web/integration-tests/tests/redacting-attributes/index.spec.js +++ b/packages/integration-tests/src/server/server-socketio.ts @@ -15,16 +15,23 @@ * limitations under the License. * */ +import { createServer } from 'http' +import { Server } from 'socket.io' -module.exports = { - 'redacting attributes with custom exporter': async function (browser) { - const url = browser.globals.getUrl('/redacting-attributes/index.ejs') - await browser.url(url) +const httpServer = createServer() +const io = new Server(httpServer, { + cors: { + origin: true, + }, +}) - await browser.click('#btnClick') +io.on('connection', (socket) => { + socket.on('hello', () => {}) + socket.on('ping', (...args) => { + socket.emit('pong', ...args) + }) +}) - const clickSpan = await browser.globals.findSpan((span) => span.name === 'click') - await browser.assert.ok(clickSpan) - await browser.assert.strictEqual(clickSpan.tags['http.url'], '[redacted]') - }, -} +const PORT = Number.parseInt(process.env.PORT || '3002') + +httpServer.listen(PORT) diff --git a/packages/web/utils/server/serverTiming.js b/packages/integration-tests/src/server/server-timing.ts similarity index 67% rename from packages/web/utils/server/serverTiming.js rename to packages/integration-tests/src/server/server-timing.ts index 282d7bfa..88dd5fc5 100644 --- a/packages/web/utils/server/serverTiming.js +++ b/packages/integration-tests/src/server/server-timing.ts @@ -15,12 +15,12 @@ * limitations under the License. * */ +import { FastifyReply } from 'fastify' -function generateHex(length) { - return [...Array(length).keys()].map(() => parseInt(Math.random() * 16).toString(16)).join('') -} +const generateHex = (length: number) => + [...Array(length).keys()].map(() => Math.floor(Math.random() * 16).toString(16)).join('') -function generateServerTiming() { +export const generateServerTiming = () => { const traceId = generateHex(32) const spanId = generateHex(16) const formatted = `traceparent;desc="00-${traceId}-${spanId}-01"` @@ -31,11 +31,6 @@ function generateServerTiming() { } } -function setServerTimingHeader(responseObject, serverTiming = generateServerTiming()) { - responseObject.set('Server-Timing', serverTiming.header) -} - -module.exports = { - generateServerTiming, - setServerTimingHeader, +export const setServerTimingHeader = (reply: FastifyReply, serverTiming = generateServerTiming()) => { + reply.header('Server-Timing', serverTiming.header) } diff --git a/packages/integration-tests/src/server/server.ts b/packages/integration-tests/src/server/server.ts new file mode 100644 index 00000000..aa3b058d --- /dev/null +++ b/packages/integration-tests/src/server/server.ts @@ -0,0 +1,177 @@ +/** + * + * Copyright 2024 Splunk Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ +import Fastify from 'fastify' +import path from 'path' +import { RENDER_AGENT_TEMPLATE } from './render-agent' +import ejs from 'ejs' +import * as fs from 'node:fs' +import cors from '@fastify/cors' +import { serverTimingMiddleware, delayMiddleware, noCacheMiddleware } from './middlewares' +import fastifyStatic from '@fastify/static' +import fastifyView from '@fastify/view' +import fastifyWebsockets from '@fastify/websocket' + +const GLOBAL_TEST_BUFFER_TIMEOUT = 20 + +const fastify = Fastify({ + logger: { + transport: { + target: 'pino-pretty', + options: { + colorize: true, + translateTime: 'SYS:standard', + ignore: 'pid,hostname', + }, + }, + }, +}) + +fastify.register(cors) +fastify.register(fastifyView, { + engine: { + ejs: ejs, + }, + root: path.join(__dirname, '../tests'), +}) +fastify.register(fastifyStatic, { + root: path.join(__dirname, '../tests'), + prefix: '/public/', + list: true, +}) + +fastify.register(fastifyStatic, { + root: path.join(__dirname, '../../../web/dist/artifacts'), + prefix: '/artifacts/', + decorateReply: false, +}) + +fastify.addHook('preHandler', delayMiddleware) +fastify.addHook('onSend', serverTimingMiddleware) +fastify.addHook('onSend', noCacheMiddleware) + +fastify.register(fastifyWebsockets) +fastify.register(function (f) { + f.get('/ws', { websocket: true }, (socket) => { + socket.on('message', () => { + socket.send('hi from server') + }) + }) +}) + +fastify.post('/api/v2/spans', (request, reply) => { + reply.send('') +}) + +fastify.post('/echo', (request, reply) => { + reply.send(request.body) +}) + +fastify.get('/some-data', (request, reply) => { + reply.send({ + key1: 'value1', + key2: 'value2', + key3: 'value3', + }) +}) + +fastify.get('/no-server-timings', (request, reply) => { + reply.removeHeader('Server-Timing') + reply.send() +}) + +fastify.get('/', (request, reply) => { + reply.send() +}) + +fastify.get<{ + Querystring: { + beaconEndpoint?: string + disableInstrumentation?: string + } +}>('*', async (request, reply) => { + const beaconUrl = new URL(`http://${request.headers.host}/api/v2/spans`) + const defaultFile = '/artifacts/splunk-otel-web.js' + + const parsedUrl = new URL(request.url, `http://${request.host}`) + const filePath = path.join(__dirname, '../tests', parsedUrl.pathname) + + if (fs.existsSync(filePath)) { + if (parsedUrl.pathname.endsWith('.ejs')) { + return reply.viewAsync(parsedUrl.pathname, { + renderAgent(userOpts = {}, noInit = false, file = defaultFile, cdnVersion = null) { + const options: Record = { + beaconEndpoint: beaconUrl.toString(), + applicationName: 'splunk-otel-js-dummy-app', + debug: true, + bufferTimeout: GLOBAL_TEST_BUFFER_TIMEOUT, + ...userOpts, + } + + if (typeof request.query.disableInstrumentation === 'string') { + if (!options.instrumentations) { + options.instrumentations = {} + } + + request.query.disableInstrumentation.split(',').forEach((instrumentation) => { + options.instrumentations[instrumentation] = false + }) + } + + if (typeof request.query.beaconEndpoint === 'string') { + options.beaconEndpoint = request.query.beaconEndpoint + } + + if (cdnVersion) { + file = `https://cdn.signalfx.com/o11y-gdi-rum/${cdnVersion}/splunk-otel-web.js` + } + + return ejs.render(RENDER_AGENT_TEMPLATE, { + file, + noInit, + options: JSON.stringify(options), + otelApiGlobalsFile: '/artifacts/otel-api-globals.js', + }) + }, + }) + } else { + return reply.sendFile(`${parsedUrl.pathname}`) + } + } else { + reply.status(404).send('Error: Invalid URL') + } +}) + +const PORT = Number.parseInt(process.env.PORT || '3000') + +const start = async () => { + try { + await fastify.listen({ port: PORT }) + console.log('Server is running...') + } catch (err) { + fastify.log.error(err) + process.exit(1) + } +} + +process.on('SIGINT', async () => { + console.log('Shutting down server...') + await fastify.close() + process.exit(0) +}) + +void start() diff --git a/packages/web/integration-tests/tests/cdn/index.ejs b/packages/integration-tests/src/tests/cdn/index.ejs similarity index 87% rename from packages/web/integration-tests/tests/cdn/index.ejs rename to packages/integration-tests/src/tests/cdn/index.ejs index 44b8027f..c16fa380 100644 --- a/packages/web/integration-tests/tests/cdn/index.ejs +++ b/packages/integration-tests/src/tests/cdn/index.ejs @@ -7,7 +7,7 @@ <%- renderAgent({}, false, null, 'latest' ) %> diff --git a/packages/integration-tests/src/tests/cdn/index.spec.ts b/packages/integration-tests/src/tests/cdn/index.spec.ts new file mode 100644 index 00000000..2da4624a --- /dev/null +++ b/packages/integration-tests/src/tests/cdn/index.spec.ts @@ -0,0 +1,61 @@ +/** + * + * Copyright 2024 Splunk Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ +import { expect } from '@playwright/test' +import { test } from '../../utils/test' + +test.describe('cdn', () => { + test('JS unhandled error', async ({ browserName, recordPage }) => { + await recordPage.goTo('/cdn/index.ejs') + + await recordPage.waitForSpans((spans) => spans.some((s) => s.name === 'onerror')) + const errorSpans = recordPage.receivedSpans.filter((s) => s.name === 'onerror') + + expect(errorSpans).toHaveLength(1) + + const errorSpan = errorSpans[0] + expect(errorSpan.tags['component']).toBe('error') + expect(errorSpan.tags['error']).toBe('true') + expect(errorSpan.tags['error.object']).toBe('TypeError') + + let expectedMessage = '' + + switch (browserName) { + case 'chromium': + expectedMessage = "Cannot set properties of null (setting 'prop1')" + break + case 'firefox': + expectedMessage = 'test is null' + break + case 'webkit': + expectedMessage = "null is not an object (evaluating 'test.prop1 = true')" + break + } + + expect(errorSpan.tags['error.message']).toBe(expectedMessage) + + const rumScriptFetchSpans = recordPage.receivedSpans.filter( + (s) => s.name === 'resourceFetch' && (s.tags['http.url']).includes('cdn.signalfx.com'), + ) + expect(rumScriptFetchSpans).toHaveLength(1) + const rumScriptFetchSpan = rumScriptFetchSpans[0] + expect(rumScriptFetchSpan.tags['http.url']).toBe( + 'https://cdn.signalfx.com/o11y-gdi-rum/latest/splunk-otel-web.js', + ) + expect(rumScriptFetchSpan.tags['splunk.rumVersion']).toBe('0.19.3') + }) +}) diff --git a/packages/web/integration-tests/tests/connectivity/connectivity.ejs b/packages/integration-tests/src/tests/connectivity/connectivity.ejs similarity index 100% rename from packages/web/integration-tests/tests/connectivity/connectivity.ejs rename to packages/integration-tests/src/tests/connectivity/connectivity.ejs diff --git a/packages/integration-tests/src/tests/connectivity/connectivity.spec.ts b/packages/integration-tests/src/tests/connectivity/connectivity.spec.ts new file mode 100644 index 00000000..920ae309 --- /dev/null +++ b/packages/integration-tests/src/tests/connectivity/connectivity.spec.ts @@ -0,0 +1,66 @@ +/** + * + * Copyright 2024 Splunk Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ +import { expect } from '@playwright/test' +import { test } from '../../utils/test' + +test.describe('connectivity', () => { + test('Connectivity events are captured', async ({ browserName, recordPage }) => { + if (browserName === 'firefox') { + // TODO: Investigate why setOffline is not working in firefox + test.skip() + } + + await recordPage.goTo('/connectivity/connectivity.ejs') + + await recordPage.waitForSpans((spans) => spans.some((s) => s.name === 'documentFetch')) + + // Wait some time to see if we get any connectivity spans + await recordPage.waitForTimeoutAndFlushData(1000) + const connectivitySpansBeforeOffline = recordPage.receivedSpans.filter((span) => span.name === 'connectivity') + + expect(connectivitySpansBeforeOffline.length).toBe(0) + + await recordPage.setOffline() + + await recordPage.waitForTimeoutAndFlushData(1000) + const connectivitySpansAfterOffline = recordPage.receivedSpans.filter((span) => span.name === 'connectivity') + + expect(connectivitySpansAfterOffline.length).toBe(0) + + await recordPage.setOnline() + await recordPage.waitForSpans((spans) => spans.filter((s) => s.name === 'connectivity').length >= 1) + const connectivitySpansAfterOnline = recordPage.receivedSpans.filter((span) => span.name === 'connectivity') + + expect(connectivitySpansAfterOnline).toHaveLength(2) + expect(connectivitySpansAfterOnline[0].tags['online']).toBe('false') + expect(connectivitySpansAfterOnline[1].tags['online']).toBe('true') + + expect(recordPage.receivedErrorSpans).toHaveLength(0) + + await recordPage.setOffline() + await recordPage.setOnline() + + await recordPage.waitForSpans((spans) => spans.filter((s) => s.name === 'connectivity').length >= 4) + const allConnectivitySpans = recordPage.receivedSpans.filter((span) => span.name === 'connectivity') + + expect(allConnectivitySpans).toHaveLength(4) + expect(allConnectivitySpans[2].tags['online']).toBe('false') + expect(allConnectivitySpans[3].tags['online']).toBe('true') + expect(recordPage.receivedErrorSpans).toHaveLength(0) + }) +}) diff --git a/packages/integration-tests/src/tests/context/async-context.spec.ts b/packages/integration-tests/src/tests/context/async-context.spec.ts new file mode 100644 index 00000000..51211a1b --- /dev/null +++ b/packages/integration-tests/src/tests/context/async-context.spec.ts @@ -0,0 +1,128 @@ +/** + * + * Copyright 2024 Splunk Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ +import { test } from '../../utils/test' +import { expect } from '@playwright/test' +import { RecordPage } from '../../pages/record-page' + +const runTest = async (recordPage: RecordPage, urlPath: string) => { + await recordPage.goTo(urlPath) + + await recordPage.locator('#btn1').click() + + await recordPage.waitForTestToFinish() + + const clickSpans = recordPage.receivedSpans.filter((span) => span.name === 'click') + const childSpans = recordPage.receivedSpans.filter((span) => span.name === 'context-child') + + expect(clickSpans).toHaveLength(1) + expect(clickSpans[0].parentId).toBeUndefined() + expect(childSpans).toHaveLength(1) + expect(childSpans[0].parentId).toBe(clickSpans[0].id) + + expect(recordPage.receivedErrorSpans).toHaveLength(0) +} + +test.describe('context', () => { + test('setTimeout', async ({ recordPage }) => { + await runTest(recordPage, '/context/set-timeout.ejs') + recordPage.clearReceivedSpans() + + await recordPage.locator('#btn2').click() + await recordPage.waitForTimeout(1000) + await recordPage.waitForTestToFinish() + + const clickSpans = recordPage.receivedSpans.filter((span) => span.name === 'click') + const childSpans = recordPage.receivedSpans.filter((span) => span.name === 'context-child') + + expect(clickSpans).toHaveLength(1) + expect(childSpans).toHaveLength(1) + expect(childSpans[0].parentId, "Child span in a longer timeout doesn't have a parent").toBeUndefined() + expect(recordPage.receivedErrorSpans).toHaveLength(0) + }) + + test('promise constructor', async ({ recordPage }) => { + await recordPage.goTo('/context/promise.ejs') + + for (let i = 1; i <= 5; i++) { + await recordPage.locator('#btn' + i).click() + await recordPage.waitForTimeout(1000) + await recordPage.waitForTestToFinish() + + const clickSpans = recordPage.receivedSpans.filter((span) => span.name === 'click') + const childSpans = recordPage.receivedSpans.filter((span) => span.name === 'context-child') + + expect(clickSpans).toHaveLength(1) + expect(childSpans).toHaveLength(1) + expect(childSpans[0].parentId).toBeDefined() + expect(childSpans[0].parentId, `Child span ${i} belongs to user interaction trace.`).toBe(clickSpans[0].id) + + expect(recordPage.receivedErrorSpans).toHaveLength(0) + recordPage.clearReceivedSpans() + } + }) + + test('fetch then', async ({ recordPage }) => { + await runTest(recordPage, '/context/fetch-then.ejs') + }) + + test('xhr event callback', async ({ recordPage }) => { + await runTest(recordPage, '/context/xhr-events.ejs') + }) + + test('xhr event with removal', async ({ recordPage }) => { + await runTest(recordPage, '/context/xhr-events-removed.ejs') + }) + + test('xhr onload property', async ({ recordPage }) => { + await runTest(recordPage, '/context/xhr-onload.ejs') + }) + + test('mutation observer on textNode', async ({ recordPage }) => { + await runTest(recordPage, '/context/mutation-observer-textnode.ejs') + }) + + test('MessagePort - addEventListener', async ({ recordPage }) => { + await runTest(recordPage, '/context/messageport-addeventlistener.ejs') + }) + + test('MessagePort - onmessage', async ({ recordPage }) => { + await runTest(recordPage, '/context/messageport-onmessage.ejs') + }) + + test('MessagePort: Works with cors', async ({ recordPage }) => { + await recordPage.goTo('/context/messageport-cors.ejs') + await recordPage.waitForTestToFinish() + + const messageEventSpans = recordPage.receivedSpans.filter((span) => span.name === 'message-event') + + expect(messageEventSpans).toHaveLength(1) + expect(recordPage.receivedErrorSpans).toHaveLength(0) + }) + + test('location - hashchange', async ({ recordPage }) => { + await runTest(recordPage, '/context/location-hash.ejs') + + const clickSpans = recordPage.receivedSpans.filter((span) => span.name === 'click') + const routeChangeSpans = recordPage.receivedSpans.filter((span) => span.name === 'routeChange') + + expect(clickSpans).toHaveLength(1) + expect(routeChangeSpans).toHaveLength(1) + + expect(routeChangeSpans[0].parentId).toBe(clickSpans[0].id) + }) +}) diff --git a/packages/web/integration-tests/tests/context/fetch-then.ejs b/packages/integration-tests/src/tests/context/fetch-then.ejs similarity index 100% rename from packages/web/integration-tests/tests/context/fetch-then.ejs rename to packages/integration-tests/src/tests/context/fetch-then.ejs diff --git a/packages/integration-tests/src/tests/context/framework/framework.spec.ts b/packages/integration-tests/src/tests/context/framework/framework.spec.ts new file mode 100644 index 00000000..d099e287 --- /dev/null +++ b/packages/integration-tests/src/tests/context/framework/framework.spec.ts @@ -0,0 +1,74 @@ +/** + * + * Copyright 2024 Splunk Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ +import { test } from '../../../utils/test' +import { expect } from '@playwright/test' +import { RecordPage } from '../../../pages/record-page' + +const runTest = async (recordPage: RecordPage, urlPath: string) => { + recordPage.clearReceivedSpans() + + await recordPage.goTo(urlPath) + await recordPage.locator('#btn1').click() + + await recordPage.waitForTestToFinish() + + const clickSpans = recordPage.receivedSpans.filter((span) => span.name === 'click') + const customSpans = recordPage.receivedSpans.filter((span) => span.name === 'custom-span') + + expect(clickSpans).toHaveLength(1) + expect(customSpans).toHaveLength(1) + + expect(clickSpans[0].parentId).toBeUndefined() + expect(customSpans[0].parentId).toBe(clickSpans[0].id) + expect(customSpans[0].traceId).toBe(clickSpans[0].traceId) + + expect(recordPage.receivedErrorSpans).toHaveLength(0) +} + +test.describe('framework', () => { + test('Vue 2 with async', async ({ recordPage }) => { + await runTest(recordPage, '/context/framework/vue2.ejs') + }) + + test('Vue 3 with async', async ({ recordPage }) => { + await runTest(recordPage, '/context/framework/vue3.ejs') + }) + + test('React 16 with async', async ({ recordPage }) => { + await runTest(recordPage, '/context/framework/react-16.ejs') + }) + + test('React latest with async', async ({ recordPage }) => { + await recordPage.goTo('/context/framework/react-latest.ejs') + await recordPage.locator('#btn1').click() + await recordPage.waitForTestToFinish() + + const clickSpans = recordPage.receivedSpans.filter((span) => span.name === 'click') + const customSpans = recordPage.receivedSpans.filter((span) => span.name === 'custom-span') + + expect(clickSpans.length).toBeGreaterThanOrEqual(1) + expect(customSpans).toHaveLength(1) + + // TODO: Investigate for some reason for react 17 there are several nested clicks + clickSpans.forEach((span) => { + expect(customSpans[0].traceId, span.traceId) + }) + + expect(recordPage.receivedErrorSpans).toHaveLength(0) + }) +}) diff --git a/packages/web/integration-tests/tests/context/framework/react-16.ejs b/packages/integration-tests/src/tests/context/framework/react-16.ejs similarity index 100% rename from packages/web/integration-tests/tests/context/framework/react-16.ejs rename to packages/integration-tests/src/tests/context/framework/react-16.ejs diff --git a/packages/web/integration-tests/tests/context/framework/react-latest.ejs b/packages/integration-tests/src/tests/context/framework/react-latest.ejs similarity index 83% rename from packages/web/integration-tests/tests/context/framework/react-latest.ejs rename to packages/integration-tests/src/tests/context/framework/react-latest.ejs index ccc41ab2..9f1303aa 100644 --- a/packages/web/integration-tests/tests/context/framework/react-latest.ejs +++ b/packages/integration-tests/src/tests/context/framework/react-latest.ejs @@ -10,8 +10,8 @@ } }) %> - - + + @@ -24,9 +24,9 @@ const [show, setShow] = React.useState(false); function onClick(e) { - setShow(!show); + setShow(!show); } - + return (
@@ -38,7 +38,7 @@ function Lazy() { React.useEffect(() => { const span = SplunkRum.provider.getTracer('test').startSpan('custom-span').end() - }); + }); return (

Lazy

@@ -54,4 +54,4 @@ - \ No newline at end of file + diff --git a/packages/web/integration-tests/tests/context/framework/vue2.ejs b/packages/integration-tests/src/tests/context/framework/vue2.ejs similarity index 92% rename from packages/web/integration-tests/tests/context/framework/vue2.ejs rename to packages/integration-tests/src/tests/context/framework/vue2.ejs index ac6ded46..4564484b 100644 --- a/packages/web/integration-tests/tests/context/framework/vue2.ejs +++ b/packages/integration-tests/src/tests/context/framework/vue2.ejs @@ -15,7 +15,7 @@

Vue 2

-
+
@@ -27,7 +27,7 @@ } }) - var app = new Vue({ + const app = new Vue({ el: '#app', data: { toggle: false @@ -35,4 +35,4 @@ }) - \ No newline at end of file + diff --git a/packages/web/integration-tests/tests/context/framework/vue3.ejs b/packages/integration-tests/src/tests/context/framework/vue3.ejs similarity index 100% rename from packages/web/integration-tests/tests/context/framework/vue3.ejs rename to packages/integration-tests/src/tests/context/framework/vue3.ejs diff --git a/packages/web/integration-tests/tests/context/location-hash.ejs b/packages/integration-tests/src/tests/context/location-hash.ejs similarity index 100% rename from packages/web/integration-tests/tests/context/location-hash.ejs rename to packages/integration-tests/src/tests/context/location-hash.ejs diff --git a/packages/web/integration-tests/tests/context/messageport-addeventlistener.ejs b/packages/integration-tests/src/tests/context/messageport-addeventlistener.ejs similarity index 95% rename from packages/web/integration-tests/tests/context/messageport-addeventlistener.ejs rename to packages/integration-tests/src/tests/context/messageport-addeventlistener.ejs index 90b19eef..2af397d4 100644 --- a/packages/web/integration-tests/tests/context/messageport-addeventlistener.ejs +++ b/packages/integration-tests/src/tests/context/messageport-addeventlistener.ejs @@ -16,7 +16,7 @@ - - + + diff --git a/packages/web/integration-tests/tests/docload/docload-ignored.ejs b/packages/integration-tests/src/tests/docload/docload-ignored.ejs similarity index 100% rename from packages/web/integration-tests/tests/docload/docload-ignored.ejs rename to packages/integration-tests/src/tests/docload/docload-ignored.ejs diff --git a/packages/web/integration-tests/tests/docload/docload.ejs b/packages/integration-tests/src/tests/docload/docload.ejs similarity index 100% rename from packages/web/integration-tests/tests/docload/docload.ejs rename to packages/integration-tests/src/tests/docload/docload.ejs diff --git a/packages/integration-tests/src/tests/docload/docload.spec.ts b/packages/integration-tests/src/tests/docload/docload.spec.ts new file mode 100644 index 00000000..f6dfa48f --- /dev/null +++ b/packages/integration-tests/src/tests/docload/docload.spec.ts @@ -0,0 +1,137 @@ +/** + * + * Copyright 2024 Splunk Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ +import { expect } from '@playwright/test' +import { test } from '../../utils/test' +import { timesMakeSense } from '../../utils/time-make-sense' + +test.describe('docload', () => { + test('resources before load event are correctly captured', async ({ recordPage }) => { + await recordPage.goTo('/docload/docload-all.ejs') + + await recordPage.waitForSpans((spans) => spans.filter((span) => span.name === 'documentLoad').length === 1) + const docLoadSpans = recordPage.receivedSpans.filter((span) => span.name === 'documentLoad') + + expect(docLoadSpans).toHaveLength(1) + + const resources = ['css-font-img.css', 'splunk-black.png?delay=300', 'iframe.ejs', 'splunk.woff'] + for (const urlEnd of resources) { + const resourceSpans = recordPage.receivedSpans.filter( + (span) => span.tags['http.url'] && (span.tags['http.url'] as string).endsWith(urlEnd), + ) + + expect(docLoadSpans[0].traceId, `${urlEnd} has correct traceId`).toBe(resourceSpans[0].traceId) + } + + const ignoredResources = ['/some-data', '/some-data?delay=1', '/api/v2/spans'] + for (const urlEnd of ignoredResources) { + const resourceSpans = recordPage.receivedSpans.filter( + (span) => + span.tags['component'] === 'document-load' && + typeof span.tags['http.url'] === 'string' && + span.tags['http.url'].endsWith(urlEnd), + ) + + expect(resourceSpans, `${urlEnd} is not captured`).toHaveLength(0) + } + }) + + test('documentFetch, resourceFetch, and documentLoad spans', async ({ recordPage }) => { + await recordPage.goTo('/docload/docload.ejs') + + await recordPage.waitForSpans((spans) => spans.filter((span) => span.name === 'documentLoad').length === 1) + const docLoadSpans = recordPage.receivedSpans.filter((span) => span.name === 'documentLoad') + const docFetchSpans = recordPage.receivedSpans.filter((span) => span.name === 'documentFetch') + const scriptFetchSpans = recordPage.receivedSpans.filter( + (span) => + span.name === 'resourceFetch' && + typeof span.tags['http.url'] === 'string' && + span.tags['http.url'].includes('splunk-otel-web.js'), + ) + const brokenImageFetchSpans = recordPage.receivedSpans.filter( + (span) => + span.name === 'resourceFetch' && + typeof span.tags['http.url'] === 'string' && + span.tags['http.url'].includes('/nosuchimage.jpg'), + ) + + expect(docFetchSpans).toHaveLength(1) + expect(docLoadSpans).toHaveLength(1) + expect(docLoadSpans[0].traceId.match(/[a-f0-9]+/), 'Checking sanity of traceId').toBeTruthy() + expect(docLoadSpans[0].id.match(/[a-f0-9]+/), 'Checking sanity of id').toBeTruthy() + expect(docFetchSpans[0].traceId).toBe(docLoadSpans[0].traceId) + expect(docFetchSpans[0].parentId).toBe(docLoadSpans[0].id) + + expect(scriptFetchSpans).toHaveLength(1) + expect(scriptFetchSpans[0].traceId).toBe(docLoadSpans[0].traceId) + expect(scriptFetchSpans[0].parentId).toBe(docLoadSpans[0].id) + expect(scriptFetchSpans[0].tags['component']).toBe('document-load') + + expect(brokenImageFetchSpans.length).toBeGreaterThanOrEqual(1) + expect(brokenImageFetchSpans[0].traceId).toBe(docLoadSpans[0].traceId) + expect(brokenImageFetchSpans[0].parentId).toBe(docLoadSpans[0].id) + + expect(docFetchSpans[0].tags['component']).toBe('document-load') + expect(docLoadSpans[0].tags['location.href']).toBe('http://localhost:3000/docload/docload.ejs') + + timesMakeSense(docFetchSpans[0].annotations, 'domainLookupStart', 'domainLookupEnd') + timesMakeSense(docFetchSpans[0].annotations, 'connectStart', 'connectEnd') + timesMakeSense(docFetchSpans[0].annotations, 'requestStart', 'responseStart') + timesMakeSense(docFetchSpans[0].annotations, 'responseStart', 'responseEnd') + timesMakeSense(docFetchSpans[0].annotations, 'fetchStart', 'responseEnd') + + expect(docFetchSpans[0].tags['link.traceId']).toBeDefined() + expect(docFetchSpans[0].tags['link.spanId']).toBeDefined() + expect(parseInt(scriptFetchSpans[0].tags['http.response_content_length'] as string)).toBeGreaterThan(0) + + expect(docLoadSpans[0].tags['component']).toBe('document-load') + expect(docLoadSpans[0].tags['location.href']).toBe('http://localhost:3000/docload/docload.ejs') + expect( + (docLoadSpans[0].tags['screen.xy'] as string).match(/[0-9]+x[0-9]+/), + 'Checking sanity of screen.xy', + ).toBeTruthy() + + timesMakeSense(docLoadSpans[0].annotations, 'domContentLoadedEventStart', 'domContentLoadedEventEnd') + timesMakeSense(docLoadSpans[0].annotations, 'loadEventStart', 'loadEventEnd') + timesMakeSense(docLoadSpans[0].annotations, 'fetchStart', 'domInteractive') + timesMakeSense(docLoadSpans[0].annotations, 'fetchStart', 'domComplete') + + expect(recordPage.receivedErrorSpans).toHaveLength(0) + }) + + test('ignoring resource URLs', async ({ recordPage }) => { + await recordPage.goTo('/docload/docload-ignored.ejs') + + await recordPage.waitForSpans((spans) => spans.filter((span) => span.name === 'documentFetch').length === 1) + const ignoredResourceFetchSpans = recordPage.receivedSpans.filter( + (span) => span.tags['http.url'] === 'http://localhost:3000/non-impactful-resource.jpg', + ) + + expect(ignoredResourceFetchSpans).toHaveLength(0) + }) + + test('module can be disabled', async ({ recordPage }) => { + await recordPage.goTo('/docload/docload.ejs?disableInstrumentation=document') + + await recordPage.waitForTimeoutAndFlushData(1000) + const SPAN_TYPES = ['documentFetch', 'documentLoad', 'resourceFetch'] + const documentSpans = recordPage.receivedSpans.filter((span) => SPAN_TYPES.includes(span.name)) + + expect(documentSpans).toHaveLength(0) + expect(recordPage.receivedErrorSpans).toHaveLength(0) + }) +}) diff --git a/packages/web/integration-tests/tests/docload/iframe.ejs b/packages/integration-tests/src/tests/docload/iframe.ejs similarity index 100% rename from packages/web/integration-tests/tests/docload/iframe.ejs rename to packages/integration-tests/src/tests/docload/iframe.ejs diff --git a/packages/integration-tests/src/tests/errors/errors.spec.ts b/packages/integration-tests/src/tests/errors/errors.spec.ts new file mode 100644 index 00000000..68e12db0 --- /dev/null +++ b/packages/integration-tests/src/tests/errors/errors.spec.ts @@ -0,0 +1,179 @@ +/** + * + * Copyright 2024 Splunk Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ +import { expect } from '@playwright/test' +import { test } from '../../utils/test' + +test.describe('errors', () => { + test('DOM resource 4xx', async ({ recordPage }) => { + await recordPage.goTo('/errors/views/resource-4xx.ejs') + await recordPage.waitForSpans( + (spans) => spans.filter((span) => span.name === 'eventListener.error').length === 1, + ) + const errorSpans = recordPage.receivedSpans.filter((span) => span.name === 'eventListener.error') + + expect(errorSpans).toHaveLength(1) + + expect(errorSpans[0].tags['component']).toBe('error') + expect(errorSpans[0].tags['error.type']).toBe('error') + expect(errorSpans[0].tags['target_element']).toBe('IMG') + expect(errorSpans[0].tags['target_xpath']).toBe('//html/body/img') + expect( + (errorSpans[0].tags['target_src'] as string).endsWith('/nonexistent.png'), + `Checking target_src: ${errorSpans[0]['target_src']}`, + ).toBeTruthy() + }) + + test('JS syntax error', async ({ recordPage, browserName }) => { + await recordPage.goTo('/errors/views/js-syntax-error.ejs') + await recordPage.waitForSpans((spans) => spans.filter((span) => span.name === 'onerror').length === 1) + const errorSpans = recordPage.receivedSpans.filter((span) => span.name === 'onerror') + + expect(errorSpans).toHaveLength(1) + + expect(errorSpans[0].tags['component']).toBe('error') + expect(errorSpans[0].tags['error']).toBe('true') + expect(errorSpans[0].tags['error.object']).toBe('SyntaxError') + + const errorMessageMap = { + chromium: "Unexpected token ';'", + firefox: "expected expression, got ';'", + webkit: "Unexpected token ';'", + } + + expect(errorSpans[0].tags['error.message']).toBe(errorMessageMap[browserName]) + }) + + test('JS unhandled error', async ({ recordPage, browserName }) => { + await recordPage.goTo('/errors/views/unhandled-error.ejs') + await recordPage.waitForSpans((spans) => spans.filter((span) => span.name === 'onerror').length === 1) + const errorSpans = recordPage.receivedSpans.filter((span) => span.name === 'onerror') + + expect(errorSpans).toHaveLength(1) + + expect(errorSpans[0].tags['component']).toBe('error') + expect(errorSpans[0].tags['error']).toBe('true') + expect(errorSpans[0].tags['error.object']).toBe('TypeError') + + const errorMessageMap = { + chromium: "Cannot set properties of null (setting 'prop1')", + firefox: 'test is null', + webkit: "null is not an object (evaluating 'test.prop1 = true')", + } + + expect(errorSpans[0].tags['error.message']).toBe(errorMessageMap[browserName]) + }) + + test('unhandled promise rejection', async ({ recordPage }) => { + await recordPage.goTo('/errors/views/unhandled-rejection.ejs') + await recordPage.waitForSpans( + (spans) => spans.filter((span) => span.name === 'unhandledrejection').length === 1, + ) + const errorSpans = recordPage.receivedSpans.filter((span) => span.name === 'unhandledrejection') + + expect(errorSpans).toHaveLength(1) + expect(errorSpans[0].tags['component']).toBe('error') + expect(errorSpans[0].tags['error']).toBe('true') + expect(errorSpans[0].tags['error.object']).toBe('String') + expect(errorSpans[0].tags['error.message']).toBe('rejection-value') + }) + + test('manual console.error', async ({ recordPage, browserName }) => { + await recordPage.goTo('/errors/views/console-error.ejs') + await recordPage.waitForSpans((spans) => spans.filter((span) => span.name === 'console.error').length === 1) + const errorSpans = recordPage.receivedSpans.filter((span) => span.name === 'console.error') + + expect(errorSpans).toHaveLength(1) + expect(errorSpans[0].tags['component']).toBe('error') + expect(errorSpans[0].tags['error']).toBe('true') + expect(errorSpans[0].tags['error.object']).toBe('TypeError') + + const errorMessageMap = { + chromium: "Cannot set properties of null (setting 'anyField')", + firefox: 'someNull is null', + webkit: "null is not an object (evaluating 'someNull.anyField = 'value'')", + } + + expect(errorSpans[0].tags['error.message']).toBe(errorMessageMap[browserName]) + }) + + test('SplunkRum.error', async ({ recordPage, browserName }) => { + const url = 'http://localhost:3000/errors/views/splunkrum-error.ejs' + await recordPage.goTo(url) + await recordPage.waitForSpans((spans) => spans.filter((span) => span.name === 'SplunkRum.error').length === 1) + const errorSpans = recordPage.receivedSpans.filter((span) => span.name === 'SplunkRum.error') + + expect(errorSpans).toHaveLength(1) + expect(errorSpans[0].tags['component']).toBe('error') + expect(errorSpans[0].tags['error']).toBe('true') + expect(errorSpans[0].tags['error.object']).toBe('TypeError') + + const errorMessages = { + chromium: "Cannot set properties of null (setting 'anyField')", + firefox: 'someNull is null', + webkit: "null is not an object (evaluating 'someNull.anyField = 'value'')", + } + + expect(errorSpans[0].tags['error.message']).toBe(errorMessages[browserName]) + + const errorStackMap = { + chromium: `TypeError: Cannot set properties of null (setting 'anyField')\n at ${url}:63:25`, + firefox: `@${url}:63:7\n`, + webkit: `global code@${url}:63:15`, + } + + expect(errorSpans[0].tags['error.stack']).toBe(errorStackMap[browserName]) + }) + + test('module can be disabled', async ({ recordPage }) => { + await recordPage.goTo('/errors/views/unhandled-error.ejs?disableInstrumentation=errors') + await recordPage.waitForTimeoutAndFlushData(1000) + const errorSpans = recordPage.receivedSpans.filter((span) => span.name === 'onerror') + + expect(errorSpans).toHaveLength(0) + }) + + test('minified script with source map id', async ({ recordPage }) => { + await recordPage.goTo('/errors/views/minified-file-errors.ejs') + await recordPage.locator('#button1').click() + + await recordPage.waitForSpans((spans) => spans.filter((span) => span.name === 'onerror').length === 1) + const errorSpans = recordPage.receivedSpans.filter((span) => span.name === 'onerror') + + expect(errorSpans).toHaveLength(1) + expect(errorSpans[0].tags['error.message']).toBe('Error from script1.js') + expect((errorSpans[0].tags['error.source_map_ids'] as string).includes('script1.min.js')).toBeTruthy() + expect( + (errorSpans[0].tags['error.source_map_ids'] as string).includes( + '9663c60664c425cef3b141c167e9b324240ce10ae488726293892b7130266a6b', + ), + ).toBeTruthy() + + await recordPage.locator('#button2').click() + await recordPage.waitForSpans((spans) => spans.filter((span) => span.name === 'onerror').length === 2) + const errorSpans2 = recordPage.receivedSpans.filter((span) => span.name === 'onerror') + + expect(errorSpans2).toHaveLength(2) + expect(errorSpans2[1].tags['error.message']).toBe('Error from script2.js') + expect((errorSpans2[1].tags['error.source_map_ids'] as string).includes('script2.min.js')).toBeTruthy() + expect( + (errorSpans2[1].tags['error.source_map_ids'] as string).includes( + '9793573cdc2ab308a0b1996bea14253ec8832bc036210475ded0813cafa27066', + ), + ).toBeTruthy() + }) +}) diff --git a/packages/web/integration-tests/tests/errors/views/console-error.ejs b/packages/integration-tests/src/tests/errors/views/console-error.ejs similarity index 93% rename from packages/web/integration-tests/tests/errors/views/console-error.ejs rename to packages/integration-tests/src/tests/errors/views/console-error.ejs index babfa642..e193b851 100644 --- a/packages/web/integration-tests/tests/errors/views/console-error.ejs +++ b/packages/integration-tests/src/tests/errors/views/console-error.ejs @@ -5,7 +5,7 @@ Errors test <%- renderAgent({}) %> - + + diff --git a/packages/web/integration-tests/tests/errors/views/resource-4xx.ejs b/packages/integration-tests/src/tests/errors/views/resource-4xx.ejs similarity index 78% rename from packages/web/integration-tests/tests/errors/views/resource-4xx.ejs rename to packages/integration-tests/src/tests/errors/views/resource-4xx.ejs index ff695a76..53968579 100644 --- a/packages/web/integration-tests/tests/errors/views/resource-4xx.ejs +++ b/packages/integration-tests/src/tests/errors/views/resource-4xx.ejs @@ -3,7 +3,7 @@ Errors test - <%- renderAgent({allowInsecureBeacon: true}) %> + <%- renderAgent({ allowInsecureBeacon: true }) %>

Dom parsing error simulation

diff --git a/packages/web/integration-tests/tests/errors/views/script1.min.js b/packages/integration-tests/src/tests/errors/views/script1.min.js similarity index 100% rename from packages/web/integration-tests/tests/errors/views/script1.min.js rename to packages/integration-tests/src/tests/errors/views/script1.min.js diff --git a/packages/web/integration-tests/tests/errors/views/script2.min.js b/packages/integration-tests/src/tests/errors/views/script2.min.js similarity index 100% rename from packages/web/integration-tests/tests/errors/views/script2.min.js rename to packages/integration-tests/src/tests/errors/views/script2.min.js diff --git a/packages/web/integration-tests/tests/errors/views/splunkrum-error.ejs b/packages/integration-tests/src/tests/errors/views/splunkrum-error.ejs similarity index 94% rename from packages/web/integration-tests/tests/errors/views/splunkrum-error.ejs rename to packages/integration-tests/src/tests/errors/views/splunkrum-error.ejs index 1fbb0035..a3ef1fb9 100644 --- a/packages/web/integration-tests/tests/errors/views/splunkrum-error.ejs +++ b/packages/integration-tests/src/tests/errors/views/splunkrum-error.ejs @@ -5,7 +5,7 @@ Errors test <%- renderAgent({}) %> diff --git a/packages/web/integration-tests/tests/resource-observer/resources-ignored.ejs b/packages/integration-tests/src/tests/resource-observer/resources-ignored.ejs similarity index 67% rename from packages/web/integration-tests/tests/resource-observer/resources-ignored.ejs rename to packages/integration-tests/src/tests/resource-observer/resources-ignored.ejs index a6bb7116..e0477a85 100644 --- a/packages/web/integration-tests/tests/resource-observer/resources-ignored.ejs +++ b/packages/integration-tests/src/tests/resource-observer/resources-ignored.ejs @@ -3,11 +3,11 @@ Resource observer ignore test - + <%- renderAgent({}, true) %> + <%- renderAgent({instrumentations: {socketio: true}}) %> @@ -13,9 +13,9 @@ + <%- renderAgent() %> diff --git a/packages/integration-tests/src/tests/synthetics/synthetics.spec.ts b/packages/integration-tests/src/tests/synthetics/synthetics.spec.ts new file mode 100644 index 00000000..4925ffb2 --- /dev/null +++ b/packages/integration-tests/src/tests/synthetics/synthetics.spec.ts @@ -0,0 +1,40 @@ +/** + * + * Copyright 2024 Splunk Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ +import { test } from '../../utils/test' +import { expect } from '@playwright/test' + +test.describe('synthetics', () => { + test('applies synthetics run Id attribute on all spans', async ({ recordPage }) => { + const syntheticsRunId = 'abcd1234'.repeat(4) + await recordPage.goTo('/synthetics/index.ejs?syntheticsRunId=' + syntheticsRunId) + + await recordPage.waitForSpans((spans) => spans.filter((span) => span.name === 'guard-span').length === 1) + const receivedSpans = recordPage.receivedSpans + + expect(receivedSpans.every((span) => span.tags['Synthetics-RunId'] === syntheticsRunId)).toBeTruthy() + }) + + test('applies synthetics run id attribute on all spans', async ({ recordPage }) => { + await recordPage.goTo('/synthetics/index.ejs') + + await recordPage.waitForSpans((spans) => spans.filter((span) => span.name === 'guard-span').length === 1) + const receivedSpans = recordPage.receivedSpans + + expect(receivedSpans.every((span) => span.tags['Synthetics-RunId'] === undefined)).toBeTruthy() + }) +}) diff --git a/packages/web/integration-tests/tests/user-interaction/causality.ejs b/packages/integration-tests/src/tests/user-interaction/causality.ejs similarity index 100% rename from packages/web/integration-tests/tests/user-interaction/causality.ejs rename to packages/integration-tests/src/tests/user-interaction/causality.ejs diff --git a/packages/integration-tests/src/tests/user-interaction/causality.spec.ts b/packages/integration-tests/src/tests/user-interaction/causality.spec.ts new file mode 100644 index 00000000..b6fcf6d9 --- /dev/null +++ b/packages/integration-tests/src/tests/user-interaction/causality.spec.ts @@ -0,0 +1,60 @@ +/** + * + * Copyright 2024 Splunk Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ +import { test } from '../../utils/test' +import { expect } from '@playwright/test' + +test.describe('causality', () => { + test('handles causality of a mouse click', async ({ recordPage }) => { + await recordPage.goTo('/user-interaction/causality.ejs') + + await recordPage.locator('body').click() + await recordPage.locator('#btn1').click() + + await recordPage.waitForSpans( + (spans) => + spans.filter((span) => span.name === 'click' && span.tags['target_xpath'] === '//*[@id="btn1"]') + .length === 1 && + spans.filter((span) => span.tags['http.url'] === 'http://localhost:3000/some-data').length === 1, + ) + + const clickSpans = recordPage.receivedSpans.filter( + (span) => span.name === 'click' && span.tags['target_xpath'] === '//*[@id="btn1"]', + ) + const fetchSpans = recordPage.receivedSpans.filter( + (span) => span.tags['http.url'] === 'http://localhost:3000/some-data', + ) + + expect(clickSpans).toHaveLength(1) + expect(fetchSpans).toHaveLength(1) + expect(fetchSpans[0].parentId).toBe(clickSpans[0].id) + expect(recordPage.receivedErrorSpans).toHaveLength(0) + }) + + test('can be disabled', async ({ recordPage }) => { + await recordPage.goTo('/user-interaction/causality.ejs?disableInstrumentation=interactions') + + await recordPage.locator('body').click() + await recordPage.locator('#btn1').click() + + await recordPage.waitForTimeoutAndFlushData(1000) + + const clickSpans = recordPage.receivedSpans.filter((span) => span.name === 'click') + + expect(clickSpans).toHaveLength(0) + }) +}) diff --git a/packages/web/integration-tests/tests/user-interaction/forms.ejs b/packages/integration-tests/src/tests/user-interaction/forms.ejs similarity index 100% rename from packages/web/integration-tests/tests/user-interaction/forms.ejs rename to packages/integration-tests/src/tests/user-interaction/forms.ejs diff --git a/packages/integration-tests/src/tests/user-interaction/forms.spec.ts b/packages/integration-tests/src/tests/user-interaction/forms.spec.ts new file mode 100644 index 00000000..36d3ec7c --- /dev/null +++ b/packages/integration-tests/src/tests/user-interaction/forms.spec.ts @@ -0,0 +1,38 @@ +/** + * + * Copyright 2024 Splunk Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ +import { test } from '../../utils/test' +import { expect } from '@playwright/test' + +test.describe('forms', () => { + test('handles form submit', async ({ recordPage }) => { + await recordPage.goTo('/user-interaction/forms.ejs') + + await recordPage.locator('#btnSubmit').click() + + await recordPage.waitForSpans((spans) => spans.filter((span) => span.name === 'submit').length === 1) + + const submitSpans = recordPage.receivedSpans.filter((span) => span.name === 'submit') + + expect(submitSpans).toHaveLength(1) + expect(submitSpans[0].tags['component']).toBe('user-interaction') + expect(submitSpans[0].tags['event_type']).toBe('submit') + expect(submitSpans[0].tags['target_element']).toBe('FORM') + expect(submitSpans[0].tags['target_xpath']).toBe('//*[@id="form1"]') + expect(recordPage.receivedErrorSpans).toHaveLength(0) + }) +}) diff --git a/packages/web/integration-tests/tests/user-interaction/history.ejs b/packages/integration-tests/src/tests/user-interaction/history.ejs similarity index 100% rename from packages/web/integration-tests/tests/user-interaction/history.ejs rename to packages/integration-tests/src/tests/user-interaction/history.ejs diff --git a/packages/integration-tests/src/tests/user-interaction/history.spec.ts b/packages/integration-tests/src/tests/user-interaction/history.spec.ts new file mode 100644 index 00000000..50e10754 --- /dev/null +++ b/packages/integration-tests/src/tests/user-interaction/history.spec.ts @@ -0,0 +1,61 @@ +/** + * + * Copyright 2024 Splunk Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ +import { test } from '../../utils/test' +import { expect } from '@playwright/test' + +test.describe('history', () => { + test('handles hash navigation', async ({ recordPage }) => { + await recordPage.goTo('/user-interaction/history.ejs') + + await recordPage.locator('#btnGoToPage').click() + + await recordPage.waitForSpans((spans) => spans.filter((span) => span.name === 'routeChange').length === 1) + const navigationSpans = recordPage.receivedSpans.filter((span) => span.name === 'routeChange') + + expect(navigationSpans).toHaveLength(1) + expect(navigationSpans[0].tags['component']).toBe('user-interaction') + expect(navigationSpans[0].tags['prev.href']).toBe('http://localhost:3000/user-interaction/history.ejs') + expect(navigationSpans[0].tags['location.href']).toBe( + 'http://localhost:3000/user-interaction/history.ejs#another-page', + ) + expect(recordPage.receivedErrorSpans).toHaveLength(0) + }) + + test('handles history navigation', async ({ recordPage }) => { + await recordPage.goTo('/user-interaction/history.ejs') + + await recordPage.locator('#btnGoToPage').click() + await recordPage.locator('#btnGoBack').click() + await recordPage.locator('#btnGoForward').click() + + await recordPage.waitForSpans((spans) => spans.filter((span) => span.name === 'routeChange').length === 3) + const navigationSpans = recordPage.receivedSpans.filter((span) => span.name === 'routeChange') + + expect(navigationSpans).toHaveLength(3) + expect(navigationSpans[1].tags['component']).toBe('user-interaction') + expect(navigationSpans[1].tags['prev.href']).toBe( + 'http://localhost:3000/user-interaction/history.ejs#another-page', + ) + expect(navigationSpans[1].tags['location.href']).toBe('http://localhost:3000/user-interaction/history.ejs') + expect(navigationSpans[2].tags['component']).toBe('user-interaction') + expect(navigationSpans[2].tags['prev.href']).toBe('http://localhost:3000/user-interaction/history.ejs') + expect(navigationSpans[2].tags['location.href']).toBe( + 'http://localhost:3000/user-interaction/history.ejs#another-page', + ) + }) +}) diff --git a/packages/web/integration-tests/tests/user-interaction/keyboard.ejs b/packages/integration-tests/src/tests/user-interaction/keyboard.ejs similarity index 96% rename from packages/web/integration-tests/tests/user-interaction/keyboard.ejs rename to packages/integration-tests/src/tests/user-interaction/keyboard.ejs index 2e70f68c..b6f88451 100644 --- a/packages/web/integration-tests/tests/user-interaction/keyboard.ejs +++ b/packages/integration-tests/src/tests/user-interaction/keyboard.ejs @@ -22,6 +22,7 @@ diff --git a/packages/integration-tests/src/tests/xhr/xhr.spec.ts b/packages/integration-tests/src/tests/xhr/xhr.spec.ts new file mode 100644 index 00000000..34c2e0eb --- /dev/null +++ b/packages/integration-tests/src/tests/xhr/xhr.spec.ts @@ -0,0 +1,68 @@ +/** + * + * Copyright 2024 Splunk Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ +import { test } from '../../utils/test' +import { expect } from '@playwright/test' + +test.describe('xhr', () => { + test('XHR request is registered', async ({ recordPage }) => { + await recordPage.goTo('/xhr/xhr-basic.ejs') + await recordPage.waitForSpans( + (spans) => spans.filter((span) => span.tags['http.url'] === 'http://localhost:3000/some-data').length === 1, + ) + const xhrSpans = recordPage.receivedSpans.filter( + (span) => span.tags['http.url'] === 'http://localhost:3000/some-data', + ) + + expect(xhrSpans).toHaveLength(1) + expect(xhrSpans[0].tags['component']).toBe('xml-http-request') + expect(xhrSpans[0].tags['http.status_code']).toBe('200') + expect(xhrSpans[0].tags['http.status_text']).toBe('OK') + expect(xhrSpans[0].tags['http.method']).toBe('GET') + expect(xhrSpans[0].tags['http.url']).toBe('http://localhost:3000/some-data') + expect(xhrSpans[0].tags['http.response_content_length']).toBe('49') + expect(xhrSpans[0].tags['link.traceId']).toBeTruthy() + expect(xhrSpans[0].tags['link.spanId']).toBeTruthy() + }) + + test('module can be disabled', async ({ recordPage }) => { + await recordPage.goTo('/xhr/xhr-basic.ejs?disableInstrumentation=xhr') + await recordPage.waitForTimeout(1000) + + const xhrSpans = recordPage.receivedSpans.filter( + (span) => span.tags['http.url'] === 'http://localhost:3000/some-data', + ) + + expect(xhrSpans).toHaveLength(0) + }) + + test('XHR request can be ignored', async ({ recordPage }) => { + await recordPage.goTo('/xhr/xhr-ignored.ejs') + + await recordPage.waitForSpans((spans) => spans.filter((span) => span.name === 'guard-span').length === 1) + + const xhrSpans = recordPage.receivedSpans.filter((span) => + ['http://localhost:3000/some-data', 'http://localhost:3000/no-server-timings'].includes( + span.tags['http.url'] as string, + ), + ) + const guardSpans = recordPage.receivedSpans.filter((span) => span.name === 'guard-span') + + expect(xhrSpans).toHaveLength(0) + expect(guardSpans).toHaveLength(1) + }) +}) diff --git a/packages/web/integration-tests/tests/xhr/xhr-ignored.spec.js b/packages/integration-tests/src/utils/test.ts similarity index 54% rename from packages/web/integration-tests/tests/xhr/xhr-ignored.spec.js rename to packages/integration-tests/src/utils/test.ts index 336f9de5..1f6a35c4 100644 --- a/packages/web/integration-tests/tests/xhr/xhr-ignored.spec.js +++ b/packages/integration-tests/src/utils/test.ts @@ -16,17 +16,22 @@ * */ -module.exports = { - 'XHR request can be ignored': async function (browser) { - await browser.url(browser.globals.getUrl('/xhr/views/xhr-ignored.ejs')) +import { test as base } from '@playwright/test' - await browser.globals.findSpan((span) => span.name === 'guard-span') +import { RecordPage } from '../pages/record-page' - await browser.assert.not.ok( - browser.globals.getReceivedSpans().find((span) => span.tags['http.url'] === '/some-data'), - ) - await browser.assert.not.ok( - browser.globals.getReceivedSpans().find((span) => span.tags['http.url'] === '/no-server-timings'), - ) - }, +type TestFixtures = { + recordPage: RecordPage } + +export const test = base.extend({ + recordPage: async ({ browser }, use) => { + const recordingContext = await browser.newContext({ viewport: { height: 1080, width: 1920 } }) + const recordPage = new RecordPage(await recordingContext.newPage(), recordingContext) + await recordPage.mockNetwork() + + await use(recordPage) + + await recordingContext.close() + }, +}) diff --git a/packages/integration-tests/src/utils/time-make-sense.ts b/packages/integration-tests/src/utils/time-make-sense.ts new file mode 100644 index 00000000..9f5c11d6 --- /dev/null +++ b/packages/integration-tests/src/utils/time-make-sense.ts @@ -0,0 +1,50 @@ +/** + * + * Copyright 2024 Splunk Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ +import { expect } from '@playwright/test' + +export const timesMakeSense = ( + annotations: Array<{ timestamp: number; value: string }>, + startName: string, + endName: string, +) => { + const annotationsObject: Record = {} + annotations.forEach((event) => { + annotationsObject[event.value] = event.timestamp + }) + + expect(annotationsObject[startName]).toBeTruthy() + expect(annotationsObject[endName]).toBeTruthy() + + expect(annotationsObject[startName]).toBeLessThanOrEqual(annotationsObject[endName]) + + const diff = annotationsObject[endName] - annotationsObject[startName] + const fiveMinutes = 5 * 60 * 1000 * 1000 + + // Sanity check for time difference + expect(diff).toBeLessThanOrEqual(fiveMinutes) + + // Also looking for rough synchronization with reality (at least from our CI systems/laptops...) + const nowMicros = new Date().getTime() * 1000 + let clockSkew = annotationsObject[startName] - nowMicros + if (clockSkew < 0) { + clockSkew = -clockSkew + } + + // Sanity check for clock skew + expect(clockSkew).toBeLessThanOrEqual(fiveMinutes) +} diff --git a/packages/web/utils/devServer/runDevServer.js b/packages/integration-tests/src/version.ts similarity index 80% rename from packages/web/utils/devServer/runDevServer.js rename to packages/integration-tests/src/version.ts index 1d7e2e48..09751ad4 100644 --- a/packages/web/utils/devServer/runDevServer.js +++ b/packages/integration-tests/src/version.ts @@ -16,5 +16,5 @@ * */ -const { buildInstrumentationBackend } = require('../testBackendProvider') -buildInstrumentationBackend({ enableHttps: false, hostname: 'localhost' }) +// this is an autogenerated file, see scripts/version-update.js +export const VERSION = '0.20.0-beta.0' diff --git a/packages/integration-tests/tsconfig.json b/packages/integration-tests/tsconfig.json new file mode 100644 index 00000000..0cd8a3bb --- /dev/null +++ b/packages/integration-tests/tsconfig.json @@ -0,0 +1,8 @@ +{ + "include": ["**/*.ts"], + "compilerOptions": { + "allowSyntheticDefaultImports": true, + "target": "esnext", + "moduleResolution": "nodenext" + } +} diff --git a/packages/web/integration-tests/tests/cdn/index.spec.js b/packages/web/integration-tests/tests/cdn/index.spec.js deleted file mode 100644 index 41fbdf30..00000000 --- a/packages/web/integration-tests/tests/cdn/index.spec.js +++ /dev/null @@ -1,60 +0,0 @@ -/** - * - * Copyright 2024 Splunk Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -module.exports = { - '@tags': ['skip-ie11'], - 'JS unhandled error': async function (browser) { - browser.globals.clearReceivedSpans() - await browser.url(browser.globals.getUrl('/cdn/index.ejs')) - - const errorSpan = await browser.globals.findSpan((s) => s.name === 'onerror') - await browser.assert.ok(!!errorSpan, 'Checking presence of error span.') - - const tags = errorSpan.tags - await browser.assert.strictEqual(tags['component'], 'error') - await browser.assert.strictEqual(tags['error'], 'true') - await browser.assert.strictEqual(tags['error.object'], 'TypeError') - - switch (browser.options.desiredCapabilities.browserName.toLowerCase()) { - case 'chrome': - await browser.assert.strictEqual( - tags['error.message'], - "Cannot set properties of null (setting 'prop1')", - ) - break - case 'firefox': - await browser.assert.strictEqual(tags['error.message'], 'test is null') - break - case 'safari': - await browser.assert.strictEqual( - tags['error.message'], - "null is not an object (evaluating 'test.prop1 = true')", - ) - break - } - - const rumScriptFetchSpan = await browser.globals.findSpan( - (s) => s.name === 'resourceFetch' && s.tags['http.url'].includes('cdn.signalfx.com'), - ) - await browser.assert.ok(!!rumScriptFetchSpan, 'Checking presence of splunk-otel-web fetch span.') - - const cdnUrl = 'https://cdn.signalfx.com/o11y-gdi-rum/latest/splunk-otel-web.js' - await browser.assert.strictEqual(rumScriptFetchSpan.tags['http.url'], cdnUrl) - await browser.assert.strictEqual(rumScriptFetchSpan.tags['splunk.rumVersion'], '0.19.3') - }, -} diff --git a/packages/web/integration-tests/tests/connectivity/connectivity.spec.js b/packages/web/integration-tests/tests/connectivity/connectivity.spec.js deleted file mode 100644 index be2e973d..00000000 --- a/packages/web/integration-tests/tests/connectivity/connectivity.spec.js +++ /dev/null @@ -1,60 +0,0 @@ -/** - * - * Copyright 2024 Splunk Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -module.exports = { - 'Connectivity events are captured': async function (browser) { - // When we have a test run that supports chrome dev tools protocol we can properly emulate connectivity. - // Otherwise this could be an unit test. - // Should probably do some timing checks but don't want to add flakiness atm - - await browser.url(browser.globals.getUrl('/connectivity/connectivity.ejs')) - - await browser.globals.findSpan((span) => span.name === 'documentFetch') - const connectivitySpans = browser.globals.getReceivedSpans().filter((span) => span.name === 'connectivity') - await browser.assert.strictEqual(connectivitySpans.length, 0, 'No connectivity spans cause we are offline') - - await browser.globals.emulateOffline(true) - let offlineSpan = await browser.globals.findSpan( - (span) => span.name === 'connectivity' && span.tags['online'] === 'false', - ) - let onlineSpan = await browser.globals.findSpan( - (span) => span.name === 'connectivity' && span.tags['online'] === 'true', - ) - - await browser.assert.ok(!!onlineSpan, 'Offline span exists') - await browser.assert.ok(!!offlineSpan, 'Online span exists') - await browser.globals.assertNoErrorSpans() - - browser.globals.clearReceivedSpans() - - await browser.globals.emulateOffline(false) - await browser.globals.emulateOffline(true) - - offlineSpan = await browser.globals.findSpan( - (span) => span.name === 'connectivity' && span.tags['online'] === 'false', - ) - onlineSpan = await browser.globals.findSpan( - (span) => span.name === 'connectivity' && span.tags['online'] === 'true', - ) - - await browser.assert.ok(!!onlineSpan, 'Offline span exists') - await browser.assert.ok(!!offlineSpan, 'Online span exists') - - await browser.globals.assertNoErrorSpans() - }, -} diff --git a/packages/web/integration-tests/tests/context/async-context.spec.js b/packages/web/integration-tests/tests/context/async-context.spec.js deleted file mode 100644 index 30048c7d..00000000 --- a/packages/web/integration-tests/tests/context/async-context.spec.js +++ /dev/null @@ -1,149 +0,0 @@ -/** - * - * Copyright 2024 Splunk Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -const { isBrowser } = require('../../utils/helpers') - -/** - * @param {import('nightwatch').NightwatchBrowser} browser - * @param {string} filename - * @param {(clickSpan, childSpan) => void} - */ -async function runTest(browser, filename, extraChecks) { - await browser.url(browser.globals.getUrl(filename)) - - await browser.click('#btn1') - await browser.globals.waitForTestToFinish() - - const clickSpan = await browser.globals.findSpan((span) => span.name === 'click') - const childSpan = await browser.globals.findSpan((span) => span.name === 'context-child') - - browser.assert.not.ok(clickSpan.parentId, 'Click span does not have a parent.') - console.log(childSpan) - browser.assert.strictEqual(childSpan.parentId, clickSpan.id, 'Child span belongs to user interaction trace.') - - await browser.globals.assertNoErrorSpans() - - if (extraChecks) { - await extraChecks(clickSpan, childSpan) - } -} - -module.exports = { - '@disabled': true, - 'afterEach': function (browser) { - browser.globals.clearReceivedSpans() - }, - 'setTimeout': async function (browser) { - await runTest(browser, '/context/set-timeout.ejs') - - browser.globals.clearReceivedSpans() - - await browser.click('#btn2') - await browser.globals.waitForTestToFinish() - - const clickSpan = await browser.globals.findSpan((span) => span.name === 'click') - const childSpan = await browser.globals.findSpan((span) => span.name === 'context-child') - - browser.assert.ok(!!clickSpan, 'Click has a span') - browser.assert.not.ok(childSpan.parentId, "Child span in a longer timeout doesn't have a parent") - - await browser.globals.assertNoErrorSpans() - }, - 'setImmediate': function (browser) { - if (!isBrowser(browser, 'ie')) { - // Only exists in IE - return Promise.resolve() - } - - return runTest(browser, '/context/set-immediate.ejs') - }, - 'promise constructor': async function (browser) { - if (isBrowser(browser, 'ie')) { - // Doesn't exist natively in IE - return Promise.resolve() - } - - await browser.url(browser.globals.getUrl('/context/promise.ejs')) - - for (let i = 1; i <= 5; i++) { - await browser.click('#btn' + i) - await browser.globals.waitForTestToFinish() - - const clickSpan = await browser.globals.findSpan((span) => span.name === 'click') - const childSpan = await browser.globals.findSpan((span) => span.name === 'context-child') - - browser.assert.not.ok(clickSpan.parentId, `Click ${i} span does not have a parent.`) - browser.assert.strictEqual( - childSpan.parentId, - clickSpan.id, - `Child span ${i} belongs to user interaction trace.`, - ) - - await browser.globals.assertNoErrorSpans() - browser.globals.clearReceivedSpans() - } - - return true - }, - 'fetch then': function (browser) { - if (isBrowser(browser, 'ie')) { - // Doesn't exist natively in IE - return Promise.resolve() - } - - return runTest(browser, '/context/fetch-then.ejs') - }, - 'xhr event callback': function (browser) { - return runTest(browser, '/context/xhr-events.ejs') - }, - 'xhr event with removal': function (browser) { - return runTest(browser, '/context/xhr-events-removed.ejs') - }, - 'xhr onload property': function (browser) { - return runTest(browser, '/context/xhr-onload.ejs') - }, - 'mutation observer on textNode': function (browser) { - return runTest(browser, '/context/mutation-observer-textnode.ejs') - }, - 'MessagePort - addEventListener': function (browser) { - return runTest(browser, '/context/messageport-addeventlistener.ejs') - }, - 'MessagePort - onmessage': function (browser) { - return runTest(browser, '/context/messageport-onmessage.ejs') - }, - 'MessagePort: Works with cors': async function (browser) { - await browser.url(browser.globals.getUrl('/context/messageport-cors.ejs')) - await browser.globals.waitForTestToFinish() - - await browser.globals.findSpan((span) => span.name === 'message-event') - - await browser.globals.assertNoErrorSpans() - }, - 'location - hashchange': async function (browser) { - await runTest(browser, '/context/location-hash.ejs') - - const clickSpan = await browser.globals.findSpan((span) => span.name === 'click') - const routeChangeSpan = await browser.globals.findSpan((span) => span.name === 'routeChange') - - browser.assert.strictEqual( - routeChangeSpan.parentId, - clickSpan.id, - 'Route change span belongs to user interaction trace.', - ) - }, -} diff --git a/packages/web/integration-tests/tests/context/framework/framework.spec.js b/packages/web/integration-tests/tests/context/framework/framework.spec.js deleted file mode 100644 index 1481093e..00000000 --- a/packages/web/integration-tests/tests/context/framework/framework.spec.js +++ /dev/null @@ -1,64 +0,0 @@ -/** - * - * Copyright 2024 Splunk Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -async function runTest(browser, filename) { - browser.globals.clearReceivedSpans() - await browser.url(browser.globals.getUrl(filename)) - await browser.click('#btn1') - //TODO this causes issues after lastId guard span change - await browser.globals.waitForTestToFinish() - - const customSpan = await browser.globals.findSpan((span) => span.name === 'custom-span') - const clickSpan = await browser.globals.findSpan((span) => span.name === 'click') - - browser.assert.not.ok(clickSpan.parentId, 'Click span does not have a parent.') - browser.assert.strictEqual(customSpan.parentId, clickSpan.id, 'Child span belongs to user interaction trace.') - browser.assert.strictEqual(customSpan.traceId, clickSpan.traceId, 'Spans have same traceId') - - await browser.globals.assertNoErrorSpans() -} - -module.exports = { - '@disabled': true, - 'Vue 2 with async': async function (browser) { - runTest(browser, '/context/framework/vue2.ejs') - }, - 'Vue 3 with async': async function (browser) { - runTest(browser, '/context/framework/vue3.ejs') - }, - 'React 16 with async': async function (browser) { - runTest(browser, '/context/framework/react-16.ejs') - }, - 'React 17 with async': async function (browser) { - browser.globals.clearReceivedSpans() - - await browser.url(browser.globals.getUrl('/context/framework/react-latest.ejs')) - await browser.click('#btn1') - await browser.globals.waitForTestToFinish() - - //Investigate: for some reason for react 17 there are several nested clicks - const customSpan = await browser.globals.findSpan((span) => span.name === 'custom-span') - const clickSpans = browser.globals.getReceivedSpans().filter((span) => span.name === 'click') - - clickSpans.forEach((span) => { - browser.assert.strictEqual(customSpan.traceId, span.traceId, 'Spans have same traceId') - }) - - await browser.globals.assertNoErrorSpans() - }, -} diff --git a/packages/web/integration-tests/tests/context/set-immediate.ejs b/packages/web/integration-tests/tests/context/set-immediate.ejs deleted file mode 100644 index 17c24f88..00000000 --- a/packages/web/integration-tests/tests/context/set-immediate.ejs +++ /dev/null @@ -1,28 +0,0 @@ - - - - - setImmediate - - <%- renderAgent({ - context: { - async: true - } - }) %> - - -

setImmediate

- - - - - - diff --git a/packages/web/integration-tests/tests/cookies/cookies.spec.js b/packages/web/integration-tests/tests/cookies/cookies.spec.js deleted file mode 100644 index 1f478aa5..00000000 --- a/packages/web/integration-tests/tests/cookies/cookies.spec.js +++ /dev/null @@ -1,107 +0,0 @@ -/** - * - * Copyright 2024 Splunk Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -const { isBrowser } = require('../../utils/helpers') - -module.exports = { - 'afterEach': function (browser) { - browser.globals.clearReceivedSpans() - }, - 'setting session cookie should work': async function (browser) { - await browser.url(browser.globals.getUrl('/cookies/cookies.ejs')) - - // This should create two streams of documentLoad sequences, all with the same sessionId but having - // two scriptInstances (one from parent, one from iframe) - const parent = await browser.globals.findSpan( - (span) => span.name === 'documentFetch' && span.tags['location.href'].includes('cookies.ejs'), - ) - await browser.assert.ok(parent.tags['browser.instance.id']) - - await browser.assert.notEqual(parent.tags['splunk.scriptInstance'], parent.tags['splunk.rumSessionId']) - - const iframe = await browser.globals.findSpan( - (span) => span.name === 'documentFetch' && span.tags['location.href'].includes('iframe.ejs'), - ) - await browser.assert.ok(iframe.tags['splunk.rumSessionId']) - await browser.assert.notEqual(iframe.tags['splunk.scriptInstance'], iframe.tags['splunk.rumSessionId']) - - // same session id & instanceId - await browser.assert.equal(parent.tags['splunk.rumSessionId'], iframe.tags['splunk.rumSessionId']) - await browser.assert.equal(parent.tags['browser.instance.id'], iframe.tags['browser.instance.id']) - - // but different scriptInstance - await browser.assert.notEqual(parent.tags['splunk.scriptInstance'], iframe.tags['splunk.scriptInstance']) - - const cookie = await browser.getCookie('_splunk_rum_sid') - await browser.assert.ok(cookie) - // FIXME we previously tested that the cookie was marked SameSite=Strict but new session implementation - // has a race between iframes and parents from the same domain. - - await browser.globals.assertNoErrorSpans() - }, - 'setting session cookie in iframe should work': async function (browser) { - await browser.url(browser.globals.getUrl('/cookies/cookies.iframe.ejs')) - - const fetchSpan = await browser.globals.findSpan( - (span) => span.name === 'documentFetch' && span.tags.app === 'iframe', - ) - await browser.assert.ok(fetchSpan.tags['splunk.rumSessionId']) - const cookie = await browser.getCookie('_splunk_rum_sid') - await browser.assert.ok(cookie) - await browser.assert.ok(fetchSpan) - if (!isBrowser(browser, 'internet explorer') && browser.globals.enableHttps) { - await browser.assert.ok(cookie.secure) - } - - if (!isBrowser(browser, { 'internet explorer': true, 'safari': true })) { - await browser.assert.equal(cookie.sameSite, browser.globals.enableHttps ? 'None' : 'Strict') - } - }, - 'setting cookieDomain via config sets it on subdomains also': async function (browser) { - // chrome: too old to remember - // edge: doesnt' work in saucelab right now for some reason? - if (isBrowser(browser, { chrome: true, microsoftedge: true })) { - return - } - - /* - We are using nip.io to let us test subdomains not sure how reliable it is, so if - you are debugging flaky test then this should be your first guess. - cookies-domain.ejs has cookieDomain set to 127.0.0.1.nip.io, cookie set via cookieDomain - should be accessible for subdomains also so when we go to test. subdomain we should find the same - cookie. - */ - const protocol = browser.globals.enableHttps ? 'https' : 'http' - await browser.url(`${protocol}://127.0.0.1.nip.io:${browser.globals.httpPort}/cookies/cookies-domain.ejs`) - const cookie = await browser.getCookie('_splunk_rum_sid') - const cookieParse = decodeURI(cookie.value) - - await browser.assert.ok(cookie) - - await browser.url(`${protocol}://test.127.0.0.1.nip.io:${browser.globals.httpPort}/cookies/cookies-domain.ejs`) - - const cookie2 = await browser.getCookie('_splunk_rum_sid') - const cookie2Parse = decodeURI(cookie2.value) - - await browser.assert.strictEqual(cookie.domain, cookie2.domain) - await browser.assert.strictEqual(cookieParse.id, cookie2Parse.id) - await browser.assert.strictEqual(cookieParse.startTime, cookie2Parse.startTime) - - await browser.globals.assertNoErrorSpans() - }, -} diff --git a/packages/web/integration-tests/tests/docload/dockload-all.spec.js b/packages/web/integration-tests/tests/docload/dockload-all.spec.js deleted file mode 100644 index 583b1cea..00000000 --- a/packages/web/integration-tests/tests/docload/dockload-all.spec.js +++ /dev/null @@ -1,51 +0,0 @@ -/** - * - * Copyright 2024 Splunk Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -module.exports = { - 'resources before load event are correctly captured': async function (browser) { - browser.globals.clearReceivedSpans() - await browser.url(browser.globals.getUrl('/docload/docload-all.ejs')) - - const docLoad = await browser.globals.findSpan((span) => span.name === 'documentLoad') - await browser.assert.ok(!!docLoad, 'documentLoad present') - - //t=300 to defer load event until xhr/fetch/beacon can be potentially sent so we can test that they are not sent - const resources = ['css-font-img.css', 'splunk-black.png?t=300', 'iframe.ejs', 'splunk.woff'] - const receivedSpans = await browser.globals.getReceivedSpans() - for (const urlEnd of resources) { - const resourceSpan = receivedSpans.filter( - (span) => span.tags['http.url'] && span.tags['http.url'].endsWith(urlEnd), - )[0] - await browser.assert.ok(!!resourceSpan, urlEnd) - await browser.assert.strictEqual(docLoad.traceId, resourceSpan.traceId, `${urlEnd} has correct traceId`) - } - - // no xhr, fetch, beacon span - // t=1 because otherwise xhr and fetch identical - const ignoredResources = ['/some-data', '/some-data?t=1', '/api/v2/spans'] - for (const urlEnd of ignoredResources) { - const resourceSpan = receivedSpans.filter( - (span) => - span.tags['component'] === 'document-load' && - span.tags['http.url'] && - span.tags['http.url'].endsWith(urlEnd), - )[0] - await browser.assert.not.ok(!!resourceSpan, `${urlEnd} is ignored`) - } - }, -} diff --git a/packages/web/integration-tests/tests/docload/docload.spec.js b/packages/web/integration-tests/tests/docload/docload.spec.js deleted file mode 100644 index 7d8d28d4..00000000 --- a/packages/web/integration-tests/tests/docload/docload.spec.js +++ /dev/null @@ -1,120 +0,0 @@ -/** - * - * Copyright 2024 Splunk Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -module.exports = { - '@tags': ['safari-10.1'], - 'beforeEach': function (browser) { - browser.globals.clearReceivedSpans() - }, - 'documentFetch, resourceFetch, and documentLoad spans': async function (browser) { - const url = browser.globals.getUrl('/docload/docload.ejs') - await browser.url(url) - - const docFetch = await browser.globals.findSpan((span) => span.name === 'documentFetch') - const docLoad = await browser.globals.findSpan((span) => span.name === 'documentLoad') - const scriptFetch = await browser.globals.findSpan( - (span) => span.name === 'resourceFetch' && span.tags['http.url'].includes('splunk-otel-web.js'), - ) - const brokenImgFetch = await browser.globals.findSpan( - (span) => span.name === 'resourceFetch' && span.tags['http.url'].includes('/nosuchimage.jpg'), - ) - - await browser.assert.ok(docFetch, 'Checking docFetch span') - await browser.assert.ok(docLoad, 'Checking docLoad span') - await browser.assert.ok(docLoad.traceId.match(/[a-f0-9]+/), 'Checking sanity of traceId') - await browser.assert.ok(docLoad.id.match(/[a-f0-9]+/), 'Checking sanity of id') - await browser.assert.strictEqual(docFetch.traceId, docLoad.traceId) - await browser.assert.strictEqual(docFetch.parentId, docLoad.id) - - if (!browser.globals.isBrowser({ safari: { max: 10 }, ie: true })) { - await browser.assert.ok(scriptFetch, 'Checking scriptFetch span') - await browser.assert.strictEqual(scriptFetch.traceId, docLoad.traceId) - await browser.assert.strictEqual(scriptFetch.parentId, docLoad.id) - await browser.assert.strictEqual(scriptFetch.tags['component'], 'document-load') - await browser.assert.strictEqual(scriptFetch.tags['location.href'], url) - - await browser.assert.ok(brokenImgFetch, 'Checking brokenImgFetch span') - await browser.assert.strictEqual(brokenImgFetch.traceId, docLoad.traceId) - await browser.assert.strictEqual(brokenImgFetch.parentId, docLoad.id) - } - - // docFetch - await browser.assert.strictEqual(docFetch.tags['component'], 'document-load') - await browser.assert.strictEqual(docFetch.tags['location.href'], url) - await browser.timesMakeSense(docFetch.annotations, 'domainLookupStart', 'domainLookupEnd') - await browser.timesMakeSense(docFetch.annotations, 'connectStart', 'connectEnd') - await browser.timesMakeSense(docFetch.annotations, 'requestStart', 'responseStart') - await browser.timesMakeSense(docFetch.annotations, 'responseStart', 'responseEnd') - await browser.timesMakeSense(docFetch.annotations, 'fetchStart', 'responseEnd') - if (!browser.globals.isBrowser({ safari: true, ie: true })) { - await browser.assert.ok( - docFetch.tags['http.response_content_length'] >= 0, - 'Checking response_content_length', - ) - await browser.assert.ok(docFetch.tags['link.traceId'], 'Checking presence of link.traceId') - await browser.assert.ok(docFetch.tags['link.spanId'], 'Checking presence of link.spanId') - } - - // scriptFetch - if (!browser.globals.isBrowser({ safari: true, ie: true })) { - await browser.assert.ok( - scriptFetch.tags['http.response_content_length'] >= 0, - 'Checking response_content_length', - ) - await browser.assert.ok(docFetch.tags['link.traceId'], 'Checking presence of link.traceId') - await browser.assert.ok(docFetch.tags['link.spanId'], 'Checking presence of link.spanId') - } - // http.url has already been checked by the findSpan - - // documentLoad - await browser.assert.strictEqual(docLoad.tags['component'], 'document-load') - await browser.assert.strictEqual(docLoad.tags['location.href'], url) - await browser.assert.ok(docLoad.tags['screen.xy'].match(/[0-9]+x[0-9]+/), 'Checking sanity of screen.xy') - await browser.timesMakeSense(docLoad.annotations, 'domContentLoadedEventStart', 'domContentLoadedEventEnd') - await browser.timesMakeSense(docLoad.annotations, 'loadEventStart', 'loadEventEnd') - await browser.timesMakeSense(docLoad.annotations, 'fetchStart', 'domInteractive') - await browser.timesMakeSense(docLoad.annotations, 'fetchStart', 'domComplete') - - await browser.globals.assertNoErrorSpans() - }, - 'ignoring resource URLs': async function (browser) { - await browser.url(browser.globals.getUrl('/docload/docload-ignored.ejs')) - - await browser.globals.findSpan((span) => span.name === 'documentFetch') - const url = browser.globals.getUrl('/', []) - await browser.assert.not.ok( - browser.globals - .getReceivedSpans() - .find((span) => span.tags['http.url'] === url + 'non-impactful-resource.jpg'), - ) - }, - 'module can be disabled': async function (browser) { - await browser.url(browser.globals.getUrl('/docload/docload.ejs?disableInstrumentation=document')) - await browser.globals.waitForTestToFinish() - - await browser.assert.ok(await browser.globals.findSpan((span) => span.name === 'eventListener.error')) - - const SPAN_TYPES = ['documentFetch', 'documentLoad', 'resourceFetch'] - await browser.assert.not.ok( - browser.globals.getReceivedSpans().find((span) => SPAN_TYPES.includes(span.name)), - 'No spans with docload module type', - ) - - await browser.globals.assertNoErrorSpans() - }, -} diff --git a/packages/web/integration-tests/tests/errors/index.spec.js b/packages/web/integration-tests/tests/errors/index.spec.js deleted file mode 100644 index 5182d039..00000000 --- a/packages/web/integration-tests/tests/errors/index.spec.js +++ /dev/null @@ -1,232 +0,0 @@ -/** - * - * Copyright 2024 Splunk Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -const { isBrowser } = require('../../utils/helpers') - -module.exports = { - 'beforeEach': function (browser) { - browser.globals.clearReceivedSpans() - }, - 'DOM resource 4xx': async function (browser) { - await browser.url(browser.globals.getUrl('/errors/views/resource-4xx.ejs')) - - const errorSpan = await browser.globals.findSpan((s) => s.name === 'eventListener.error') - await browser.assert.ok(!!errorSpan, 'Checking if error span was found.') - - const tags = errorSpan.tags - await browser.assert.strictEqual(tags['component'], 'error') - await browser.assert.strictEqual(tags['error.type'], 'error') - await browser.assert.strictEqual(tags['target_element'], 'IMG') - await browser.assert.strictEqual(tags['target_xpath'], '//html/body/img') - await browser.assert.ok( - tags['target_src'].endsWith('/nonexistent.png'), - `Checking target_src: ${tags['target_src']}`, - ) - }, - 'JS syntax error': async function (browser) { - await browser.url(browser.globals.getUrl('/errors/views/js-syntax-error.ejs')) - - const errorSpan = await browser.globals.findSpan((s) => s.name === 'onerror') - await browser.assert.ok(!!errorSpan, 'Checking presence of error span.') - - const tags = errorSpan.tags - await browser.assert.strictEqual(tags['component'], 'error') - await browser.assert.strictEqual(tags['error'], 'true') - await browser.assert.strictEqual( - tags['error.object'], - isBrowser(browser, 'internet explorer') ? 'String' : 'SyntaxError', - ) - - switch (browser.options.desiredCapabilities.browserName.toLowerCase()) { - case 'chrome': - await browser.assert.strictEqual(tags['error.message'], "Unexpected token ';'") - break - case 'firefox': - await browser.assert.strictEqual(tags['error.message'], "expected expression, got ';'") - break - case 'safari': - await browser.assert.strictEqual(tags['error.message'], "Unexpected token ';'") - break - case 'internet explorer': - await browser.assert.strictEqual(tags['error.message'], 'Syntax error') - break - } - }, - 'JS unhandled error': async function (browser) { - await browser.url(browser.globals.getUrl('/errors/views/unhandled-error.ejs')) - - const errorSpan = await browser.globals.findSpan((s) => s.name === 'onerror') - await browser.assert.ok(errorSpan, 'Checking presence of error span.') - - const tags = errorSpan.tags - await browser.assert.strictEqual(tags['component'], 'error') - await browser.assert.strictEqual(tags['error'], 'true') - await browser.assert.strictEqual(tags['error.object'], 'TypeError') - - switch (browser.options.desiredCapabilities.browserName.toLowerCase()) { - case 'chrome': - await browser.assert.strictEqual( - tags['error.message'], - "Cannot set properties of null (setting 'prop1')", - ) - break - case 'firefox': - await browser.assert.strictEqual(tags['error.message'], 'test is null') - break - case 'safari': - await browser.assert.strictEqual( - tags['error.message'], - "null is not an object (evaluating 'test.prop1 = true')", - ) - break - case 'internet explorer': - await browser.assert.strictEqual( - tags['error.message'], - "Unable to set property 'prop1' of undefined or null reference", - ) - break - } - }, - 'unhandled promise rejection': async function (browser) { - if (isBrowser(browser, 'internet explorer')) { - return // No native promise - } - - await browser.url(browser.globals.getUrl('/errors/views/unhandled-rejection.ejs')) - - const errorSpan = await browser.globals.findSpan((s) => s.name === 'unhandledrejection') - await browser.assert.ok(!!errorSpan, 'Checking presence of error span.') - - const tags = errorSpan.tags - await browser.assert.strictEqual(tags['component'], 'error') - await browser.assert.strictEqual(tags['error'], 'true') - await browser.assert.strictEqual(tags['error.object'], 'String') - await browser.assert.strictEqual(tags['error.message'], 'rejection-value') - }, - 'manual console.error': async function (browser) { - const browserName = browser.options.desiredCapabilities.browserName.toLowerCase() - - const url = browser.globals.getUrl('/errors/views/console-error.ejs') - await browser.url(url) - - const errorSpan = await browser.globals.findSpan((s) => s.name === 'console.error') - await browser.assert.ok(!!errorSpan, 'Checking presence of error span.') - - const tags = errorSpan.tags - await browser.assert.strictEqual(tags['component'], 'error') - await browser.assert.strictEqual(tags['error'], 'true') - await browser.assert.strictEqual(tags['error.object'], 'TypeError') - - const ERROR_MESSAGE_MAP = { - 'safari': "null is not an object (evaluating 'someNull.anyField = 'value'')", - 'chrome': "Cannot set properties of null (setting 'anyField')", - 'microsoftedge': "Cannot set properties of null (setting 'anyField')", - 'firefox': 'someNull is null', - 'internet explorer': "Unable to set property 'anyField' of undefined or null reference", - } - await browser.assert.strictEqual(tags['error.message'], ERROR_MESSAGE_MAP[browserName]) - - const ERROR_STACK_MAP = { - 'safari': `global code@${url}:62:15`, - 'chrome': `TypeError: Cannot set properties of null (setting 'anyField')\n at ${url}:62:25`, - 'microsoftedge': `TypeError: Cannot set properties of null (setting 'anyField')\n at ${url}:62:25`, - 'firefox': `@${url}:62:7\n`, - 'internet explorer': `TypeError: Unable to set property 'anyField' of undefined or null reference\n at Global code (${url}:62:7)`, - } - await browser.assert.strictEqual(tags['error.stack'], ERROR_STACK_MAP[browserName]) - }, - 'SplunkRum.error': async function (browser) { - const browserName = browser.options.desiredCapabilities.browserName.toLowerCase() - const url = browser.globals.getUrl('/errors/views/splunkrum-error.ejs') - await browser.url(url) - - const errorSpan = await browser.globals.findSpan((s) => s.name === 'SplunkRum.error') - await browser.assert.ok(!!errorSpan, 'Checking presence of error span.') - - const tags = errorSpan.tags - await browser.assert.strictEqual(tags['component'], 'error') - await browser.assert.strictEqual(tags['error'], 'true') - await browser.assert.strictEqual(tags['error.object'], 'TypeError') - - const ERROR_MESSAGE_MAP = { - 'safari': "null is not an object (evaluating 'someNull.anyField = 'value'')", - 'chrome': "Cannot set properties of null (setting 'anyField')", - 'microsoftedge': "Cannot set properties of null (setting 'anyField')", - 'firefox': 'someNull is null', - 'internet explorer': "Unable to set property 'anyField' of undefined or null reference", - } - - await browser.assert.strictEqual(tags['error.message'], ERROR_MESSAGE_MAP[browserName]) - - const ERROR_STACK_MAP = { - 'safari': `global code@${url}:62:15`, - 'microsoftedge': `TypeError: Cannot set properties of null (setting 'anyField')\n at ${url}:62:25`, - 'chrome': `TypeError: Cannot set properties of null (setting 'anyField')\n at ${url}:62:25`, - 'firefox': `@${url}:62:7\n`, - 'internet explorer': `TypeError: Unable to set property 'anyField' of undefined or null reference\n at Global code (${url}:62:7)`, - } - await browser.assert.strictEqual(tags['error.stack'], ERROR_STACK_MAP[browserName]) - }, - 'module can be disabled': async function (browser) { - await browser.url(browser.globals.getUrl('/errors/views/unhandled-error.ejs')) - await browser.globals.waitForTestToFinish() - - browser.assert.ok( - !!browser.globals.getReceivedSpans().find(({ name }) => name === 'onerror'), - 'Checking presence of error span.', - ) - - browser.globals.clearReceivedSpans() - await browser.url(browser.globals.getUrl('/errors/views/unhandled-error.ejs?disableInstrumentation=errors')) - await browser.globals.waitForTestToFinish() - - browser.assert.not.ok( - browser.globals.getReceivedSpans().find(({ name }) => name === 'onerror'), - 'Checking lack of error span.', - ) - }, - 'minified script with source map id': async function (browser) { - await browser.url(browser.globals.getUrl('/errors/views/minified_file_errors.ejs')) - // click on button1 to trigger error - await browser.click('#button1') - await browser.pause(1000) - const errorSpan1 = await browser.globals.findSpan((s) => s.name === 'onerror') - await browser.assert.ok(!!errorSpan1, 'Checking presence of error span.') - await browser.assert.strictEqual(errorSpan1.tags['error.message'], 'Error from script1.js') - // check errorSpan2.tags['error.source_map_ids'] contains the strings script1.min.js and the hash text - await browser.assert.ok(errorSpan1.tags['error.source_map_ids'].includes('script1.min.js')) - await browser.assert.ok( - errorSpan1.tags['error.source_map_ids'].includes( - '9663c60664c425cef3b141c167e9b324240ce10ae488726293892b7130266a6b', - ), - ) - // clear spans and do same for button2 - browser.globals.clearReceivedSpans() - await browser.click('#button2') - await browser.pause(1000) - const errorSpan2 = await browser.globals.findSpan((s) => s.name === 'onerror') - await browser.assert.ok(!!errorSpan2, 'Checking presence of error span.') - await browser.assert.strictEqual(errorSpan2.tags['error.message'], 'Error from script2.js') - await browser.assert.ok(errorSpan2.tags['error.source_map_ids'].includes('script2.min.js')) - await browser.assert.ok( - errorSpan2.tags['error.source_map_ids'].includes( - '9793573cdc2ab308a0b1996bea14253ec8832bc036210475ded0813cafa27066', - ), - ) - }, -} diff --git a/packages/web/integration-tests/tests/fetch/fetch.spec.js b/packages/web/integration-tests/tests/fetch/fetch.spec.js deleted file mode 100644 index 38abb2b6..00000000 --- a/packages/web/integration-tests/tests/fetch/fetch.spec.js +++ /dev/null @@ -1,121 +0,0 @@ -/** - * - * Copyright 2024 Splunk Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -module.exports = { - '@tags': ['safari-10.1', 'skip-ie11'], - 'beforeEach': function (browser) { - browser.globals.clearReceivedSpans() - }, - 'span created for fetch includes all properties': async function (browser) { - await browser.url(browser.globals.getUrl('/fetch/fetch.ejs')) - - const fetchSpan = await browser.globals.findSpan( - (span) => span.tags['http.url'] && span.tags['http.url'].endsWith('/some-data'), - ) - await browser.assert.ok(!!fetchSpan, 'Fetch span found.') - await browser.assert.strictEqual(fetchSpan.tags['component'], 'fetch') - await browser.assert.strictEqual(fetchSpan.tags['http.status_code'], '200') - await browser.assert.strictEqual(fetchSpan.tags['http.status_text'], 'OK') - await browser.assert.strictEqual(fetchSpan.tags['http.method'], 'GET') - - if (browser.options.desiredCapabilities.browserName !== 'Safari') { - await browser.assert.strictEqual(fetchSpan.tags['http.response_content_length'], '49') - } - - await browser.assert.ok(fetchSpan.tags['link.traceId'], 'got link.traceId') - await browser.assert.ok(fetchSpan.tags['link.spanId'], 'got link.spanId') - if ( - browser.options.desiredCapabilities.browserName !== 'Safari' && - browser.options.desiredCapabilities.browser_version !== '10.1' - ) { - await browser.timesMakeSense(fetchSpan.annotations, 'domainLookupStart', 'domainLookupEnd') - await browser.timesMakeSense(fetchSpan.annotations, 'connectStart', 'connectEnd') - if (browser.globals.enableHttps) { - await browser.timesMakeSense(fetchSpan.annotations, 'secureConnectionStart', 'connectEnd') - } - - await browser.timesMakeSense(fetchSpan.annotations, 'requestStart', 'responseStart') - await browser.timesMakeSense(fetchSpan.annotations, 'responseStart', 'responseEnd') - await browser.timesMakeSense(fetchSpan.annotations, 'fetchStart', 'responseEnd') - } - - await browser.globals.assertNoErrorSpans() - }, - 'fetch request can be ignored': async function (browser) { - await browser.url(browser.globals.getUrl('/fetch/fetch-ignored.ejs')) - - await browser.globals.findSpan((span) => span.name === 'guard-span') - await browser.globals.assertNoErrorSpans() - - await browser.assert.not.ok( - browser.globals - .getReceivedSpans() - .find((span) => span.tags['http.url'] && span.tags['http.url'].endsWith('/some-data')), - ) - await browser.assert.not.ok( - browser.globals - .getReceivedSpans() - .find((span) => span.tags['http.url'] && span.tags['http.url'].endsWith('/no-server-timings')), - ) - }, - 'fetch reported over CORS': async function (browser) { - const backend2 = await browser.globals.buildInstrumentationBackend() - await browser.url( - backend2.getUrl('/fetch/fetch.ejs', undefined, { beaconPort: browser.globals.httpPort }).toString(), - ) - - const fetchSpan = await browser.globals.findSpan( - (span) => span.tags['http.url'] && span.tags['http.url'].endsWith('/some-data'), - ) - await browser.assert.ok(!!fetchSpan, 'Fetch span found.') - await browser.assert.strictEqual(fetchSpan.tags['component'], 'fetch') - await browser.assert.strictEqual(fetchSpan.tags['http.status_code'], '200') - await browser.assert.strictEqual(fetchSpan.tags['http.status_text'], 'OK') - await browser.assert.strictEqual(fetchSpan.tags['http.method'], 'GET') - await browser.assert.strictEqual(fetchSpan.tags['http.host'], `${backend2.httpHostname}:${backend2.httpPort}`) - - await browser.globals.assertNoErrorSpans() - await backend2.close() - }, - 'request body exists in request object (open-telemetry/opentelemetry-js#2411)': async function (browser) { - await browser.url(browser.globals.getUrl('/fetch/fetch-post.ejs')) - await browser.globals.findSpan((span) => span.tags['http.url'] && span.tags['http.url'].endsWith('/echo')) - - browser.expect.element('#result').text.to.equal('{"test":true}') - }, - 'can be disabled (with xhr switch)': async function (browser) { - await browser.url(browser.globals.getUrl('/fetch/fetch.ejs')) - await browser.globals.waitForTestToFinish() - - await browser.assert.ok( - !!browser.globals - .getReceivedSpans() - .find((span) => span.tags['http.url'] && span.tags['http.url'].endsWith('/some-data')), - ) - - browser.globals.clearReceivedSpans() - await browser.url(browser.globals.getUrl('/fetch/fetch.ejs?disableInstrumentation=xhr,fetch')) - await browser.globals.waitForTestToFinish() - - await browser.assert.not.ok( - browser.globals - .getReceivedSpans() - .find((span) => span.tags['http.url'] && span.tags['http.url'].endsWith('/some-data')), - ) - }, -} diff --git a/packages/web/integration-tests/tests/init/init.spec.js b/packages/web/integration-tests/tests/init/init.spec.js deleted file mode 100644 index 63afe313..00000000 --- a/packages/web/integration-tests/tests/init/init.spec.js +++ /dev/null @@ -1,87 +0,0 @@ -/** - * - * Copyright 2024 Splunk Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -module.exports = { - '@tags': ['safari-10.1'], - 'No spans sent when no beacon url set': async function (browser) { - await browser.url(browser.globals.getUrl('/init/nobeacon.ejs')) - const span = await browser.globals.findSpan(() => true) - await browser.assert.not.ok(span) - }, - 'attribute-related config should work': async function (browser) { - browser.globals.clearReceivedSpans() - await browser.url(browser.globals.getUrl('/init/attributes.ejs')) - const atts = await browser.globals.findSpan((span) => span.name === 'documentLoad') - await browser.assert.ok(atts) - await browser.assert.strictEqual(atts.tags['app'], 'custom-app') - await browser.assert.strictEqual(atts.tags['environment'], 'custom-environment') - await browser.assert.strictEqual(atts.tags['key1'], 'value1') - await browser.assert.strictEqual(atts.tags['key2'], 'value2') - - await browser.click('#clickToChangeAttributes') - - const atts2 = await browser.globals.findSpan((span) => span.name === 'attributes-set') - await browser.assert.ok(atts2) - // environment still set (not cleared by setGlobalAttributes call) - await browser.assert.strictEqual(atts2.tags['environment'], 'custom-environment') - await browser.assert.strictEqual(atts2.tags['key1'], 'newvalue1') - await browser.assert.strictEqual(atts2.tags['key2'], 'value2') - - const attrsNotificationSpan = await browser.globals.findSpan((span) => span.name === 'attributes-changed') - await browser.assert.ok(attrsNotificationSpan) - - const notifiedAttrs = JSON.parse(attrsNotificationSpan.tags['payload']).attributes - await browser.assert.strictEqual(notifiedAttrs.environment, 'custom-environment') - await browser.assert.strictEqual(notifiedAttrs.key1, 'newvalue1') - await browser.assert.strictEqual(notifiedAttrs.key2, 'value2') - - await browser.click('#clickToResetAttributes') - // no argument setGlobalAttributes() call will set empty attributes - const atts3 = await browser.globals.findSpan((span) => span.name === 'attributes-reset') - await browser.assert.ok(atts3) - await browser.assert.strictEqual(atts3.tags['environment'], undefined) - await browser.assert.strictEqual(atts3.tags['key1'], undefined) - await browser.assert.strictEqual(atts3.tags['key1'], undefined) - - await browser.globals.assertNoErrorSpans() - }, - 'instrumentations.errors controls error capture': async function (browser) { - browser.globals.clearReceivedSpans() - await browser.url(browser.globals.getUrl('/init/captureErrors.ejs')) - const errorGuard = await browser.globals.findSpan((span) => span.name === 'error-guard-span') - await browser.assert.ok(errorGuard) - await browser.globals.assertNoErrorSpans() - }, - 'environment % resource attrs still get set if no global attributes': async function (browser) { - browser.globals.clearReceivedSpans() - await browser.url(browser.globals.getUrl('/init/attributes-no-globals.ejs')) - - const atts = await browser.globals.findSpan((span) => span.name === 'documentLoad') - await browser.assert.ok(atts) - await browser.assert.strictEqual(atts.tags['app'], 'custom-app') - await browser.assert.strictEqual(atts.tags['environment'], 'custom-environment') - - // Set as a resource, zipkin exporter should merge into tags - await browser.assert.strictEqual(atts.tags['telemetry.sdk.name'], '@splunk/otel-web') - await browser.assert.strictEqual(atts.tags['telemetry.sdk.language'], 'webjs') - await browser.assert.strictEqual(atts.tags['telemetry.sdk.version'], browser.globals.rumVersion) - await browser.assert.strictEqual(atts.tags['splunk.rumVersion'], browser.globals.rumVersion) - - await browser.globals.assertNoErrorSpans() - }, -} diff --git a/packages/web/integration-tests/tests/longtask/index.spec.js b/packages/web/integration-tests/tests/longtask/index.spec.js deleted file mode 100644 index c775b115..00000000 --- a/packages/web/integration-tests/tests/longtask/index.spec.js +++ /dev/null @@ -1,141 +0,0 @@ -/** - * - * Copyright 2024 Splunk Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -function browserIsCompatible(browser) { - return browser.globals.isBrowser({ chrome: true, microsoftedge: true }) -} - -module.exports = { - '@tags': ['skip-ie11'], - 'beforeEach': function (browser) { - browser.globals.clearReceivedSpans() - }, - 'reports a longtask': async function (browser) { - await browser.url(browser.globals.getUrl('/longtask/index.ejs')) - await browser.click('#btnLongtask') - - if (browserIsCompatible(browser)) { - const longtaskSpan = await browser.globals.findSpan((span) => span.name === 'longtask') - await browser.assert.ok(!!longtaskSpan, 'Checking longtask span presence.') - await browser.assert.strictEqual(longtaskSpan.tags['component'], 'splunk-longtask', 'component') - // Edge can also report this with unknown value - await browser.assert.strictEqual( - ['self', 'unknown'].includes(longtaskSpan.tags['longtask.name']), - true, - 'longtask.name', - ) - await browser.assert.strictEqual( - longtaskSpan.tags['longtask.entry_type'], - 'longtask', - 'longtask.entry_type', - ) - await browser.assert.strictEqual( - longtaskSpan.tags['longtask.attribution.name'], - 'unknown', - 'longtask.attribution.name', - ) - await browser.assert.strictEqual( - longtaskSpan.tags['longtask.attribution.entry_type'], - 'taskattribution', - 'longtask.attribution.entry_type', - ) - await browser.assert.strictEqual( - longtaskSpan.tags['longtask.attribution.start_time'], - '0', - 'longtask.attribution.start_time', - ) - await browser.assert.strictEqual( - longtaskSpan.tags['longtask.attribution.duration'], - '0', - 'longtask.attribution.duration', - ) - await browser.assert.strictEqual( - longtaskSpan.tags['longtask.attribution.container_type'], - 'window', - 'longtask.attribution.container_type', - ) - await browser.assert.strictEqual( - longtaskSpan.tags['longtask.attribution.container_src'], - '', - 'longtask.attribution.container_src', - ) - await browser.assert.strictEqual( - longtaskSpan.tags['longtask.attribution.container_id'], - '', - 'longtask.attribution.container_id', - ) - await browser.assert.strictEqual( - longtaskSpan.tags['longtask.attribution.container_name'], - '', - 'longtask.attribution.container_name', - ) - - const duration = parseFloat(longtaskSpan.tags['longtask.duration']) - await browser.assert.ok(duration > 50, `Duration (${duration}) must be over 50ms by definition.`) - // note: our longtask simulator targets 55ms, but headless chrome doesn't understand throttling - await browser.assert.ok(duration < 1000, `Duration (${duration}) must be less than 1s by definition.`) - await browser.assert.strictEqual( - longtaskSpan.duration, - duration * 1000, - 'Span duration matches longtask duration', - ) - } - - await browser.globals.assertNoErrorSpans() - }, - 'reports buffered longtask': async function (browser) { - if (!browserIsCompatible(browser)) { - return // Skip this test - } - - await browser.url(browser.globals.getUrl('/longtask/buffered.ejs')) - const longtaskSpan = await browser.globals.findSpan((span) => span.name === 'longtask') - const duration = parseFloat(longtaskSpan.tags['longtask.duration']) - await browser.assert.strictEqual( - longtaskSpan.duration, - duration * 1000, - 'Span duration matches longtask duration', - ) - }, - 'can be disabled': async function (browser) { - if (!browserIsCompatible(browser)) { - return // Skip this test - } - - await browser.url(browser.globals.getUrl('/longtask/index.ejs')) - await browser.click('#btnLongtask') - await browser.globals.waitForTestToFinish() - - browser.assert.ok( - !!browser.globals.getReceivedSpans().find(({ name }) => name === 'longtask'), - 'Checking presence of longtask span.', - ) - await browser.globals.assertNoErrorSpans() - - browser.globals.clearReceivedSpans() - await browser.url(browser.globals.getUrl('/longtask/index.ejs?disableInstrumentation=longtask')) - await browser.click('#btnLongtask') - await browser.globals.waitForTestToFinish() - - browser.assert.not.ok( - browser.globals.getReceivedSpans().find(({ name }) => name === 'longtask'), - 'Checking presence of longtask span.', - ) - await browser.globals.assertNoErrorSpans() - }, -} diff --git a/packages/web/integration-tests/tests/resource-observer/resource-obs.spec.js b/packages/web/integration-tests/tests/resource-observer/resource-obs.spec.js deleted file mode 100644 index 09edcc41..00000000 --- a/packages/web/integration-tests/tests/resource-observer/resource-obs.spec.js +++ /dev/null @@ -1,242 +0,0 @@ -/** - * - * Copyright 2024 Splunk Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -const { isBrowser } = require('../../utils/helpers') -const { inspect } = require('util') - -module.exports = { - '@disabled': true, // too flaky, will fix when migrating from nightwatch - 'beforeEach': function (browser) { - browser.globals.clearReceivedSpans() - }, - 'should report resource loads happening after page load': async function (browser) { - if ( - isBrowser(browser, { - safari: true, - edge: true, - ie: true, - }) - ) { - return - } - - await browser.url(browser.globals.getUrl('/resource-observer/resources.ejs')) - - await browser.globals.findSpan((span) => span.name === 'guard-span') - - const plAgentSpans = browser.globals - .getReceivedSpans() - .filter((span) => span.tags['http.url'] && span.tags['http.url'].endsWith('splunk-otel-web.js')) - const plImageSpans = browser.globals - .getReceivedSpans() - .filter((span) => span.tags['http.url'] && span.tags['http.url'].endsWith('no-cache.png')) - - await browser.assert.strictEqual(plAgentSpans.length, 1) - await browser.assert.strictEqual(plImageSpans.length, 1) - - const imageSpan = await browser.globals.findSpan( - (span) => span.tags['http.url'] && span.tags['http.url'].endsWith('splunk-black.png?t=100'), - ) - - await browser.assert.ok(!!imageSpan, 'Image span found.') - await browser.assert.strictEqual(imageSpan.tags['component'], 'splunk-post-doc-load-resource') - const imgUrl = browser.globals.getUrl('/utils/devServer/assets/', []) + 'splunk-black.png?t=100' - await browser.assert.strictEqual(imageSpan.tags['http.url'], imgUrl) - await browser.assert.strictEqual(imageSpan.annotations.length, 9, 'Missing network events') - - const scriptSpan = await browser.globals.findSpan( - (span) => span.tags['http.url'] && span.tags['http.url'].endsWith('test.js'), - ) - - await browser.assert.strictEqual(scriptSpan.tags['component'], 'splunk-post-doc-load-resource') - const scriptUrl = browser.globals.getUrl('/utils/devServer/assets/', []) + 'test.js' - await browser.assert.strictEqual(scriptSpan.tags['http.url'], scriptUrl) - await browser.assert.strictEqual(scriptSpan.annotations.length, 9, 'Missing network events') - }, - 'resources can be ignored': async function (browser) { - if ( - isBrowser(browser, { - safari: true, - edge: true, - ie: true, - }) - ) { - return - } - - await browser.url(browser.globals.getUrl('/resource-observer/resources-ignored.ejs')) - - await browser.globals.findSpan((span) => span.name === 'guard-span') - const url = browser.globals.getUrl('/utils/devServer/assets/', []) - await browser.assert.not.ok( - browser.globals - .getReceivedSpans() - .find( - (span) => - span.tags['http.url'] === url + 'test.js' && - span.tags['component'] === 'splunk-post-doc-load-resource', - ), - ) - const imgSpan = browser.globals - .getReceivedSpans() - .find( - (span) => - span.tags['http.url'] === url + 'splunk-black.png' && - span.tags['component'] === 'splunk-post-doc-load-resource', - ) - await browser.assert.not.ok(imgSpan) - }, - 'should create one span for cached resource': async function (browser) { - if ( - isBrowser(browser, { - safari: true, - edge: true, - ie: true, - }) - ) { - return - } - - await browser.url(browser.globals.getUrl('/resource-observer/resources-twice.ejs')) - await browser.globals.findSpan((span) => span.name === 'guard-span') - - const imageSpans = await browser.globals - .getReceivedSpans() - .filter((span) => span.tags['http.url'] && span.tags['http.url'].endsWith('splunk-black.png')) - - // for debugging flaky tests - if (imageSpans.length !== 0) { - console.log('imageSpans.length') - console.log(inspect(imageSpans, { depth: 10 })) - } - - await browser.assert.strictEqual(imageSpans.length, 1) - await browser.assert.strictEqual(imageSpans[0].tags['component'], 'document-load') - }, - 'should create two spans for non cached resource': async function (browser) { - if ( - isBrowser(browser, { - safari: true, - edge: true, - ie: true, - }) - ) { - return - } - - if (browser.options.desiredCapabilities.browserName.toLowerCase() === 'firefox') { - // Can't get fx to stop caching the image. - return - } - - await browser.url(browser.globals.getUrl('/resource-observer/resources-twice-no-cache.ejs')) - await browser.globals.findSpan((span) => span.name === 'guard-span') - - const imageSpans = await browser.globals - .getReceivedSpans() - .filter((span) => span.tags['http.url'] && span.tags['http.url'].endsWith('no-cache.png')) - - // for debugging flaky tests - if (imageSpans.length !== 2) { - console.log('imageSpans.length') - console.log(inspect(imageSpans, { depth: 10 })) - } - - await browser.assert.strictEqual(imageSpans.length, 2) - const docLoadImage = imageSpans.find((span) => span.tags['component'] === 'document-load') - const afterLoadImage = imageSpans.find((span) => span.tags['component'] === 'splunk-post-doc-load-resource') - - await browser.assert.notEqual(docLoadImage.traceId, afterLoadImage.traceId) - }, - 'should propagate tracing context to created spans': async function (browser) { - if ( - isBrowser(browser, { - safari: true, - edge: true, - ie: true, - }) - ) { - return - } - - await browser.url(browser.globals.getUrl('/resource-observer/resources-custom-context.ejs')) - await browser.globals.findSpan((span) => span.name === 'guard-span') - - const plImageRootSpan = await browser.globals - .getReceivedSpans() - .find( - (span) => - span.tags['http.url'] && - span.tags['http.url'].endsWith('splunk-black.png') && - span.tags['component'] === 'splunk-post-doc-load-resource', - ) - const plImageParentSpan = await browser.globals.getReceivedSpans().find((span) => span.name === 'image-parent') - const plImageChildSpan = await browser.globals - .getReceivedSpans() - .find( - (span) => - span.tags['http.url'] && - span.tags['http.url'].endsWith('splunk-black.svg') && - span.tags['component'] === 'splunk-post-doc-load-resource', - ) - await browser.assert.ok(plImageRootSpan) - await browser.assert.ok(plImageParentSpan) - await browser.assert.ok(plImageChildSpan) - - await browser.assert.notEqual(plImageRootSpan.traceId, plImageParentSpan.traceId) - await browser.assert.not.ok(plImageRootSpan.parentId) - await browser.assert.equal(plImageParentSpan.traceId, plImageChildSpan.traceId) - await browser.assert.equal(plImageChildSpan.parentId, plImageParentSpan.id) - - const plScriptRootSpan = await browser.globals - .getReceivedSpans() - .find( - (span) => - span.tags['http.url'] && - span.tags['http.url'].endsWith('test.js') && - span.tags['component'] === 'splunk-post-doc-load-resource', - ) - const plScriptParentSpan = await browser.globals - .getReceivedSpans() - .find((span) => span.name === 'script-parent') - const plScriptChildSpan = await browser.globals - .getReceivedSpans() - .find( - (span) => - span.tags['http.url'] && - span.tags['http.url'].endsWith('test-alt.js') && - span.tags['component'] === 'splunk-post-doc-load-resource', - ) - await browser.assert.ok(plScriptRootSpan) - await browser.assert.ok(plScriptParentSpan) - await browser.assert.ok(plScriptChildSpan) - - await browser.assert.notEqual(plScriptRootSpan.traceId, plScriptParentSpan.traceId) - await browser.assert.not.ok(plScriptRootSpan.parentId) - await browser.assert.equal(plScriptParentSpan.traceId, plScriptChildSpan.traceId) - await browser.assert.equal(plScriptChildSpan.parentId, plScriptParentSpan.id) - }, - "doesn't crash when postload instrumentation is disabled": async function (browser) { - await browser.url(browser.globals.getUrl('/resource-observer/resources-custom-context.ejs')) - await browser.click('#btn1') - - const click = await browser.globals.findSpan((span) => span.name === 'click') - await browser.assert.ok(click) - await browser.global.assertNoErrorSpans() - }, -} diff --git a/packages/web/integration-tests/tests/server-timing/index.spec.js b/packages/web/integration-tests/tests/server-timing/index.spec.js deleted file mode 100644 index a9c1a24a..00000000 --- a/packages/web/integration-tests/tests/server-timing/index.spec.js +++ /dev/null @@ -1,51 +0,0 @@ -/** - * - * Copyright 2024 Splunk Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -module.exports = { - 'traceId should be attached to documentFetch span if Server-Timing was sent': async function (browser) { - // TODO: restructure into a proper feature support matrix - const UNSUPPORTED_BROWSERS = ['Safari', 'IE', 'iPhone'] - const currentBrowser = browser.options.desiredCapabilities.browserName - if (UNSUPPORTED_BROWSERS.includes(currentBrowser)) { - return - } - - // preload the page once for firefox - // in some cases firefox will report negative fetchStart for localhost pages - // in that case opentelemetry-plugin-document-load will not report any spans - await browser.url(browser.globals.baseUrl) - - browser.globals.clearReceivedSpans() - await browser.url(browser.globals.getUrl('/server-timing/index.ejs')) - const expectedTraceId = browser.globals.getLastServerTiming().traceId - const docFetchSpan = await browser.globals.findSpan( - (span) => span.name === 'documentFetch' && span.tags['link.traceId'] === expectedTraceId, - ) - - await browser.assert.strictEqual(docFetchSpan.tags['link.traceId'], expectedTraceId) - await browser.assert.strictEqual( - docFetchSpan.tags['location.href'], - browser.globals.getUrl('/server-timing/index.ejs'), - ) - await browser.assert.strictEqual(docFetchSpan.tags['app'], 'splunk-otel-js-dummy-app') - await browser.assert.strictEqual(docFetchSpan.tags['component'], 'document-load') - await browser.assert.strictEqual(docFetchSpan.tags['splunk.rumVersion'], browser.globals.rumVersion) - - await browser.globals.assertNoErrorSpans() - }, -} diff --git a/packages/web/integration-tests/tests/socketio/socketio.spec.js b/packages/web/integration-tests/tests/socketio/socketio.spec.js deleted file mode 100644 index 1a0724f4..00000000 --- a/packages/web/integration-tests/tests/socketio/socketio.spec.js +++ /dev/null @@ -1,65 +0,0 @@ -/** - * - * Copyright 2024 Splunk Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -module.exports = { - 'beforeEach': function (browser) { - browser.globals.clearReceivedSpans() - }, - 'produces correct connect span': async function (browser) { - const url = browser.globals.getUrl('/socketio/socketio.ejs') - await browser.url(url) - - await browser.click('#connectSocket') - await browser.click('#sendHello') - await browser.click('#sendPing') - - await browser.globals.waitForTestToFinish() - await browser.globals.findSpan((span) => span.name === 'pong receive') - - const sioSpans = await browser.globals - .getReceivedSpans() - .filter((span) => span.tags['messaging.system'] === 'socket.io') - - await browser.assert.strictEqual(sioSpans.length, 3) - - await browser.assert.strictEqual(sioSpans[0].name, 'hello send') - await browser.assert.strictEqual(sioSpans[1].name, 'ping send') - await browser.assert.strictEqual(sioSpans[2].name, 'pong receive') - }, - 'produces correct connect span (socket.io already on page)': async function (browser) { - const url = browser.globals.getUrl('/socketio/socketio.before.ejs') - await browser.url(url) - - await browser.click('#connectSocket') - await browser.click('#sendHello') - await browser.click('#sendPing') - - await browser.globals.waitForTestToFinish() - await browser.globals.findSpan((span) => span.name === 'pong receive') - - const sioSpans = await browser.globals - .getReceivedSpans() - .filter((span) => span.tags['messaging.system'] === 'socket.io') - - await browser.assert.strictEqual(sioSpans.length, 3) - - await browser.assert.strictEqual(sioSpans[0].name, 'hello send') - await browser.assert.strictEqual(sioSpans[1].name, 'ping send') - await browser.assert.strictEqual(sioSpans[2].name, 'pong receive') - }, -} diff --git a/packages/web/integration-tests/tests/synthetics/index.spec.js b/packages/web/integration-tests/tests/synthetics/index.spec.js deleted file mode 100644 index 4c216699..00000000 --- a/packages/web/integration-tests/tests/synthetics/index.spec.js +++ /dev/null @@ -1,59 +0,0 @@ -/** - * - * Copyright 2024 Splunk Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -const { buildBackendContext } = require('../../utils/globals') - -module.exports = { - 'applies Synthetics Run Id attribute on all spans': async function (browser) { - const backendCtx = await buildBackendContext(browser) - - browser.globals.clearReceivedSpans() - await browser.url( - backendCtx - .getUrl('/synthetics/index.ejs', undefined, { - syntheticsRunId: 'abcd1234'.repeat(4), - }) - .toString(), - ) - - const guardSpan = await backendCtx.findSpan((s) => s.name === 'guard-span') - await browser.assert.ok(guardSpan, 'Ensuring guard-span has arrived before we start inspecting spans.') - - for (const span of backendCtx.getReceivedSpans()) { - await browser.assert.strictEqual(span.tags['Synthetics-RunId'], 'abcd1234'.repeat(4)) - } - - await backendCtx._closeBackend() - }, - - 'does not apply Synthetics Run Id attribute on all spans if not included': async function (browser) { - const backendCtx = await buildBackendContext(browser) - - backendCtx.clearReceivedSpans() - await browser.url(backendCtx.getUrl('/synthetics/index.ejs')) - - const guardSpan = await backendCtx.findSpan((s) => s.name === 'guard-span') - await browser.assert.ok(guardSpan, 'Ensuring guard-span has arrived before we start inspecting spans.') - - for (const span of backendCtx.getReceivedSpans()) { - await browser.assert.strictEqual(span.tags['Synthetics-RunId'], undefined) - } - - await backendCtx._closeBackend() - }, -} diff --git a/packages/web/integration-tests/tests/user-interaction/causality.spec.js b/packages/web/integration-tests/tests/user-interaction/causality.spec.js deleted file mode 100644 index b049da9d..00000000 --- a/packages/web/integration-tests/tests/user-interaction/causality.spec.js +++ /dev/null @@ -1,70 +0,0 @@ -/** - * - * Copyright 2024 Splunk Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -module.exports = { - 'beforeEach': function (browser) { - browser.globals.clearReceivedSpans() - }, - 'handles causality of a mouse click': async function (browser) { - await browser.url(browser.globals.getUrl('/user-interaction/causality.ejs')) - - // Do a click so web-vitals can run and then remove it's event listeners - await browser.click('body') - - await browser.click('#btn1') - - const clickSpan = await browser.globals.findSpan( - (span) => span.name === 'click' && span.tags['target_xpath'] === '//*[@id="btn1"]', - ) - await browser.assert.ok(!!clickSpan, 'Checking click span presence.') - - const fetchSpan = await browser.globals.findSpan( - (span) => span.tags['http.url'] === browser.globals.getUrl('/some-data', []), - ) - await browser.assert.ok(!!fetchSpan, 'Checking fetch span presence.') - - await browser.assert.not.ok(clickSpan.parentId, 'Click span does not have a parent.') - await browser.assert.strictEqual( - fetchSpan.parentId, - clickSpan.id, - 'Fetch span belongs to user interaction trace.', - ) - - await browser.globals.assertNoErrorSpans() - }, - 'can be disabled': async function (browser) { - await browser.url(browser.globals.getUrl('/user-interaction/causality.ejs')) - await browser.click('#btn1') - await browser.globals.waitForTestToFinish() - - browser.assert.ok( - !!(await browser.globals.getReceivedSpans().find(({ name }) => name === 'click')), - 'click span recorded', - ) - - browser.globals.clearReceivedSpans() - await browser.url(browser.globals.getUrl('/user-interaction/causality.ejs?disableInstrumentation=interactions')) - await browser.click('#btn1') - await browser.globals.waitForTestToFinish() - - browser.assert.not.ok( - await browser.globals.getReceivedSpans().find(({ name }) => name === 'click'), - 'lack of click span', - ) - }, -} diff --git a/packages/web/integration-tests/tests/user-interaction/forms.spec.js b/packages/web/integration-tests/tests/user-interaction/forms.spec.js deleted file mode 100644 index 898cbf19..00000000 --- a/packages/web/integration-tests/tests/user-interaction/forms.spec.js +++ /dev/null @@ -1,37 +0,0 @@ -/** - * - * Copyright 2024 Splunk Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -module.exports = { - 'handles form submit': async function (browser) { - browser.globals.clearReceivedSpans() - - const startUrl = browser.globals.getUrl('/user-interaction/forms.ejs') - await browser.url(startUrl) - await browser.click('#btnSubmit') - - const submitSpan = await browser.globals.findSpan((span) => span.name === 'submit') - await browser.assert.ok(!!submitSpan, 'Checking form submission span presence.') - - await browser.assert.strictEqual(submitSpan.tags['component'], 'user-interaction') - await browser.assert.strictEqual(submitSpan.tags['event_type'], 'submit') - await browser.assert.strictEqual(submitSpan.tags['target_element'], 'FORM') - await browser.assert.strictEqual(submitSpan.tags['target_xpath'], '//*[@id="form1"]') - - await browser.globals.assertNoErrorSpans() - }, -} diff --git a/packages/web/integration-tests/tests/user-interaction/history.spec.js b/packages/web/integration-tests/tests/user-interaction/history.spec.js deleted file mode 100644 index b97d3ed1..00000000 --- a/packages/web/integration-tests/tests/user-interaction/history.spec.js +++ /dev/null @@ -1,69 +0,0 @@ -/** - * - * Copyright 2024 Splunk Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -const { isBrowser } = require('../../utils/helpers') - -module.exports = { - 'handles hash navigation': async function (browser) { - browser.globals.clearReceivedSpans() - - const startUrl = browser.globals.getUrl('/user-interaction/history.ejs') - await browser.url(startUrl) - await browser.click('#btnGoToPage') - - const navigationSpan = await browser.globals.findSpan((span) => span.name === 'routeChange') - await browser.assert.ok(!!navigationSpan, 'Checking navigation span presence.') - - await browser.assert.strictEqual(navigationSpan.tags['component'], 'user-interaction') - if (!isBrowser(browser, 'ie')) { - await browser.assert.strictEqual(navigationSpan.tags['prev.href'], startUrl) - } - - await browser.assert.strictEqual(navigationSpan.tags['location.href'], startUrl + '#another-page') - - await browser.globals.assertNoErrorSpans() - }, - 'handles history navigation': async function (browser) { - browser.globals.clearReceivedSpans() - - const startUrl = browser.globals.getUrl('/user-interaction/history.ejs') - await browser.url(startUrl) - await browser.click('#btnGoToPage') - await browser.globals.findSpan((span) => span.name === 'routeChange') - - browser.globals.clearReceivedSpans() - await browser.click('#btnGoBack') - - const navigationBackSpan = await browser.globals.findSpan((span) => span.name === 'routeChange') - await browser.assert.ok(!!navigationBackSpan, 'Checking backwards navigation span presence.') - - await browser.assert.strictEqual(navigationBackSpan.tags['component'], 'user-interaction') - await browser.assert.strictEqual(navigationBackSpan.tags['prev.href'], startUrl + '#another-page') - await browser.assert.strictEqual(navigationBackSpan.tags['location.href'], startUrl) - - browser.globals.clearReceivedSpans() - await browser.click('#btnGoForward') - - const navigationForwardSpan = await browser.globals.findSpan((span) => span.name === 'routeChange') - await browser.assert.ok(!!navigationForwardSpan, 'Checking forwards navigation span presence.') - - await browser.assert.strictEqual(navigationForwardSpan.tags['component'], 'user-interaction') - await browser.assert.strictEqual(navigationForwardSpan.tags['prev.href'], startUrl) - await browser.assert.strictEqual(navigationForwardSpan.tags['location.href'], startUrl + '#another-page') - }, -} diff --git a/packages/web/integration-tests/tests/user-interaction/keyboard.spec.js b/packages/web/integration-tests/tests/user-interaction/keyboard.spec.js deleted file mode 100644 index e07cc16a..00000000 --- a/packages/web/integration-tests/tests/user-interaction/keyboard.spec.js +++ /dev/null @@ -1,45 +0,0 @@ -/** - * - * Copyright 2024 Splunk Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -module.exports = { - 'handles keyboard events': async function (browser) { - browser.globals.clearReceivedSpans() - - const startUrl = browser.globals.getUrl('/user-interaction/keyboard.ejs') - await browser.url(startUrl) - await browser.sendKeys('body', 'a') - - const keydownSpan = await browser.globals.findSpan((span) => span.name === 'keydown') - await browser.assert.ok(!!keydownSpan, 'Checking keyboard span presence.') - - await browser.assert.strictEqual(keydownSpan.tags['component'], 'user-interaction') - await browser.assert.strictEqual(keydownSpan.tags['event_type'], 'keydown') - await browser.assert.strictEqual(keydownSpan.tags['target_element'], 'BODY') - await browser.assert.strictEqual(keydownSpan.tags['target_xpath'], '//html/body') - - const keyupSpan = await browser.globals.findSpan((span) => span.name === 'keyup') - await browser.assert.ok(!!keyupSpan, 'Checking keyboard span presence.') - - await browser.assert.strictEqual(keyupSpan.tags['component'], 'user-interaction') - await browser.assert.strictEqual(keyupSpan.tags['event_type'], 'keyup') - await browser.assert.strictEqual(keyupSpan.tags['target_element'], 'BODY') - await browser.assert.strictEqual(keyupSpan.tags['target_xpath'], '//html/body') - - await browser.globals.assertNoErrorSpans() - }, -} diff --git a/packages/web/integration-tests/tests/user-interaction/mouse.spec.js b/packages/web/integration-tests/tests/user-interaction/mouse.spec.js deleted file mode 100644 index ff655914..00000000 --- a/packages/web/integration-tests/tests/user-interaction/mouse.spec.js +++ /dev/null @@ -1,140 +0,0 @@ -/** - * - * Copyright 2024 Splunk Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -module.exports = { - 'handles mouse click': async function (browser) { - browser.globals.clearReceivedSpans() - await browser.url(browser.globals.getUrl('/user-interaction/mouse.ejs')) - await browser.click('#btn1') - - const clickSpan = await browser.globals.findSpan((span) => span.name === 'click') - await browser.assert.ok(!!clickSpan, 'Checking click span presence.') - - await browser.assert.strictEqual(clickSpan.tags['component'], 'user-interaction') - await browser.assert.strictEqual(clickSpan.tags['event_type'], 'click') - await browser.assert.strictEqual(clickSpan.tags['target_element'], 'BUTTON') - await browser.assert.strictEqual(clickSpan.tags['target_xpath'], '//*[@id="btn1"]') - - await browser.globals.assertNoErrorSpans() - }, - 'handles mouse down': async function (browser) { - browser.globals.clearReceivedSpans() - await browser.url(browser.globals.getUrl('/user-interaction/mouse.ejs')) - await browser.click('#btn1') - - const mouseDownSpan = await browser.globals.findSpan((span) => span.name === 'mousedown') - await browser.assert.ok(!!mouseDownSpan, 'Checking mousedown span presence.') - - await browser.assert.strictEqual(mouseDownSpan.tags['component'], 'user-interaction') - await browser.assert.strictEqual(mouseDownSpan.tags['event_type'], 'mousedown') - await browser.assert.strictEqual(mouseDownSpan.tags['target_element'], 'BUTTON') - await browser.assert.strictEqual(mouseDownSpan.tags['target_xpath'], '//*[@id="btn1"]') - }, - 'handles mouse up': async function (browser) { - browser.globals.clearReceivedSpans() - await browser.url(browser.globals.getUrl('/user-interaction/mouse.ejs')) - await browser.click('#btn1') - - const mouseUpSpan = await browser.globals.findSpan((span) => span.name === 'mouseup') - await browser.assert.ok(!!mouseUpSpan, 'Checking mouseup span presence.') - - await browser.assert.strictEqual(mouseUpSpan.tags['component'], 'user-interaction') - await browser.assert.strictEqual(mouseUpSpan.tags['event_type'], 'mouseup') - await browser.assert.strictEqual(mouseUpSpan.tags['target_element'], 'BUTTON') - await browser.assert.strictEqual(mouseUpSpan.tags['target_xpath'], '//*[@id="btn1"]') - }, - 'handles disabling of mouse events': async function (browser) { - browser.globals.clearReceivedSpans() - await browser.url(browser.globals.getUrl('/user-interaction/mouse-disabled.ejs')) - await browser.click('#btn1') - await browser.click('#btnGuard') - - const guardSpan = await browser.globals.findSpan((span) => span.name === 'guard') - await browser.assert.ok(guardSpan, 'Checking presence of guard span.') - - await browser.assert.not.ok( - browser.globals.getReceivedSpans().find((span) => span.name === 'mouseup'), - 'Ensuring no mouseup span arrived.', - ) - await browser.assert.not.ok( - browser.globals.getReceivedSpans().find((span) => span.name === 'mousedown'), - 'Ensuring no mousedown span arrived.', - ) - await browser.assert.not.ok( - browser.globals.getReceivedSpans().find((span) => span.name === 'click'), - 'Ensuring no click span arrived.', - ) - }, - 'handles clicks with event listener on document': async function (browser) { - browser.globals.clearReceivedSpans() - await browser.url(browser.globals.getUrl('/user-interaction/mouse-document.ejs')) - await browser.click('#btn1') - - const clickSpan = await browser.globals.findSpan((span) => span.name === 'click') - await browser.assert.ok(!!clickSpan, 'Checking click span presence.') - - await browser.assert.strictEqual(clickSpan.tags['component'], 'user-interaction') - await browser.assert.strictEqual(clickSpan.tags['event_type'], 'click') - await browser.assert.strictEqual(clickSpan.tags['target_element'], 'BUTTON') - await browser.assert.strictEqual(clickSpan.tags['target_xpath'], '//*[@id="btn1"]') - - await browser.globals.assertNoErrorSpans() - }, - 'this is bubbled event is correct (open-telemetry/opentelemetry-js-contrib#643)': async function (browser) { - browser.globals.clearReceivedSpans() - await browser.url(browser.globals.getUrl('/user-interaction/mouse-bubble.ejs')) - await browser.click('#inner') - - await browser.globals.findSpan((span) => span.name === 'click') - await browser.expect.element('#result').text.to.equal('container') - - await browser.globals.assertNoErrorSpans() - }, - 'handles svg interactions': async function (browser) { - browser.globals.clearReceivedSpans() - - await browser.url(browser.globals.getUrl('/user-interaction/mouse.ejs')) - await browser.click('#btn-svg-target') - - const clickSpan = await browser.globals.findSpan((span) => span.name === 'click') - await browser.assert.ok(!!clickSpan, 'Checking click span presence.') - - await browser.assert.strictEqual(clickSpan.tags['component'], 'user-interaction') - await browser.assert.strictEqual(clickSpan.tags['event_type'], 'click') - await browser.assert.strictEqual(clickSpan.tags['target_element'], 'rect') - await browser.assert.strictEqual(clickSpan.tags['target_xpath'], '//*[@id="btn-svg-target"]') - - const mouseDownSpan = await browser.globals.findSpan((span) => span.name === 'mousedown') - await browser.assert.ok(!!mouseDownSpan, 'Checking mousedown span presence.') - - await browser.assert.strictEqual(mouseDownSpan.tags['component'], 'user-interaction') - await browser.assert.strictEqual(mouseDownSpan.tags['event_type'], 'mousedown') - await browser.assert.strictEqual(mouseDownSpan.tags['target_element'], 'rect') - await browser.assert.strictEqual(mouseDownSpan.tags['target_xpath'], '//*[@id="btn-svg-target"]') - - const mouseUpSpan = await browser.globals.findSpan((span) => span.name === 'mouseup') - await browser.assert.ok(!!mouseUpSpan, 'Checking mouseup span presence.') - - await browser.assert.strictEqual(mouseUpSpan.tags['component'], 'user-interaction') - await browser.assert.strictEqual(mouseUpSpan.tags['event_type'], 'mouseup') - await browser.assert.strictEqual(mouseUpSpan.tags['target_element'], 'rect') - await browser.assert.strictEqual(mouseUpSpan.tags['target_xpath'], '//*[@id="btn-svg-target"]') - - await browser.globals.assertNoErrorSpans() - }, -} diff --git a/packages/web/integration-tests/tests/visibility/visibility.spec.js b/packages/web/integration-tests/tests/visibility/visibility.spec.js deleted file mode 100644 index ab9c7c8a..00000000 --- a/packages/web/integration-tests/tests/visibility/visibility.spec.js +++ /dev/null @@ -1,43 +0,0 @@ -/** - * - * Copyright 2024 Splunk Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -module.exports = { - 'visibility events are captured': async function (browser) { - await browser.url(browser.globals.getUrl('/visibility/visibility.ejs')) - browser.globals.emulateTabSwitching(true) - - const tabHiddenSpan = await browser.globals.findSpan( - (span) => span.name === 'visibility' && span.tags['hidden'] === 'true', - ) - const hiddenSpans = browser.globals.getReceivedSpans().filter((span) => span.name === 'visibility') - await browser.assert.ok(!!tabHiddenSpan, 'Visibility event for hidden tab exists') - await browser.assert.strictEqual(hiddenSpans.length, 1, 'There is only one visibility event') - - browser.globals.clearReceivedSpans() - - browser.globals.emulateTabSwitching(false) - const tabVisibileSpan = await browser.globals.findSpan( - (span) => span.name === 'visibility' && span.tags['hidden'] === 'false', - ) - const visibleSpans = browser.globals.getReceivedSpans().filter((span) => span.name === 'visibility') - await browser.assert.ok(!!tabVisibileSpan, 'Visibility event for visible tab exists') - await browser.assert.strictEqual(visibleSpans.length, 1, 'There is only one visibility event') - - await browser.globals.assertNoErrorSpans() - }, -} diff --git a/packages/web/integration-tests/tests/websocket/websockets.spec.js b/packages/web/integration-tests/tests/websocket/websockets.spec.js deleted file mode 100644 index 1dff8e2c..00000000 --- a/packages/web/integration-tests/tests/websocket/websockets.spec.js +++ /dev/null @@ -1,151 +0,0 @@ -/** - * - * Copyright 2024 Splunk Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -const { isBrowser } = require('../../utils/helpers') - -function isUnsupported(browser) { - // Test manually works in IE but not here for some reason - return isBrowser(browser, { - 'safari': true, - 'internet explorer': true, - }) -} - -module.exports = { - '@disabled': true, - 'beforeEach': function (browser) { - browser.globals.clearReceivedSpans() - }, - 'produces correct connect span': async function (browser) { - if (isUnsupported(browser)) { - return - } - - const url = browser.globals.getUrl('/websocket/websocket.ejs') - await browser.url(url) - - await browser.click('#connectWs') - const wsConnectionSpan = await browser.globals.findSpan((span) => span.name === 'connect', -1000) - - await browser.assert.strictEqual(wsConnectionSpan.kind, 'CLIENT') - await browser.assert.strictEqual(wsConnectionSpan.tags['location.href'], url) - await browser.assert.strictEqual(wsConnectionSpan.tags['app'], 'splunk-otel-js-dummy-app') - await browser.assert.strictEqual(wsConnectionSpan.tags['component'], 'websocket') - await browser.assert.strictEqual(wsConnectionSpan.tags['error'], undefined) - - await browser.globals.assertNoErrorSpans() - }, - 'websocket url can be ignored': async function (browser) { - if (isUnsupported(browser)) { - return - } - - await browser.url(browser.globals.getUrl('/websocket/websocket-ignored.ejs')) - await browser.click('#connectWs') - - await browser.globals.findSpan((span) => span.name === 'websocket-guard-span') - await browser.assert.not.ok(browser.globals.getReceivedSpans().find((span) => span.name === 'connect')) - }, - 'sending send and on message create a span': async function (browser) { - if (isUnsupported(browser)) { - return - } - - await browser.url(browser.globals.getUrl('/websocket/websocket.ejs')) - await browser.click('#connectWs') - // check for connect span so connection has time to initialize otherwise send will fail - const wsConnectionSpan = await browser.globals.findSpan((span) => span.name === 'connect') - await browser.assert.ok(!!wsConnectionSpan, 'Connect span missing') - await browser.click('#sendWs') - const wsMessage = await browser.globals.findSpan((span) => span.name === 'onmessage') - await browser.assert.ok(!!wsMessage, 'Onmessage span missing') - await browser.assert.strictEqual(wsMessage.kind, 'CONSUMER') - await browser.assert.strictEqual(wsMessage.tags['component'], 'websocket') - await browser.assert.strictEqual(wsMessage.tags['protocol'], '') - await browser.assert.strictEqual(wsMessage.tags['http.url'], browser.globals.wsBase) - await browser.assert.strictEqual(wsMessage.tags['http.response_content_length'], '7') - - const wsSend = browser.globals.getReceivedSpans().find((span) => span.name === 'send') - await browser.assert.ok(!!wsSend, 'Send span missing') - await browser.assert.strictEqual(wsSend.kind, 'PRODUCER') - await browser.assert.strictEqual(wsSend.tags['component'], 'websocket') - await browser.assert.strictEqual(wsSend.tags['protocol'], '') - await browser.assert.strictEqual(wsSend.tags['http.url'], browser.globals.wsBase) - await browser.assert.strictEqual(wsSend.tags['http.request_content_length'], '12') - }, - 'websocket constructor errors are captured': async function (browser) { - if (isUnsupported(browser)) { - return - } - - await browser.url(browser.globals.getUrl('/websocket/websocket-construct-errors.ejs')) - await browser.click('#connectWs') - const wsConnectionSpan = await browser.globals.findSpan((span) => span.name === 'connect', -5000) - await browser.assert.ok(!!wsConnectionSpan, 'Connect span missing') - await browser.assert.strictEqual(wsConnectionSpan.tags['error'], 'true') - }, - 'websocket send errors are captured': async function (browser) { - if (isUnsupported(browser)) { - return - } - - await browser.url(browser.globals.getUrl('/websocket/websocket-send-errors.ejs')) - await browser.click('#connectWs') - // not many ways to generate these errors, - // trying to send msg during connect is the one I know but can be flaky - const wsSend = await browser.globals.findSpan((span) => span.name === 'send') - await browser.assert.ok(!!wsSend, 'Send span missing') - await browser.assert.strictEqual(wsSend.tags['error'], 'true') - await browser.assert.ok(wsSend.tags['error.message']) - await browser.assert.ok(wsSend.tags['error.object']) - }, - 'specifying sub-protocols does not break anything': async function (browser) { - if (isUnsupported(browser)) { - return - } - - const url = browser.globals.getUrl('/websocket/websocket-sub-protocol.ejs') - await browser.url(url) - await browser.click('#connectWs') - // Apparently there are leftover spans from last test even after clear - // as the spans arrive after clearing TODO fix somehow - - const wsConnectionSpan = await browser.globals.findSpan( - (span) => span.name === 'connect' && span.tags['location.href'] === url, - ) - await browser.assert.ok(!!wsConnectionSpan, 'Connect span missing') - await browser.assert.strictEqual(wsConnectionSpan.tags['protocols'], '["soap"]') - - await browser.click('#sendWs') - const wsMessage = await browser.globals.findSpan((span) => span.name === 'onmessage') - await browser.assert.ok(!!wsMessage, 'Onmessage span missing') - await browser.assert.strictEqual(wsMessage.kind, 'CONSUMER') - await browser.assert.strictEqual(wsMessage.tags['component'], 'websocket') - await browser.assert.strictEqual(wsMessage.tags['protocol'], 'soap') - await browser.assert.strictEqual(wsMessage.tags['http.url'], browser.globals.wsBase) - await browser.assert.strictEqual(wsMessage.tags['http.response_content_length'], '7') - - const wsSend = browser.globals.getReceivedSpans().find((span) => span.name === 'send') - await browser.assert.ok(!!wsSend, 'Send span missing') - await browser.assert.strictEqual(wsSend.kind, 'PRODUCER') - await browser.assert.strictEqual(wsSend.tags['component'], 'websocket') - await browser.assert.strictEqual(wsSend.tags['protocol'], 'soap') - await browser.assert.strictEqual(wsSend.tags['http.url'], browser.globals.wsBase) - await browser.assert.strictEqual(wsSend.tags['http.request_content_length'], '12') - }, -} diff --git a/packages/web/integration-tests/tests/webvitals/webvitals.spec.js b/packages/web/integration-tests/tests/webvitals/webvitals.spec.js deleted file mode 100644 index 1cac58f2..00000000 --- a/packages/web/integration-tests/tests/webvitals/webvitals.spec.js +++ /dev/null @@ -1,110 +0,0 @@ -/** - * - * Copyright 2024 Splunk Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -module.exports = { - 'webvitals spans': async function (browser) { - // the google webvitals library only works on chrome (and chrome-based edge) at the moment - if (!browser.globals.isBrowser({ chrome: true, microsoftedge: true })) { - return - } - - const url = browser.globals.getUrl('/webvitals/webvitals.ejs') - await browser.url(url) - // fid won't happen unless there is interaction, so simulate a bit of that - await browser.click('#clicky') - await browser.waitForElementPresent('#p2') - // webvitals library won't send the cls unless a visibility change happens, so - // force a fake one - await browser.execute(function () { - Object.defineProperty(document, 'visibilityState', { value: 'hidden', configurable: true }) - document.dispatchEvent(new Event('visibilitychange')) - }) - // force a sync - await browser.globals.waitForTestToFinish() - - const fid = await browser.globals.findSpan((span) => span.tags.fid !== undefined) - const inp = await browser.globals.findSpan((span) => span.tags.inp !== undefined) - - browser.assert.ok( - !!browser.globals.getReceivedSpans().find((span) => span.tags.lcp !== undefined), - 'Checking presence of lcp span.', - ) - browser.assert.ok( - !!browser.globals.getReceivedSpans().find((span) => span.tags.cls !== undefined), - 'Checking presence of cls span.', - ) - - // sometimes missing in automated tests - if (fid) { - await browser.assert.strictEqual(fid.name, 'webvitals') - await browser.assert.ok(fid.tags.fid > 0) - } - - if (inp) { - await browser.assert.strictEqual(inp.name, 'webvitals') - } - - await browser.globals.assertNoErrorSpans() - }, - 'webvitals - specific metrics disabled': async function (browser) { - // the google webvitals library only works on chrome (and chrome-based edge) at the moment - if (!browser.globals.isBrowser({ chrome: true, microsoftedge: true })) { - return - } - - const url = browser.globals.getUrl('/webvitals/webvitals-specific-disabled.ejs') - await browser.url(url) - // fid won't happen unless there is interaction, so simulate a bit of that - await browser.click('#clicky') - await browser.waitForElementPresent('#p2') - // webvitals library won't send the cls unless a visibility change happens, so - // force a fake one - await browser.execute(function () { - Object.defineProperty(document, 'visibilityState', { value: 'hidden', configurable: true }) - document.dispatchEvent(new Event('visibilitychange')) - }) - // force a sync - await browser.globals.waitForTestToFinish() - - const lcp = await browser.globals.findSpan((span) => span.tags.lcp !== undefined) - const cls = await browser.globals.findSpan((span) => span.tags.cls !== undefined) - const fid = await browser.globals.findSpan((span) => span.tags.fid !== undefined) - const inp = await browser.globals.findSpan((span) => span.tags.inp !== undefined) - - await browser.assert.ok(lcp) - await browser.assert.ok(cls) - - await browser.assert.strictEqual(lcp.name, 'webvitals') - await browser.assert.strictEqual(cls.name, 'webvitals') - - await browser.assert.ok(lcp.tags.lcp > 0) - await browser.assert.ok(cls.tags.cls >= 0) - - // sometimes missing in automated tests - if (fid) { - await browser.assert.strictEqual(fid.name, 'webvitals') - await browser.assert.ok(fid.tags.fid > 0) - } - - if (inp) { - await browser.assert.strictEqual(inp.name, 'webvitals') - } - - await browser.globals.assertNoErrorSpans() - }, -} diff --git a/packages/web/integration-tests/tests/xhr/xhr-basic.spec.js b/packages/web/integration-tests/tests/xhr/xhr-basic.spec.js deleted file mode 100644 index 408a9a9b..00000000 --- a/packages/web/integration-tests/tests/xhr/xhr-basic.spec.js +++ /dev/null @@ -1,84 +0,0 @@ -/** - * - * Copyright 2024 Splunk Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -const { isBrowser } = require('../../utils/helpers') - -module.exports = { - '@tags': ['safari-10.1'], - 'beforeEach': function (browser) { - browser.globals.clearReceivedSpans() - }, - 'XHR request is registered': async function (browser) { - await browser.url(browser.globals.getUrl('/xhr/views/xhr-basic.ejs')) - const xhrSpan = await browser.globals.findSpan( - (span) => span.tags['http.url'] === browser.globals.getUrl('/some-data', []), - ) - await browser.assert.ok(xhrSpan, 'got an xhr span') - await browser.assert.strictEqual(xhrSpan.tags['component'], 'xml-http-request') - await browser.assert.strictEqual(xhrSpan.tags['http.status_code'], '200') - await browser.assert.strictEqual(xhrSpan.tags['http.status_text'], 'OK') - await browser.assert.strictEqual(xhrSpan.tags['http.method'], 'GET') - await browser.assert.strictEqual(xhrSpan.tags['http.url'], browser.globals.getUrl('/some-data', [])) - if (!isBrowser(browser, { safari: true, ie: true })) { - await browser.assert.strictEqual(xhrSpan.tags['http.response_content_length'], '49') - } - - await browser.assert.ok(xhrSpan.tags['link.traceId'], 'got link.traceId') - await browser.assert.ok(xhrSpan.tags['link.spanId'], 'got link.spanId') - - if ( - !isBrowser(browser, { - safari: { max: 10 }, - ie: true, - }) - ) { - await browser.timesMakeSense(xhrSpan.annotations, 'domainLookupStart', 'domainLookupEnd') - await browser.timesMakeSense(xhrSpan.annotations, 'connectStart', 'connectEnd') - if (browser.globals.enableHttps) { - await browser.timesMakeSense(xhrSpan.annotations, 'secureConnectionStart', 'connectEnd') - } - - await browser.timesMakeSense(xhrSpan.annotations, 'requestStart', 'responseStart') - await browser.timesMakeSense(xhrSpan.annotations, 'responseStart', 'responseEnd') - await browser.timesMakeSense(xhrSpan.annotations, 'fetchStart', 'responseEnd') - } - - await browser.timesMakeSense(xhrSpan.annotations, 'open', 'send') - await browser.timesMakeSense(xhrSpan.annotations, 'send', 'loaded') - await browser.globals.assertNoErrorSpans() - }, - 'module can be disabled': async function (browser) { - await browser.url(browser.globals.getUrl('/xhr/views/xhr-basic.ejs')) - await browser.globals.waitForTestToFinish() - - const awaitedUrl = browser.globals.getUrl('/some-data', []) - browser.assert.ok( - !!browser.globals.getReceivedSpans().find((span) => span.tags['http.url'] === awaitedUrl), - 'Checking presence of xhr span.', - ) - - browser.globals.clearReceivedSpans() - await browser.url(browser.globals.getUrl('/xhr/views/xhr-basic.ejs?disableInstrumentation=xhr')) - await browser.globals.waitForTestToFinish() - - browser.assert.not.ok( - browser.globals.getReceivedSpans().find((span) => span.tags['http.url'] === awaitedUrl), - 'Checking lack of xhr span.', - ) - }, -} diff --git a/packages/web/integration-tests/utils/custom-commands/customSauceLabsEnd.js b/packages/web/integration-tests/utils/custom-commands/customSauceLabsEnd.js deleted file mode 100644 index 0bcc9bc3..00000000 --- a/packages/web/integration-tests/utils/custom-commands/customSauceLabsEnd.js +++ /dev/null @@ -1,40 +0,0 @@ -/** - * - * Copyright 2024 Splunk Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -exports.command = async function () { - // 1. Require the `Saucelabs` npm module - const SauceLabs = require('saucelabs') - // 2. Instantiate the module - const myAccount = new SauceLabs.default({ - user: process.env.SAUCE_USERNAME, - key: process.env.SAUCE_ACCESS_KEY, - region: process.env.REGION === 'eu' ? 'eu' : 'us', - }) - // 3a. Get the sessionId - const sessionId = this.sessionId - // 3b. Get the jobName - const jobName = this.currentTest.name - // 3c. Get the status - const passed = this.currentTest.results.testcases[jobName].passed > 0 - - // 4. Update the status in Sauce Labs - await myAccount.updateJob(process.env.SAUCE_USERNAME, sessionId, { passed: passed }) - - // 5. Tell Nighwatch that we are done - return this.end() -} diff --git a/packages/web/integration-tests/utils/globals.js b/packages/web/integration-tests/utils/globals.js deleted file mode 100644 index 9975070a..00000000 --- a/packages/web/integration-tests/utils/globals.js +++ /dev/null @@ -1,205 +0,0 @@ -/** - * - * Copyright 2024 Splunk Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -const { isBrowser } = require('./helpers') -const { buildInstrumentationBackend } = require('../../utils/testBackendProvider') - -const GLOBAL_TEST_BUFFER_TIMEOUT = 20 -const NETWORK_LATENCY_MARGIN = 2000 - -// Allow two buffer timeout cycles to pass, and allow an additional small grace period -const TWO_BUFFER_CYCLES = 2 * GLOBAL_TEST_BUFFER_TIMEOUT -const SPAN_WAIT_TIMEOUT = TWO_BUFFER_CYCLES + NETWORK_LATENCY_MARGIN -const SPAN_WAIT_ITERATION_TIME = Math.round(SPAN_WAIT_TIMEOUT / 10) - -async function findSpan(spans, testFn, accruedTime) { - accruedTime = accruedTime || 0 - if (accruedTime > SPAN_WAIT_TIMEOUT) { - console.error('Listing recorded spans for your convenience.') - console.error(spans) - return null - } - - const foundSpan = spans.find(testFn) - if (foundSpan) { - return foundSpan - } - - return new Promise((resolve) => { - setTimeout(() => { - resolve(findSpan(spans, testFn, accruedTime + SPAN_WAIT_ITERATION_TIME)) - }, SPAN_WAIT_ITERATION_TIME) - }) -} - -async function buildBackendContext(browser) { - const backend = await browser.globals.buildInstrumentationBackend() - - // Setting longer timeout be cause it seems to take forever for spans to arrive in Safari 10 during tests - // Real page seems fine. This timeout could be smaller but better safe than flaky for now. - const defaultTimeout = browser.globals.isBrowser({ safari: { max: 10 } }) ? -10000 : 0 - - return { - httpPort: backend.httpPort, - clearReceivedSpans: backend.clearReceivedSpans, - getReceivedSpans: backend.getReceivedSpans, - findSpan: (testFn, timeout = defaultTimeout) => findSpan(backend.getReceivedSpans(), testFn, timeout), - wsBase: backend.getWebsocketsUrl().toString(), - getLastServerTiming: backend.getLastServerTiming, - getUrl: (...args) => backend.getUrl(...args).toString(), - _closeBackend: backend.close, - assertNoErrorSpans: async () => { - await browser.assert.ok( - backend.getReceivedSpans().every((s) => s.name !== 'onerror'), - 'Ensuring no errors were reported.', - ) - }, - - // left here for old tests - baseUrl: backend.getUrl().toString(), - defaultUrl: backend.getUrl('/', ['wsProtocol', 'wsPort']).toString(), - } -} - -module.exports = { - buildBackendContext, - - // This will be run before each test suite is started - beforeEach: async (browser, done) => { - browser.globals.rumVersion = require('../../package.json').version - browser.globals.isBrowser = isBrowser.bind(null, browser) - - browser.globals.buildInstrumentationBackend = () => - buildInstrumentationBackend({ - enableHttps: browser.globals.enableHttps, - hostname: browser.globals.hostname, - }) - - Object.assign(browser.globals, await buildBackendContext(browser)) - - browser.globals.emulateTabSwitching = async function (visible) { - await browser.execute( - function (hidden) { - Object.defineProperty(document, 'hidden', { value: hidden, configurable: true }) - Object.defineProperty(document, 'visibilityState', { - value: hidden ? 'hidden' : 'visible', - configurable: true, - }) - let e - try { - e = new Event('visibilitychange') - } catch { - // IE - e = document.createEvent('Event') - e.initEvent('visibilitychange', false, false) - } - window.dispatchEvent(e) - }, - [visible], - ) - } - - browser.globals.emulateOffline = async function (online) { - await browser.execute( - function (o) { - Object.defineProperty(navigator, 'onLine', { - get: function () { - return o - }, - configurable: true, - }) - const eventType = o ? 'online' : 'offline' - let e - try { - e = new Event(eventType) - } catch { - // IE - e = document.createEvent('Event') - e.initEvent(eventType, false, false) - } - window.dispatchEvent(e) - }, - [online], - ) - } - - browser.globals.waitForTestToFinish = async () => { - const { value: lastId } = await browser.executeAsync(function (done) { - window.waitForTestToFinish(done) - }) - - if (lastId) { - await browser.globals.findSpan(({ id }) => id === lastId) - } - } - - browser.timesMakeSense = async function (eventsArr, startName, endName) { - const name2time = {} - eventsArr.forEach((event) => { - name2time[event.value] = event.timestamp - }) - await this.assert.ok(name2time[startName], `Checking presence of ${startName}`) - await this.assert.ok(name2time[endName], `Checking presence of ${endName}`) - await this.assert.ok( - name2time[startName] <= name2time[endName], - `Checking sanity of ${startName} vs ${endName}`, - ) - const diff = name2time[endName] - name2time[startName] - // times reported in zipkin as micros - // looking for egregiously incorrect response times here, not a tight bound - const fiveMinutes = 5 * 60 * 1000 * 1000 - await this.assert.ok(diff < fiveMinutes, 'Sanity check for time difference.') - // Also looking for rough synchronization with reality (at least from our CI systems/laptops...) - const nowMicros = new Date().getTime() * 1000 - let clockSkew = name2time[startName] - nowMicros - if (clockSkew < 0) { - clockSkew = -clockSkew - } - - await this.assert.ok(clockSkew <= fiveMinutes, 'Sanity check for clock skew') - } - - console.log('Started dev server.') - - // note: at the time this was written util.promisify breaks nightwatch here - browser.status(() => { - done() - }) - }, - - // This will be run after each test suite is finished - afterEach: async function (browser, done) { - // console.log('after each', browser) - try { - console.log('Closing dev server.') - if (browser.globals._closeBackend) { - await browser.globals._closeBackend() - } - - console.log('Closed dev server.') - - if (browser.options.webdriver.access_key) { - browser.customSauceLabsEnd() - } - } finally { - done() - } - }, - - GLOBAL_TEST_BUFFER_TIMEOUT, -} diff --git a/packages/web/integration-tests/utils/helpers.js b/packages/web/integration-tests/utils/helpers.js deleted file mode 100644 index c557914b..00000000 --- a/packages/web/integration-tests/utils/helpers.js +++ /dev/null @@ -1,61 +0,0 @@ -/** - * - * Copyright 2024 Splunk Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -/** - * Test if current browser matches condition. Condition can be: - * - * * 'chrome' (one browser) - * * {chrome: true} (one or multiple browsers) - * * {chrome: 80} (chrome 80 - ...) - * * {chrome: {max: 79}} (chrome ... - 79) - * * {chrome: {min: 70, max: 79}} (chrome 70 - 79) - * - * @typedef {'chrome' | 'microsoftedge' | 'firefox' | 'ie' | 'safari'} BrowserName - * @typedef {{min?: number; max?: number}} VersionCondition - * @typedef {{[x in BrowserName]?: number | VersionCondition}} BrowsersCondition - * @param {import('nightwatch').NightwatchBrowser} browser Nightwatch browser object - * @param {BrowsersCondition | BrowserName} matches Browser name or object to match with - * @return boolean - */ -function isBrowser(browser, matches) { - const browserName = browser.options.desiredCapabilities.browserName.toLowerCase() - if (typeof matches === 'string') { - return browserName === matches - } - - if (!(browserName in matches)) { - return false - } - - let condition = matches[browserName] - switch (typeof condition) { - case 'boolean': - return condition - case 'number': - condition = { min: condition } - } - - const { min, max } = condition - const version = parseInt(browser.options.desiredCapabilities.browser_version, 10) - - return (!min || version >= min) && (!max || version <= max) -} - -module.exports = { - isBrowser, -} diff --git a/packages/web/integration-tests/utils/local.conf.js b/packages/web/integration-tests/utils/local.conf.js deleted file mode 100644 index aefd76bf..00000000 --- a/packages/web/integration-tests/utils/local.conf.js +++ /dev/null @@ -1,172 +0,0 @@ -/** - * - * Copyright 2024 Splunk Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -const path = require('path') - -const geckodriver = require('geckodriver') - -const nightwatch_config = { - src_folders: ['integration-tests/tests'], - globals_path: path.join(__dirname, 'globals.js'), - filter: '**/*.spec.js', - - webdriver: {}, - // Selenium Server is running locally and is managed by Nightwatch - /* - selenium: { - start_process: true, - port: 9515, - server_path: '', // Leave empty if @nightwatch/selenium-server is installed - command: 'standalone', // Selenium 4 only - check_process_delay: 5000, - cli_args: { - 'webdriver.chrome.driver': chromeDriver.path, - 'webdriver.gecko.driver': geckoDriver.path, - } - }, - */ - - // detailed_output: false, - test_settings: { - default: { - desiredCapabilities: { - acceptSslCerts: true, - acceptInsecureCerts: true, - }, - globals: { - hostname: 'localhost', - enableHttps: true, - }, - }, - chrome: { - desiredCapabilities: { - 'browserName': 'chrome', - 'goog:chromeOptions': { - // More info on Chromedriver: https://sites.google.com/a/chromium.org/chromedriver/ - // - // w3c:false tells Chromedriver to run using the legacy JSONWire protocol (not required in Chrome 78) - w3c: true, - args: [ - //'--no-sandbox', - '--ignore-certificate-errors', - //'--allow-insecure-localhost', - // '--headless' - ], - }, - }, - webdriver: { - start_process: true, - server_path: '', - cli_args: [ - // --verbose - ], - }, - }, - headlessChrome: { - desiredCapabilities: { - 'browserName': 'chrome', - 'goog:chromeOptions': { - // More info on Chromedriver: https://sites.google.com/a/chromium.org/chromedriver/ - // - // w3c:false tells Chromedriver to run using the legacy JSONWire protocol (not required in Chrome 78) - w3c: true, - args: [ - //'--no-sandbox', - '--ignore-certificate-errors', - //'--allow-insecure-localhost', - '--headless', - ], - }, - }, - webdriver: { - start_process: true, - server_path: '', - cli_args: [ - // --verbose - ], - }, - }, - - firefox: { - desiredCapabilities: { - browserName: 'firefox', - alwaysMatch: { - 'moz:firefoxOptions': { - args: [ - // '-headless', - // '-verbose' - ], - }, - }, - }, - webdriver: { - start_process: true, - server_path: '', - cli_args: [ - // very verbose geckodriver logs - // '-vv' - ], - }, - }, - - headlessFirefox: { - desiredCapabilities: { - 'browserName': 'firefox', - 'alwaysMatch': { - 'moz:firefoxOptions': { - args: [ - '-headless', - // '-verbose' - ], - }, - }, - 'moz:firefoxOptions': { - args: [ - '-headless', - // '-verbose' - ], - }, - }, - webdriver: { - start_process: true, - server_path: process.env.GECKOWEBDRIVER - ? `${process.env.GECKOWEBDRIVER}/geckodriver` - : (geckodriver.path ?? ''), - host: '127.0.0.1', - cli_args: [ - // very verbose geckodriver logs - '-vv', - ], - }, - }, - safari: { - desiredCapabilities: { - browserName: 'safari', - alwaysMatch: { - acceptInsecureCerts: false, - }, - }, - webdriver: { - start_process: true, - server_path: '', - }, - }, - }, -} - -module.exports = nightwatch_config diff --git a/packages/web/integration-tests/utils/saucelabs.conf.js b/packages/web/integration-tests/utils/saucelabs.conf.js deleted file mode 100644 index a9ed0a1a..00000000 --- a/packages/web/integration-tests/utils/saucelabs.conf.js +++ /dev/null @@ -1,118 +0,0 @@ -/** - * - * Copyright 2024 Splunk Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -const { join } = require('path') -const build = `Nightwatch Desktop Web build-${new Date().getTime()}` - -// More information about the configuration file can be found here -// https://nightwatchjs.org/gettingstarted/configuration/ -module.exports = { - src_folders: ['integration-tests/tests'], - globals_path: join(__dirname, 'globals.js'), - // See `/tests/custom-commands/customSauceLabsEnd.js` for the logic - custom_commands_path: join(__dirname, 'custom-commands/'), - filter: '**/*.spec.js', - - test_workers: { - enabled: true, - workers: 'auto', - }, - - test_settings: { - // Our Sauce Labs object - sauceLabs: { - selenium_host: `ondemand.${process.env.REGION === 'eu' ? 'eu-central-1' : 'us-west-1'}.saucelabs.com`, - selenium_port: 80, - username: process.env.SAUCE_USERNAME, - access_key: process.env.SAUCE_ACCESS_KEY, - desiredCapabilities: { - 'acceptSslCerts': true, - 'acceptInsecureCerts': true, - 'sauce:options': { - build, - tunnelName: process.env.SAUCE_TUNNEL_ID, - screenResolution: '1600x1200', - seleniumVersion: '3.141.59', - extendedDebugging: true, - }, - }, - globals: { - hostname: 'local.test', - enableHttps: false, - }, - }, - localChrome: { - desiredCapabilities: { - browserName: 'chrome', - alwaysMatch: { - 'goog:chromeOptions': { - args: ['--no-sandbox', '--disable-infobars', '--headless'], - }, - }, - }, - - webdriver: { - start_process: true, - port: 9515, - server_path: require('chromedriver').path, - }, - }, - - // Sauce Labs capabilities - chrome: { - extends: 'sauceLabs', - desiredCapabilities: { - browserName: 'chrome', - platform: 'Windows 10', - version: 'latest', - }, - }, - firefox: { - extends: 'sauceLabs', - desiredCapabilities: { - browserName: 'firefox', - platform: 'Windows 10', - version: 'latest', - }, - }, - ie11: { - extends: 'sauceLabs', - desiredCapabilities: { - browserName: 'internet explorer', - browserVersion: '11', - platformName: 'Windows 10', - }, - }, - edge: { - extends: 'sauceLabs', - desiredCapabilities: { - browserName: 'MicrosoftEdge', - platform: 'Windows 10', - version: 'latest', - }, - }, - safari: { - extends: 'sauceLabs', - desiredCapabilities: { - 'browserName': 'safari', - 'browserVersion': '13', - 'sauce:options': {}, - }, - }, - }, -} diff --git a/packages/web/package.json b/packages/web/package.json index 7b4a15ba..cfc7dc8c 100644 --- a/packages/web/package.json +++ b/packages/web/package.json @@ -4,27 +4,12 @@ "description": "Splunk distribution of Open Telemetry for browser environment.", "repository": "github:signalfx/splunk-otel-js-browser", "scripts": { - "start": "npm-run-all -s compile start:_execute", - "start:_execute": "node utils/devServer/runDevServer.js", "compile": "npm-run-all -s compile:*", "compile:browser": "rollup -c", "compile:tsc": "tsc --build tsconfig.cjs.json tsconfig.esm.json", "test:unit:watch": "karma start", "test:unit:ci": "nyc karma start --single-run --no-auto-watch --browsers ChromeHeadlessCI", "test:unit:ci-node": "env TS_NODE_TRANSPILE_ONLY=true TS_NODE_PROJECT=tsconfig.cjs.json nyc mocha", - "test:integration:local": "npm-run-all -s compile test:integration:local:headlessChrome:_execute", - "test:integration:local:firefox": "npm-run-all -s compile test:integration:local:firefox:_execute", - "test:integration:local:chrome": "npm-run-all -s compile test:integration:local:chrome:_execute", - "test:integration:local:safari": "npm-run-all -s compile test:integration:local:safari:_execute", - "test:integration:local:all": "npm-run-all -s compile test:integration:local:all:_execute", - "test:integration:local:all:_execute": "nightwatch -c ./integration-tests/utils/local.conf.js -e headlessChrome,headlessFirefox,chrome,safari", - "test:integration:local:chrome:_execute": "nightwatch -c ./integration-tests/utils/local.conf.js -e chrome", - "test:integration:local:firefox:_execute": "nightwatch -c ./integration-tests/utils/local.conf.js -e firefox", - "test:integration:local:headlessChrome:_execute": "nightwatch -c ./integration-tests/utils/local.conf.js -e headlessChrome", - "test:integration:local:headlessFirefox:_execute": "nightwatch -c ./integration-tests/utils/local.conf.js -e headlessFirefox", - "test:integration:local:safari:_execute": "nightwatch -c ./integration-tests/utils/local.conf.js -e safari", - "test:integration:remote": "nightwatch --config ./integration-tests/utils/saucelabs.conf.js", - "test:integration:_selfSignCerts": "(cd utils && cd certs && openssl req -nodes -new -x509 -keyout server.key -out server.cert)", "test:performance": "node ./performance-tests/runner.js" }, "author": "Splunk Observability Instrumentals Team ", @@ -107,7 +92,6 @@ "rollup": "^4.18.0", "rollup-plugin-istanbul": "^4.0.0", "rollup-plugin-polyfill-node": "^0.12.0", - "saucelabs": "^7.2.2", "sinon": "^15.2.0", "socket.io": "^4.7.1", "socket.io-client": "^4.7.1", diff --git a/packages/web/rollup.config.mjs b/packages/web/rollup.config.mjs index 046a2815..53a1f9dd 100644 --- a/packages/web/rollup.config.mjs +++ b/packages/web/rollup.config.mjs @@ -79,7 +79,7 @@ export default [ context: 'window', }, { - input: 'integration-tests/otel-api-globals.ts', + input: 'src/otel-api-globals.ts', output: { file: 'dist/artifacts/otel-api-globals.js', format: 'iife', diff --git a/packages/web/integration-tests/otel-api-globals.ts b/packages/web/src/otel-api-globals.ts similarity index 100% rename from packages/web/integration-tests/otel-api-globals.ts rename to packages/web/src/otel-api-globals.ts diff --git a/packages/web/utils/certs/README.md b/packages/web/utils/certs/README.md deleted file mode 100644 index b034b1d8..00000000 --- a/packages/web/utils/certs/README.md +++ /dev/null @@ -1,6 +0,0 @@ -# A note on certs in this directory -The certificate files you will find in this directory are self-signed, and only used for locally executed tests. -You can regenerate them by running: -```bash -openssl req -nodes -new -x509 -keyout server.key -out server.cert -``` diff --git a/packages/web/utils/certs/server.cert b/packages/web/utils/certs/server.cert deleted file mode 100644 index 07c5ee72..00000000 --- a/packages/web/utils/certs/server.cert +++ /dev/null @@ -1,17 +0,0 @@ ------BEGIN CERTIFICATE----- -MIICpDCCAYwCCQDQgkbpp7L17TANBgkqhkiG9w0BAQsFADAUMRIwEAYDVQQDDAls -b2NhbGhvc3QwHhcNMjAxMDE0MTUyOTIwWhcNMjAxMTEzMTUyOTIwWjAUMRIwEAYD -VQQDDAlsb2NhbGhvc3QwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDY -kvyzqq2bJWNebHs1OFf4a8ea9VgVQetE6AYJcxcnLmunRdB16pBaB5NmK0ZoJLfa -rRDPtjMwG0ZI0MPeYH2zsTtFx7lb6ZTZGx8c3lA9LNNje+leJgF0Yo7KMOdgqHXj -7c0G5aC5ua/L/HRa1ecDv4sD9CHFrKObkPAYVCGSlYFHKLC6UcVo/TZU2t2MOgYu -opQ4j+3i2A2kepmUr2+NFuQMG4MkKHYL+wn2n/jITTHpffRHYqwOV5kBHGnABpJx -9T6wjO6+MZ5VB05qbCfOusc4WSQFa/XeFKEMGvJxIF1IS0Nd5nUCy3u44yHt+Q+U -Dw9yvHYjgNdL1hozpEI/AgMBAAEwDQYJKoZIhvcNAQELBQADggEBAA09fiCh7bzN -N3s1zwJqwKt6Zw5GbpbdlQc/8e0wkEBscmmu7QWSDThWFM0CHBteDQLULyWMg1lx -2UIFVS66Mm4UWE+/FC+Kv7/vZD9Gm/Z6Qcjz8ZCyAToX66T8ezVxPs5B2NtzuNd4 -/KZAHz5EhJpTLIOtzuz/80RKMiGtt5mm8Ap5dc9blF5uv87KtWHcZUwaIi+YQNaE -it+h/zDM8fSKzMLLj9m76kVl27qJHRXU+w7r+AuAvzaaWVlsdTHHopMiKJEpviAo -bmNl1BIab2FWQdFs4A2IySwnl9USHEAZEtiwa1SycEU6ZWzoVDJi0IzAxIsAApm1 -XJra0fmcCOo= ------END CERTIFICATE----- diff --git a/packages/web/utils/certs/server.key b/packages/web/utils/certs/server.key deleted file mode 100644 index 59d003f4..00000000 --- a/packages/web/utils/certs/server.key +++ /dev/null @@ -1,28 +0,0 @@ ------BEGIN PRIVATE KEY----- -MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQDYkvyzqq2bJWNe -bHs1OFf4a8ea9VgVQetE6AYJcxcnLmunRdB16pBaB5NmK0ZoJLfarRDPtjMwG0ZI -0MPeYH2zsTtFx7lb6ZTZGx8c3lA9LNNje+leJgF0Yo7KMOdgqHXj7c0G5aC5ua/L -/HRa1ecDv4sD9CHFrKObkPAYVCGSlYFHKLC6UcVo/TZU2t2MOgYuopQ4j+3i2A2k -epmUr2+NFuQMG4MkKHYL+wn2n/jITTHpffRHYqwOV5kBHGnABpJx9T6wjO6+MZ5V -B05qbCfOusc4WSQFa/XeFKEMGvJxIF1IS0Nd5nUCy3u44yHt+Q+UDw9yvHYjgNdL -1hozpEI/AgMBAAECggEAfov0qK70XhFMrOHKZGCV1oxzx7HQVCt+Y3cIAAGF59kS -Al2i3yO+UTzX/f0PWM8LtvUlO5mE83kcanPB33CWQqcusSlwv6oO1YUpet7UF8rX -gevngqHGe9ETCkXlDO+iYxqSkJMghSVg+Vp8So9uOxxzCE5gZXbo6n+YKz8Nlu48 -32qvFLtHzUXavR48zfTfiKcEjKEz4ALmgUhezp4KXT0EagIoN3rkXD44rFgmLwrl -ANkE5evhcFFnbeOCQMmQacse1lrI7T84CmhRJy+Ds3yX7EkCCDlZPN3ecnq6lk+Y -glJXGTUxKenrR27kC1BvzOKlWjHDZh6vE7HYLmFl8QKBgQD+772ssMTSZckWXZrD -Bgvo12W0JKCrrJsNZI8CcC1QWAaHzdjJ/Wwq8LBwJfeQOA8En7oQLFUe0GivNTUN -azVFv4HSISOkRPf4Z5iFmuNFhLMDErFC+Hp1628rzolb1QFVnyyQQtK5ae/ivwll -YhJhQtKFMQwU5IanlyBrDhy+tQKBgQDZekb3XcECaU0S20KNBozK7gYQCg4/Wxu0 -o8bZL1v4VNle7+g+zXtUFXVFxBWWGDKZFKv8m2bsNZyAM/wcPusYnGSh4lzf8HJX -vaUml8a9+3b/Rx2CI+5SPzXVnGHxad/nTiE3lMVk4jn4WakZXIAY1EZ1jcjxl4cg -nyoBjyuhowKBgFme8CAIWHjafXGWYHSU6JTnPAKzKaSQXkW8ByZFRzjKaSlOrRp9 -M4x45p3WSwxxGJ54hFFxNE9L1rFZVBguiyDE7R9wtPXnpQC3cn/tbl4fA0teQ+CW -N37C0GcnCVS2MezmsyTXyPqTilg87lGsw2dbtWRLVML3f3rk/umDoYlNAoGAV7wr -uwmYnudqT4wjCg+1Zvyld30HCGoyACr+QooHmWRpmRJB6GwvL9nESNP1PWsfpoAf -bjYnTG3IaDQQpjh/0ZruQhy3CSTQjDMW2D3s0KIM/Kls7x6du5KCnl2LacAjswXj -ArBC/hMB/f316SRXDS0+C5iak1RlEDUF1kEzAe8CgYBYFHlSaW+0PgHdoMau0zdH -9uqvST2Wo/srMlWOsUmNHkONw/rHDk5bP7FLEZtMiahrx7PxhjDO/weRwa4dHeFF -zA1h2Z8JwECf2XPsG0+uBN6GaLU8rlbaUYCyJhMI8MHRCbZo8kkLJtT0E3DD1mkV -ZJlGq2+Ha3khTCeoEK9R+Q== ------END PRIVATE KEY----- diff --git a/packages/web/utils/devServer/devServer.js b/packages/web/utils/devServer/devServer.js deleted file mode 100644 index b2cc8ef4..00000000 --- a/packages/web/utils/devServer/devServer.js +++ /dev/null @@ -1,259 +0,0 @@ -/** - * - * Copyright 2024 Splunk Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -const path = require('path') -const express = require('express') -const bodyParser = require('body-parser') -const cors = require('cors') -const WebSocket = require('ws') -const { Server: SIOServer } = require('socket.io') -const { renderFile } = require('ejs') -const { buildBasicLocalServer } = require('../../utils/server') -const { generateServerTiming, setServerTimingHeader } = require('../../utils/server/serverTiming') -const { registerTemplateProvider } = require('./templateProvider') - -function buildServer({ enableHttps, listener }) { - const { server, serverOptions } = buildBasicLocalServer({ enableHttps, listener }) - - const sockets = {} - let nextSocketId = 0 - server.on('connection', function (socket) { - nextSocketId += 1 - const socketId = nextSocketId - sockets[socketId] = socket - socket.on('close', () => { - delete sockets[socketId] - }) - }) - - function close() { - return new Promise((resolve, reject) => { - for (const socketId in sockets) { - sockets[socketId].destroy() - } - - const address = server.address() - server.close((err) => { - if (err) { - console.error(`Unable to close server at port ${address.port}`, err) - reject(err) - } else { - console.debug(`Closed server at port ${address.port}`) - resolve() - } - }) - }) - } - - return { server, serverOptions, close } -} - -function startHttpServer({ enableHttps, listener, port }) { - const { server, serverOptions, close } = buildServer({ enableHttps, listener }) - return new Promise((resolve, reject) => { - server.on('listening', () => { - console.info('Listening now at: ', server.address()) - resolve({ - close, - address: server.address(), - serverOptions, - server, - }) - }) - - server.on('error', (e) => { - console.error(e) - reject(e) - }) - - // 0 acquires a random available port - // 127.0.0.1 has been found to work with Circle CI and Browserstack well - server.listen(port, '127.0.0.1') - }) -} - -function startWebsocketServer({ enableHttps, listener, port }) { - const { server, serverOptions, close } = buildServer({ enableHttps, listener }) - return new Promise((resolve, reject) => { - const websocketServer = new WebSocket.Server({ server }) - - server.on('listening', () => { - console.info('Listening now at: ', server.address()) - resolve({ - close, - address: server.address(), - serverOptions, - server, - websocketServer, - }) - }) - - server.on('error', (e) => { - console.error(e) - reject(e) - }) - - // 0 acquires a random available port - // 127.0.0.1 has been found to work with Circle CI and Browserstack well - server.listen(port, '127.0.0.1') - }) -} - -function getSpans(spanOrSpans) { - if (Array.isArray(spanOrSpans)) { - return spanOrSpans.flatMap(getSpans) - } - - return [ - { - name: spanOrSpans.name, - timestamp: spanOrSpans.timestamp, - duration: spanOrSpans.duration, - tags: spanOrSpans.tags, - kind: spanOrSpans.kind, - annotations: spanOrSpans.annotations, - traceId: spanOrSpans.traceId, - id: spanOrSpans.id, - parentId: spanOrSpans.parentId, - }, - ] -} - -exports.runIntegrationDevelopmentServer = async function run({ - onSpanReceived, - enableHttps, - httpPort: requestedHttpPort, - websocketsPort: requestedWebsocketsPort, -}) { - enableHttps = enableHttps || false - let lastServerTiming - function addHeaders(response) { - lastServerTiming = generateServerTiming() - setServerTimingHeader(response, lastServerTiming) - } - - if (!onSpanReceived) { - onSpanReceived = () => {} - } - - const app = express() - app.engine('html', renderFile) - app.engine('ejs', renderFile) - - app.use(bodyParser.json({ type: 'text/plain' })) - app.use(bodyParser.json({ type: 'application/json' })) - app.use(cors()) - - app.get('/', function (_, res) { - addHeaders(res) - return res.render(path.resolve(__dirname, 'index.html')) - }) - app.get('/empty-page', (_, res) => { - res.send('') - }) - - app.use(function (req, res, next) { - if (req.query && req.query.t) { - const timeout = parseInt(req.query.t) - setTimeout(next, timeout) - } else { - next() - } - }) - - registerTemplateProvider({ app, addHeaders, enableHttps }) - - app.get('/utils/devServer/assets/no-cache.png', function (req, res) { - res.sendFile(path.join(__dirname, './assets/no-cache.png'), { - etag: false, - lastModified: false, - headers: { - 'Cache-Control': 'no-store, max-age=0', - }, - }) - }) - app.use(express.static(path.join(__dirname, '../../'))) - - app.post('/api/v2/spans', (req, res) => { - getSpans(req.body).forEach(onSpanReceived) - res.send('') - }) - - app.get('/some-data', (req, response) => { - setServerTimingHeader(response) - response.send({ - key1: 'value1', - key2: 'value2', - key3: 'value3', - }) - }) - - app.post('/echo', (req, res) => { - res.send(req.body) - }) - - app.get('/no-server-timings', (_, res) => { - res.sendStatus(200) - }) - - const { - close: closeHttpServer, - address: { port: httpPort }, - server: httpServer, - serverOptions: httpOptions, - } = await startHttpServer({ enableHttps, listener: app, port: requestedHttpPort }) - app.set('httpPort', httpPort) - - const { - close: closeWebsocketServer, - address: { port: websocketsPort }, - serverOptions: wsOptions, - websocketServer, - } = await startWebsocketServer({ enableHttps, port: requestedWebsocketsPort }) - - websocketServer.on('connection', function connection(ws) { - console.log('websocket connected') - ws.on('message', function incoming(message) { - console.log('ws received: ', message) - ws.send('success') - }) - }) - - const io = new SIOServer(httpServer) - - io.on('connection', function (socket) { - console.log('socket.io connected', socket.id) - socket.on('hello', () => { - // Nothing - }) - socket.on('ping', () => { - socket.emit('pong') - }) - }) - - return { - close: () => Promise.all([closeHttpServer(), closeWebsocketServer()]), - httpHostname: httpOptions.hostname, - httpPort, - httpProtocol: enableHttps ? 'https' : 'http', - websocketsHostname: wsOptions.hostname, - websocketsPort, - websocketsProtocol: enableHttps ? 'wss' : 'ws', - getLastServerTiming: () => lastServerTiming, - } -} diff --git a/packages/web/utils/devServer/index.html b/packages/web/utils/devServer/index.html deleted file mode 100644 index f05b7bc5..00000000 --- a/packages/web/utils/devServer/index.html +++ /dev/null @@ -1,100 +0,0 @@ - - - - - - - - - - -

Hi!

- - - Change name - Change name after fetch -

- - Use this button to trigger a span related to an unhandled promise rejection. -

- -

- - Use this button to tigger a span for a regular fetch request. -

- -

- - Use this button to tigger a span for connecting to a websockets server. -

- - - - - - diff --git a/packages/web/utils/devServer/templateProvider.js b/packages/web/utils/devServer/templateProvider.js deleted file mode 100644 index 5fe92079..00000000 --- a/packages/web/utils/devServer/templateProvider.js +++ /dev/null @@ -1,133 +0,0 @@ -/** - * - * Copyright 2024 Splunk Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -const path = require('path') -const fs = require('fs') - -const { render } = require('ejs') - -const INJECT_TEMPLATE = ` - - -` - -exports.registerTemplateProvider = ({ app, addHeaders, enableHttps }) => { - app.use(function (req, res, next) { - const filepath = path.resolve(__dirname, '..', '..', 'integration-tests', 'tests', req.path.substring(1)) - if (fs.existsSync(filepath)) { - const beaconUrl = new URL(`${enableHttps ? 'https' : 'http'}://${req.headers.host}/api/v2/spans`) - if (req.query.beaconPort) { - beaconUrl.port = req.query.beaconPort - } - - let defaultFile = '/dist/artifacts/splunk-otel-web.js' - const browser = req.header('User-Agent') - if (browser && browser.includes('Trident')) { - defaultFile = '/dist/artifacts/splunk-otel-web-legacy.js' - } - - addHeaders(res) - return res.render(filepath, { - httpPort: app.get('httpPort'), - renderAgent(userOpts = {}, noInit = false, file = defaultFile, cdnVersion = null) { - const options = { - beaconEndpoint: beaconUrl.toString(), - applicationName: 'splunk-otel-js-dummy-app', - debug: true, - bufferTimeout: require('../../integration-tests/utils/globals').GLOBAL_TEST_BUFFER_TIMEOUT, - ...userOpts, - } - - if (req.query.disableInstrumentation) { - if (!options.instrumentations) { - options.instrumentations = {} - } - - req.query.disableInstrumentation.split(',').forEach((instrumentation) => { - options.instrumentations[instrumentation] = false - }) - } - - if (cdnVersion) { - file = `https://cdn.signalfx.com/o11y-gdi-rum/${cdnVersion}/splunk-otel-web.js` - } - - return render(INJECT_TEMPLATE, { - file, - noInit, - options: JSON.stringify(options), - otelApiGlobalsFile: '/dist/artifacts/otel-api-globals.js', - }) - }, - }) - } - - return next() - }) -} diff --git a/packages/web/utils/server/index.js b/packages/web/utils/server/index.js deleted file mode 100644 index cedc453b..00000000 --- a/packages/web/utils/server/index.js +++ /dev/null @@ -1,80 +0,0 @@ -/** - * - * Copyright 2024 Splunk Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -const http = require('http') -const https = require('https') -const { getCertificateConfig } = require('../certs') - -const HOST = 'localhost' - -function buildBasicLocalServer({ enableHttps, listener }) { - const serverOptions = { - hostname: 'localhost', - ...getCertificateConfig(), - } - - const server = (enableHttps ? https : http).createServer(serverOptions, listener) - return { server, serverOptions } -} - -function buildPromisifiedClose(server) { - return () => - new Promise((resolve, reject) => { - const address = server.address() - server.close((err) => { - if (err) { - console.error(err) - reject(err) - } else { - console.debug(`Closed server at port ${address.port}`) - resolve() - } - }) - }) -} - -async function startLocalHttpServer({ enableHttps, listener, port }) { - enableHttps = enableHttps === undefined ? true : enableHttps - - const { server, serverOptions } = buildBasicLocalServer({ enableHttps, listener }) - return new Promise((resolve, reject) => { - server.on('listening', () => { - console.info('Listening now at: ', server.address()) - resolve({ - close: buildPromisifiedClose(server), - address: server.address(), - serverOptions, - }) - }) - - server.on('error', (e) => { - console.error(e) - reject(e) - }) - - // 0 acquires a random available port - // 127.0.0.1 has been found to work with Circle CI and Browserstack well - server.listen(port === undefined ? 0 : port, HOST) - }) -} - -module.exports = { - buildBasicLocalServer, - startLocalHttpServer, - buildPromisifiedClose, -} diff --git a/packages/web/utils/testBackendProvider.js b/packages/web/utils/testBackendProvider.js deleted file mode 100644 index 206de3be..00000000 --- a/packages/web/utils/testBackendProvider.js +++ /dev/null @@ -1,73 +0,0 @@ -/** - * - * Copyright 2024 Splunk Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -const { runIntegrationDevelopmentServer } = require('./devServer/devServer') - -exports.buildInstrumentationBackend = async ({ - enableHttps, - hostname, - httpPort: requestedHttpPort, - websocketsPort: requestedWebsocketsPort, -}) => { - const spans = [] - function handleSpanReceived(span) { - spans.push(span) - } - - const { close, httpProtocol, httpPort, websocketsProtocol, websocketsPort, getLastServerTiming } = - await runIntegrationDevelopmentServer({ - onSpanReceived: handleSpanReceived, - enableHttps, - httpPort: requestedHttpPort, - websocketsPort: requestedWebsocketsPort, - }) - - const availableSearchParams = { - wsProtocol: websocketsProtocol, - wsPort: websocketsPort, - } - - function getUrl(path = '/', includedParams = Object.keys(availableSearchParams), otherParams = {}) { - const url = new URL(path, `${httpProtocol}://${hostname}:${httpPort}`) - includedParams.forEach((name) => { - url.searchParams.set(name, availableSearchParams[name]) - }) - Object.entries(otherParams).forEach(([key, value]) => url.searchParams.set(key, value)) - return url - } - - function getWebsocketsUrl() { - return new URL(`${websocketsProtocol}://${hostname}:${websocketsPort}`) - } - - console.log(`Running integration tests server at: ${getUrl()}`) - console.log(`Running integration tests websockets server at: ${getWebsocketsUrl()}`) - - return { - clearReceivedSpans: () => { - spans.length = 0 - }, - close, - getLastServerTiming, - getReceivedSpans: () => spans, - getUrl, - getWebsocketsUrl, - httpPort, - httpHostname: hostname, - } -}