From 82fd8399b0bc13b43e354fa3cd407ab82b3d2105 Mon Sep 17 00:00:00 2001 From: Robbie Date: Mon, 26 Aug 2024 18:59:13 +0100 Subject: [PATCH] chore: Remove retries for testcafe tests but increase timeout. Wait for lag in parallel (#1380) * Bump typescript version to 5.5 and also bump related libraries * Remove retries but increase timeout * Wrap in Selector * Add console log saying how long it took to get results * Add "after" query param * Add max allowed api errors * Stop on first fail * Make testSessionId more readable, change ingestion endpoint * Don't use uuidv7 * Pass env vars as dependencies * Improve test namr * Fix typo * Typo * only assert that custom-event was seen * try using a different distinct_id * undo changes, look for all events * use successfunction to look for custom-event in response * Move asserts to a later step in the CI so that ingestion lag can be in parallel * Tidy up deps Move ts-node to dev deps Upgrade express due to dep warning * Tidy up how results file are written and read * Use jest override * Fix complaining about timeout and interval types * WIP * Override testcafe's typescript compiler * Revert check script to js * Sanitise file names * Correctly named script * Some more debuggability * Add a script to make it easier to run locally * Add some debugging * Fix check file * Revert changes to wait duration * Move waits around * Add assertion for whether the snippet has loaded * Revert the assertion event count * Re-add missing asserts * Remove some unused stuff * Be more specific about test files * Fix url * Disable request batching * Rewrite the retry loop to do an attempt immediately * Assert on the number of captures * Fix event count assert * Add captures map * Test * Fix captures * Fix first assert * Fix other asserts * Fix deadline * Increase timeout to 30 minutes * Use optional catch binding syntax * Add an explainer comment * Remove unnecessary hackiness * Slightly improve the hackiness even more --------- Co-authored-by: Phani Raj --- .eslintrc.js | 7 + .github/workflows/testcafe.yml | 9 +- .testcaferc.js | 7 +- package.json | 21 +- pnpm-lock.yaml | 696 ++++++++++-------- scripts/run-testcafe-localhost.mjs | 38 + scripts/run-testcafe-with-retries.mjs | 31 - src/__tests__/personProcessing.test.ts | 2 +- src/entrypoints/recorder.ts | 2 +- .../exception-autocapture/error-conversion.ts | 4 +- .../exception-autocapture/type-checking.ts | 2 +- src/extensions/toolbar.ts | 4 +- src/extensions/web-vitals/index.ts | 2 +- src/heatmaps.ts | 2 +- src/request.ts | 4 +- src/storage.ts | 20 +- src/types.ts | 2 +- src/utils/index.ts | 6 +- src/utils/request-utils.ts | 2 +- testcafe/check-testcafe-results.js | 67 ++ testcafe/e2e.spec.js | 109 ++- testcafe/helpers.js | 179 +++-- testcafe/tsconfig.json | 10 + 23 files changed, 802 insertions(+), 424 deletions(-) create mode 100644 scripts/run-testcafe-localhost.mjs delete mode 100644 scripts/run-testcafe-with-retries.mjs create mode 100644 testcafe/check-testcafe-results.js create mode 100644 testcafe/tsconfig.json diff --git a/.eslintrc.js b/.eslintrc.js index 46c7415e7..0b65965de 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -7,6 +7,7 @@ const rules = { '@typescript-eslint/no-this-alias': 'off', '@typescript-eslint/no-explicit-any': 'off', '@typescript-eslint/no-unused-vars': ['error'], + '@typescript-eslint/no-unused-expressions': 'off', 'no-prototype-builtins': 'off', 'no-empty': 'off', 'no-console': 'error', @@ -101,6 +102,12 @@ module.exports = { Cypress: true, }, }, + { + files: 'testcafe/**/*', + globals: { + __dirname: true, + }, + }, ], root: true, } diff --git a/.github/workflows/testcafe.yml b/.github/workflows/testcafe.yml index e0bac07c6..fb90e63c8 100644 --- a/.github/workflows/testcafe.yml +++ b/.github/workflows/testcafe.yml @@ -54,4 +54,11 @@ jobs: run: pnpm build-rollup - name: Run ${{ matrix.name }} test - run: node scripts/run-testcafe-with-retries.mjs --browser "${{ matrix.browser }}" --attempts 3 + env: + BRANCH_NAME: ${{ github.head_ref || github.ref_name }} + RUN_ID: ${{ github.run_id }} + BROWSER: ${{ matrix.browser }} + run: pnpm testcafe ${{ matrix.browser }} --stop-on-first-fail + + - name: Check ${{ matrix.name }} events + run: pnpm check-testcafe-results diff --git a/.testcaferc.js b/.testcaferc.js index c8e6ff7ca..de784d9d0 100644 --- a/.testcaferc.js +++ b/.testcaferc.js @@ -1,3 +1,8 @@ module.exports = { - src: './testcafe', + src: './testcafe/*.spec.js', + compilerOptions: { + typescript: { + customCompilerModulePath: require.resolve('typescript'), + }, + }, } diff --git a/package.json b/package.json index 7b15c3e69..c01b8e1d1 100644 --- a/package.json +++ b/package.json @@ -23,7 +23,9 @@ "typecheck": "tsc --noEmit --project tsconfig.json", "cypress": "cypress open", "prepare": "husky install", - "deprecate-old-versions": "node scripts/deprecate-old-versions.mjs" + "deprecate-old-versions": "node scripts/deprecate-old-versions.mjs", + "check-testcafe-results": "ts-node testcafe/check-testcafe-results.js", + "run-testcafe-localhost": "node scripts/run-testcafe-localhost.mjs" }, "main": "dist/main.js", "module": "dist/module.js", @@ -59,29 +61,31 @@ "@testing-library/preact": "^3.2.4", "@types/eslint": "^8.44.6", "@types/jest": "^29.5.1", + "@types/node": "^22.5.0", "@types/react-dom": "^18.0.10", "@types/sinon": "^17.0.1", "@types/uuid": "^9.0.1", "@types/web": "^0.0.154", - "@typescript-eslint/eslint-plugin": "^6.19.0", - "@typescript-eslint/parser": "^6.19.0", + "@typescript-eslint/eslint-plugin": "^8.2.0", + "@typescript-eslint/parser": "^8.2.0", "babel-eslint": "10.1.0", "babel-jest": "^26.6.3", "compare-versions": "^6.1.0", "cypress": "13.6.3", "cypress-localstorage-commands": "^2.2.6", "date-fns": "^3.6.0", - "eslint": "8.56.0", + "eslint": "8.57.0", "eslint-config-posthog-js": "link:eslint-rules", "eslint-config-prettier": "^8.5.0", "eslint-plugin-compat": "^4.1.4", - "eslint-plugin-jest": "^27.2.3", + "eslint-plugin-jest": "^28.8.0", "eslint-plugin-no-only-tests": "^3.1.0", "eslint-plugin-posthog-js": "link:eslint-rules", "eslint-plugin-prettier": "^4.2.1", "eslint-plugin-react": "^7.30.1", "eslint-plugin-react-hooks": "^4.6.0", - "express": "^4.18.2", + "expect": "^29.7.0", + "express": "^4.19.2", "fast-check": "^2.17.0", "husky": "^8.0.1", "jest": "^27.5.1", @@ -89,7 +93,7 @@ "jsdom-global": "3.0.2", "lint-staged": "^10.2.11", "localStorage": "1.0.4", - "msw": "^1.2.1", + "msw": "^1.3.3", "node-fetch": "^2.6.11", "posthog-js": "link:", "preact-render-to-string": "^6.3.1", @@ -103,7 +107,8 @@ "testcafe": "1.19.0", "testcafe-browser-provider-browserstack": "1.14.0", "tslib": "^2.5.0", - "typescript": "^4.9.5", + "ts-node": "^10.9.2", + "typescript": "^5.5.4", "yargs": "^17.7.2" }, "lint-staged": { diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 9384cf9f7..ab3c1511b 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -56,7 +56,7 @@ devDependencies: version: 0.4.4(rollup@4.9.6) '@rollup/plugin-typescript': specifier: ^11.1.6 - version: 11.1.6(rollup@4.9.6)(tslib@2.5.0)(typescript@4.9.5) + version: 11.1.6(rollup@4.9.6)(tslib@2.5.0)(typescript@5.5.4) '@rrweb/types': specifier: 2.0.0-alpha.13 version: 2.0.0-alpha.13 @@ -78,6 +78,9 @@ devDependencies: '@types/jest': specifier: ^29.5.1 version: 29.5.1 + '@types/node': + specifier: ^22.5.0 + version: 22.5.0 '@types/react-dom': specifier: ^18.0.10 version: 18.0.10 @@ -91,14 +94,14 @@ devDependencies: specifier: ^0.0.154 version: 0.0.154 '@typescript-eslint/eslint-plugin': - specifier: ^6.19.0 - version: 6.19.0(@typescript-eslint/parser@6.19.0)(eslint@8.56.0)(typescript@4.9.5) + specifier: ^8.2.0 + version: 8.2.0(@typescript-eslint/parser@8.2.0)(eslint@8.57.0)(typescript@5.5.4) '@typescript-eslint/parser': - specifier: ^6.19.0 - version: 6.19.0(eslint@8.56.0)(typescript@4.9.5) + specifier: ^8.2.0 + version: 8.2.0(eslint@8.57.0)(typescript@5.5.4) babel-eslint: specifier: 10.1.0 - version: 10.1.0(eslint@8.56.0) + version: 10.1.0(eslint@8.57.0) babel-jest: specifier: ^26.6.3 version: 26.6.3(@babel/core@7.18.9) @@ -115,20 +118,20 @@ devDependencies: specifier: ^3.6.0 version: 3.6.0 eslint: - specifier: 8.56.0 - version: 8.56.0 + specifier: 8.57.0 + version: 8.57.0 eslint-config-posthog-js: specifier: link:eslint-rules version: link:eslint-rules eslint-config-prettier: specifier: ^8.5.0 - version: 8.5.0(eslint@8.56.0) + version: 8.5.0(eslint@8.57.0) eslint-plugin-compat: specifier: ^4.1.4 - version: 4.1.4(eslint@8.56.0) + version: 4.1.4(eslint@8.57.0) eslint-plugin-jest: - specifier: ^27.2.3 - version: 27.2.3(@typescript-eslint/eslint-plugin@6.19.0)(eslint@8.56.0)(jest@27.5.1)(typescript@4.9.5) + specifier: ^28.8.0 + version: 28.8.0(@typescript-eslint/eslint-plugin@8.2.0)(eslint@8.57.0)(jest@27.5.1)(typescript@5.5.4) eslint-plugin-no-only-tests: specifier: ^3.1.0 version: 3.1.0 @@ -137,16 +140,19 @@ devDependencies: version: link:eslint-rules eslint-plugin-prettier: specifier: ^4.2.1 - version: 4.2.1(eslint-config-prettier@8.5.0)(eslint@8.56.0)(prettier@2.7.1) + version: 4.2.1(eslint-config-prettier@8.5.0)(eslint@8.57.0)(prettier@2.7.1) eslint-plugin-react: specifier: ^7.30.1 - version: 7.30.1(eslint@8.56.0) + version: 7.30.1(eslint@8.57.0) eslint-plugin-react-hooks: specifier: ^4.6.0 - version: 4.6.0(eslint@8.56.0) + version: 4.6.0(eslint@8.57.0) + expect: + specifier: ^29.7.0 + version: 29.7.0 express: - specifier: ^4.18.2 - version: 4.18.2 + specifier: ^4.19.2 + version: 4.19.2 fast-check: specifier: ^2.17.0 version: 2.17.0 @@ -155,7 +161,7 @@ devDependencies: version: 8.0.1 jest: specifier: ^27.5.1 - version: 27.5.1 + version: 27.5.1(ts-node@10.9.2) jsdom: specifier: 16.5.0 version: 16.5.0 @@ -169,8 +175,8 @@ devDependencies: specifier: 1.0.4 version: 1.0.4 msw: - specifier: ^1.2.1 - version: 1.2.1(typescript@4.9.5) + specifier: ^1.3.3 + version: 1.3.3(typescript@5.5.4) node-fetch: specifier: ^2.6.11 version: 2.6.11 @@ -188,7 +194,7 @@ devDependencies: version: 4.9.6 rollup-plugin-dts: specifier: ^6.1.0 - version: 6.1.0(rollup@4.9.6)(typescript@4.9.5) + version: 6.1.0(rollup@4.9.6)(typescript@5.5.4) rollup-plugin-visualizer: specifier: ^5.12.0 version: 5.12.0(rollup@4.9.6) @@ -207,12 +213,15 @@ devDependencies: testcafe-browser-provider-browserstack: specifier: 1.14.0 version: 1.14.0 + ts-node: + specifier: ^10.9.2 + version: 10.9.2(@types/node@22.5.0)(typescript@5.5.4) tslib: specifier: ^2.5.0 version: 2.5.0 typescript: - specifier: ^4.9.5 - version: 4.9.5 + specifier: ^5.5.4 + version: 5.5.4 yargs: specifier: ^17.7.2 version: 17.7.2 @@ -1638,6 +1647,13 @@ packages: dev: true optional: true + /@cspotcode/source-map-support@0.8.1: + resolution: {integrity: sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==} + engines: {node: '>=12'} + dependencies: + '@jridgewell/trace-mapping': 0.3.9 + dev: true + /@cypress/request@3.0.1: resolution: {integrity: sha512-TWivJlJi8ZDx2wGOw1dbLuHJKUYX7bWySw377nlnGOW3hP9/MUKIsEdXT/YngWxVdgNCHRBmFlBipE+5/2ZZlQ==} engines: {node: '>= 6'} @@ -1675,13 +1691,13 @@ packages: - supports-color dev: true - /@eslint-community/eslint-utils@4.4.0(eslint@8.56.0): + /@eslint-community/eslint-utils@4.4.0(eslint@8.57.0): resolution: {integrity: sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} peerDependencies: eslint: ^6.0.0 || ^7.0.0 || >=8.0.0 dependencies: - eslint: 8.56.0 + eslint: 8.57.0 eslint-visitor-keys: 3.4.3 dev: true @@ -1707,14 +1723,15 @@ packages: - supports-color dev: true - /@eslint/js@8.56.0: - resolution: {integrity: sha512-gMsVel9D7f2HLkBma9VbtzZRehRogVRfbr++f06nL2vnCGCNlzOD+/MUov/F4p8myyAHspEhVobgjpX64q5m6A==} + /@eslint/js@8.57.0: + resolution: {integrity: sha512-Ys+3g2TaW7gADOJzPt83SJtCDhMjndcDMFVQ/Tj9iA1BfJzFKD9mAUXT3OenpuPHbI6P/myECxRJrofUsDx/5g==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} dev: true /@humanwhocodes/config-array@0.11.14: resolution: {integrity: sha512-3T8LkOmg45BV5FICb15QQMsyUSWrQ8AygVfC7ZG32zOalnqrilm018ZVCw0eapXux8FtA33q8PSRSstjee3jSg==} engines: {node: '>=10.10.0'} + deprecated: Use @eslint/config-array instead dependencies: '@humanwhocodes/object-schema': 2.0.2 debug: 4.3.4(supports-color@8.1.1) @@ -1730,6 +1747,7 @@ packages: /@humanwhocodes/object-schema@2.0.2: resolution: {integrity: sha512-6EwiSjwWYP7pTckG6I5eyFANjPhmPjUX9JRLUSfNPC7FX7zK9gyZAfUEaECL6ALTpGX5AjnBq3C9XmVWPitNpw==} + deprecated: Use @eslint/object-schema instead dev: true /@istanbuljs/load-nyc-config@1.1.0: @@ -1753,14 +1771,14 @@ packages: engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0} dependencies: '@jest/types': 27.5.1 - '@types/node': 14.0.27 + '@types/node': 22.5.0 chalk: 4.1.2 jest-message-util: 27.5.1 jest-util: 27.5.1 slash: 3.0.0 dev: true - /@jest/core@27.5.1: + /@jest/core@27.5.1(ts-node@10.9.2): resolution: {integrity: sha512-AK6/UTrvQD0Cd24NSqmIA6rKsu0tKIxfiCducZvqxYdmMisOYAsdItspT+fQDQYARPf8XgjAFZi0ogW2agH5nQ==} engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0} peerDependencies: @@ -1774,14 +1792,14 @@ packages: '@jest/test-result': 27.5.1 '@jest/transform': 27.5.1 '@jest/types': 27.5.1 - '@types/node': 14.0.27 + '@types/node': 22.5.0 ansi-escapes: 4.3.2 chalk: 4.1.2 emittery: 0.8.1 exit: 0.1.2 graceful-fs: 4.2.9 jest-changed-files: 27.5.1 - jest-config: 27.5.1 + jest-config: 27.5.1(ts-node@10.9.2) jest-haste-map: 27.5.1 jest-message-util: 27.5.1 jest-regex-util: 27.5.1 @@ -1811,15 +1829,15 @@ packages: dependencies: '@jest/fake-timers': 27.5.1 '@jest/types': 27.5.1 - '@types/node': 14.0.27 + '@types/node': 22.5.0 jest-mock: 27.5.1 dev: true - /@jest/expect-utils@29.5.0: - resolution: {integrity: sha512-fmKzsidoXQT2KwnrwE0SQq3uj8Z763vzR8LnLBwC2qYWEFpjX8daRsk6rHUM1QvNlEW/UJXNXm59ztmJJWs2Mg==} + /@jest/expect-utils@29.7.0: + resolution: {integrity: sha512-GlsNBWiFQFCVi9QVSx7f5AgMeLxe9YCCs5PuP2O2LdjDAA8Jh9eX7lA1Jq/xdXw3Wb3hyvlFNfZIfcRetSzYcA==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} dependencies: - jest-get-type: 29.4.3 + jest-get-type: 29.6.3 dev: true /@jest/fake-timers@27.5.1: @@ -1828,7 +1846,7 @@ packages: dependencies: '@jest/types': 27.5.1 '@sinonjs/fake-timers': 8.1.0 - '@types/node': 14.0.27 + '@types/node': 22.5.0 jest-message-util: 27.5.1 jest-mock: 27.5.1 jest-util: 27.5.1 @@ -1857,7 +1875,7 @@ packages: '@jest/test-result': 27.5.1 '@jest/transform': 27.5.1 '@jest/types': 27.5.1 - '@types/node': 14.0.27 + '@types/node': 22.5.0 chalk: 4.1.2 collect-v8-coverage: 1.0.1 exit: 0.1.2 @@ -1888,6 +1906,13 @@ packages: '@sinclair/typebox': 0.25.24 dev: true + /@jest/schemas@29.6.3: + resolution: {integrity: sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + '@sinclair/typebox': 0.27.8 + dev: true + /@jest/source-map@27.5.1: resolution: {integrity: sha512-y9NIHUYF3PJRlHk98NdC/N1gl88BL08aQQgu4k4ZopQkCw9t9cV8mtl3TV8b/YCB8XaVTFrmUTAJvjsntDireg==} engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0} @@ -1971,7 +1996,7 @@ packages: dependencies: '@types/istanbul-lib-coverage': 2.0.3 '@types/istanbul-reports': 3.0.0 - '@types/node': 14.0.27 + '@types/node': 22.5.0 '@types/yargs': 15.0.5 chalk: 4.1.2 dev: true @@ -1982,19 +2007,19 @@ packages: dependencies: '@types/istanbul-lib-coverage': 2.0.3 '@types/istanbul-reports': 3.0.0 - '@types/node': 14.0.27 + '@types/node': 22.5.0 '@types/yargs': 16.0.4 chalk: 4.1.2 dev: true - /@jest/types@29.5.0: - resolution: {integrity: sha512-qbu7kN6czmVRc3xWFQcAN03RAUamgppVUdXrvl1Wr3jlNF93o9mJbGcDWrwGB6ht44u7efB1qCFgVQmca24Uog==} + /@jest/types@29.6.3: + resolution: {integrity: sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} dependencies: - '@jest/schemas': 29.4.3 + '@jest/schemas': 29.6.3 '@types/istanbul-lib-coverage': 2.0.3 '@types/istanbul-reports': 3.0.0 - '@types/node': 14.0.27 + '@types/node': 22.5.0 '@types/yargs': 17.0.24 chalk: 4.1.2 dev: true @@ -2419,6 +2444,13 @@ packages: '@jridgewell/sourcemap-codec': 1.4.15 dev: true + /@jridgewell/trace-mapping@0.3.9: + resolution: {integrity: sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==} + dependencies: + '@jridgewell/resolve-uri': 3.1.1 + '@jridgewell/sourcemap-codec': 1.4.15 + dev: true + /@mdn/browser-compat-data@5.2.61: resolution: {integrity: sha512-jzPrheqEtrnUWWNUS8SFVbQAnoO6rOXetkjiJyzP92UM+BNcyExLD0Qikv9z6TU9D6A9rbXkh3pSmZ5G88d6ew==} dev: true @@ -2436,15 +2468,15 @@ packages: set-cookie-parser: 2.6.0 dev: true - /@mswjs/interceptors@0.17.9: - resolution: {integrity: sha512-4LVGt03RobMH/7ZrbHqRxQrS9cc2uh+iNKSj8UWr8M26A2i793ju+csaB5zaqYltqJmA2jUq4VeYfKmVqvsXQg==} + /@mswjs/interceptors@0.17.10: + resolution: {integrity: sha512-N8x7eSLGcmUFNWZRxT1vsHvypzIRgQYdG0rJey/rZCy6zT/30qDt8Joj7FxzGNLSwXbeZqJOMqDurp7ra4hgbw==} engines: {node: '>=14'} dependencies: '@open-draft/until': 1.0.3 '@types/debug': 4.1.7 '@xmldom/xmldom': 0.8.7 debug: 4.3.4(supports-color@8.1.1) - headers-polyfill: 3.1.2 + headers-polyfill: 3.2.5 outvariant: 1.4.0 strict-event-emitter: 0.2.8 web-encoding: 1.1.5 @@ -2542,7 +2574,7 @@ packages: terser: 5.27.0 dev: true - /@rollup/plugin-typescript@11.1.6(rollup@4.9.6)(tslib@2.5.0)(typescript@4.9.5): + /@rollup/plugin-typescript@11.1.6(rollup@4.9.6)(tslib@2.5.0)(typescript@5.5.4): resolution: {integrity: sha512-R92yOmIACgYdJ7dJ97p4K69I8gg6IEHt8M7dUBxN3W6nrO8uUxX5ixl0yU/N3aZTi8WhPuICvOHXQvF6FaykAA==} engines: {node: '>=14.0.0'} peerDependencies: @@ -2559,7 +2591,7 @@ packages: resolve: 1.22.8 rollup: 4.9.6 tslib: 2.5.0 - typescript: 4.9.5 + typescript: 5.5.4 dev: true /@rollup/pluginutils@5.1.0(rollup@4.9.6): @@ -2696,6 +2728,10 @@ packages: resolution: {integrity: sha512-XJfwUVUKDHF5ugKwIcxEgc9k8b7HbznCp6eUfWgu710hMPNIO4aw4/zB5RogDQz8nd6gyCDpU9O/m6qYEWY6yQ==} dev: true + /@sinclair/typebox@0.27.8: + resolution: {integrity: sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA==} + dev: true + /@sinonjs/commons@1.8.1: resolution: {integrity: sha512-892K+kWUUi3cl+LlqEWIDrhvLgdL79tECi8JZUyq6IviKy/DNhuzCRlbHUjxK89f4ypPMMaFnFuR9Ie6DoIMsw==} dependencies: @@ -2790,7 +2826,7 @@ packages: chalk: 3.0.0 css.escape: 1.5.1 dom-accessibility-api: 0.6.3 - jest: 27.5.1 + jest: 27.5.1(ts-node@10.9.2) lodash: 4.17.21 redent: 3.0.0 dev: true @@ -2810,10 +2846,22 @@ packages: engines: {node: '>= 6'} dev: true + /@tsconfig/node10@1.0.11: + resolution: {integrity: sha512-DcRjDCujK/kCk/cUe8Xz8ZSpm8mS3mNNpta+jGCA6USEDfktlNvm1+IuZ9eTcDbNk41BHwpHHeW+N1lKCz4zOw==} + dev: true + + /@tsconfig/node12@1.0.11: + resolution: {integrity: sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==} + dev: true + /@tsconfig/node14@1.0.3: resolution: {integrity: sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==} dev: true + /@tsconfig/node16@1.0.4: + resolution: {integrity: sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==} + dev: true + /@types/aria-query@5.0.1: resolution: {integrity: sha512-XTIieEY+gvJ39ChLcB4If5zHtPxt3Syj5rgZR+e1ctpmK8NjPf0zFqsz4JpLJT0xla9GFDKjy8Cpu331nrmE1Q==} dev: true @@ -2888,7 +2936,7 @@ packages: resolution: {integrity: sha512-SEYeGAIQIQX8NN6LDKprLjbrd5dARM5EXsd8GI/A5l0apYI1fGMWgPHSe4ZKL4eozlAyI+doUE9XbYS4xCkQ1w==} dependencies: '@types/minimatch': 3.0.3 - '@types/node': 14.0.27 + '@types/node': 22.5.0 dev: true /@types/glob@7.2.0: @@ -2896,14 +2944,14 @@ packages: requiresBuild: true dependencies: '@types/minimatch': 3.0.3 - '@types/node': 14.0.27 + '@types/node': 22.5.0 dev: true optional: true /@types/graceful-fs@4.1.3: resolution: {integrity: sha512-AiHRaEB50LQg0pZmm659vNBb9f4SJ0qrAnteuzhSeAUcJKxoYgEnprg/83kppCnc2zvtCKbdZry1a5pVY3lOTQ==} dependencies: - '@types/node': 14.0.27 + '@types/node': 22.5.0 dev: true /@types/istanbul-lib-coverage@2.0.3: @@ -2925,7 +2973,7 @@ packages: /@types/jest@29.5.1: resolution: {integrity: sha512-tEuVcHrpaixS36w7hpsfLBLpjtMRJUE09/MHXn923LOVojDwyC14cWcfc0rDs0VEfUyYmt/+iX1kxxp+gZMcaQ==} dependencies: - expect: 29.5.0 + expect: 29.7.0 pretty-format: 29.5.0 dev: true @@ -2954,8 +3002,10 @@ packages: resolution: {integrity: sha512-i1KGxqcvJaLQali+WuypQnXwcplhtNtjs66eNsZpp2P2FL/trJJxx/VWsM0YCL2iMoIJrbXje48lvIQAQ4p2ZA==} dev: true - /@types/node@14.0.27: - resolution: {integrity: sha512-kVrqXhbclHNHGu9ztnAwSncIgJv/FaxmzXJvGXNdcCpV1b8u1/Mi6z6m0vwy0LzKeXFTPLH0NzwmoJ3fNCIq0g==} + /@types/node@22.5.0: + resolution: {integrity: sha512-DkFrJOe+rfdHTqqMg0bSNlGlQ85hSoh2TPzZyhHsXnMtligRWpxUySiyw8FY14ITt24HVCiQPWxS3KO/QlGmWg==} + dependencies: + undici-types: 6.19.8 dev: true /@types/parse-json@4.0.0: @@ -2999,7 +3049,7 @@ packages: /@types/set-cookie-parser@2.4.2: resolution: {integrity: sha512-fBZgytwhYAUkj/jC/FAV4RQ5EerRup1YQsXQCh8rZfiHkc4UahC192oH0smGwsXol3cL3A5oETuAHeQHmhXM4w==} dependencies: - '@types/node': 14.0.27 + '@types/node': 22.5.0 dev: true /@types/sinon@17.0.1: @@ -3058,68 +3108,58 @@ packages: resolution: {integrity: sha512-oJoftv0LSuaDZE3Le4DbKX+KS9G36NzOeSap90UIK0yMA/NhKJhqlSGtNDORNRaIbQfzjXDrQa0ytJ6mNRGz/Q==} requiresBuild: true dependencies: - '@types/node': 14.0.27 + '@types/node': 22.5.0 dev: true optional: true - /@typescript-eslint/eslint-plugin@6.19.0(@typescript-eslint/parser@6.19.0)(eslint@8.56.0)(typescript@4.9.5): - resolution: {integrity: sha512-DUCUkQNklCQYnrBSSikjVChdc84/vMPDQSgJTHBZ64G9bA9w0Crc0rd2diujKbTdp6w2J47qkeHQLoi0rpLCdg==} - engines: {node: ^16.0.0 || >=18.0.0} + /@typescript-eslint/eslint-plugin@8.2.0(@typescript-eslint/parser@8.2.0)(eslint@8.57.0)(typescript@5.5.4): + resolution: {integrity: sha512-02tJIs655em7fvt9gps/+4k4OsKULYGtLBPJfOsmOq1+3cdClYiF0+d6mHu6qDnTcg88wJBkcPLpQhq7FyDz0A==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: - '@typescript-eslint/parser': ^6.0.0 || ^6.0.0-alpha - eslint: ^7.0.0 || ^8.0.0 + '@typescript-eslint/parser': ^8.0.0 || ^8.0.0-alpha.0 + eslint: ^8.57.0 || ^9.0.0 typescript: '*' peerDependenciesMeta: typescript: optional: true dependencies: '@eslint-community/regexpp': 4.10.0 - '@typescript-eslint/parser': 6.19.0(eslint@8.56.0)(typescript@4.9.5) - '@typescript-eslint/scope-manager': 6.19.0 - '@typescript-eslint/type-utils': 6.19.0(eslint@8.56.0)(typescript@4.9.5) - '@typescript-eslint/utils': 6.19.0(eslint@8.56.0)(typescript@4.9.5) - '@typescript-eslint/visitor-keys': 6.19.0 - debug: 4.3.4(supports-color@8.1.1) - eslint: 8.56.0 + '@typescript-eslint/parser': 8.2.0(eslint@8.57.0)(typescript@5.5.4) + '@typescript-eslint/scope-manager': 8.2.0 + '@typescript-eslint/type-utils': 8.2.0(eslint@8.57.0)(typescript@5.5.4) + '@typescript-eslint/utils': 8.2.0(eslint@8.57.0)(typescript@5.5.4) + '@typescript-eslint/visitor-keys': 8.2.0 + eslint: 8.57.0 graphemer: 1.4.0 - ignore: 5.2.4 + ignore: 5.3.2 natural-compare: 1.4.0 - semver: 7.5.4 - ts-api-utils: 1.0.1(typescript@4.9.5) - typescript: 4.9.5 + ts-api-utils: 1.3.0(typescript@5.5.4) + typescript: 5.5.4 transitivePeerDependencies: - supports-color dev: true - /@typescript-eslint/parser@6.19.0(eslint@8.56.0)(typescript@4.9.5): - resolution: {integrity: sha512-1DyBLG5SH7PYCd00QlroiW60YJ4rWMuUGa/JBV0iZuqi4l4IK3twKPq5ZkEebmGqRjXWVgsUzfd3+nZveewgow==} - engines: {node: ^16.0.0 || >=18.0.0} + /@typescript-eslint/parser@8.2.0(eslint@8.57.0)(typescript@5.5.4): + resolution: {integrity: sha512-j3Di+o0lHgPrb7FxL3fdEy6LJ/j2NE8u+AP/5cQ9SKb+JLH6V6UHDqJ+e0hXBkHP1wn1YDFjYCS9LBQsZDlDEg==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: - eslint: ^7.0.0 || ^8.0.0 + eslint: ^8.57.0 || ^9.0.0 typescript: '*' peerDependenciesMeta: typescript: optional: true dependencies: - '@typescript-eslint/scope-manager': 6.19.0 - '@typescript-eslint/types': 6.19.0 - '@typescript-eslint/typescript-estree': 6.19.0(typescript@4.9.5) - '@typescript-eslint/visitor-keys': 6.19.0 + '@typescript-eslint/scope-manager': 8.2.0 + '@typescript-eslint/types': 8.2.0 + '@typescript-eslint/typescript-estree': 8.2.0(typescript@5.5.4) + '@typescript-eslint/visitor-keys': 8.2.0 debug: 4.3.4(supports-color@8.1.1) - eslint: 8.56.0 - typescript: 4.9.5 + eslint: 8.57.0 + typescript: 5.5.4 transitivePeerDependencies: - supports-color dev: true - /@typescript-eslint/scope-manager@5.62.0: - resolution: {integrity: sha512-VXuvVvZeQCQb5Zgf4HAxc04q5j+WrNAtNh9OwCsCgpKqESMTu3tF/jhZ3xG6T4NZwWl65Bg8KuS2uEvhSfLl0w==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} - dependencies: - '@typescript-eslint/types': 5.62.0 - '@typescript-eslint/visitor-keys': 5.62.0 - dev: true - /@typescript-eslint/scope-manager@6.19.0: resolution: {integrity: sha512-dO1XMhV2ehBI6QN8Ufi7I10wmUovmLU0Oru3n5LVlM2JuzB4M+dVphCPLkVpKvGij2j/pHBWuJ9piuXx+BhzxQ==} engines: {node: ^16.0.0 || >=18.0.0} @@ -3128,58 +3168,44 @@ packages: '@typescript-eslint/visitor-keys': 6.19.0 dev: true - /@typescript-eslint/type-utils@6.19.0(eslint@8.56.0)(typescript@4.9.5): - resolution: {integrity: sha512-mcvS6WSWbjiSxKCwBcXtOM5pRkPQ6kcDds/juxcy/727IQr3xMEcwr/YLHW2A2+Fp5ql6khjbKBzOyjuPqGi/w==} - engines: {node: ^16.0.0 || >=18.0.0} + /@typescript-eslint/scope-manager@8.2.0: + resolution: {integrity: sha512-OFn80B38yD6WwpoHU2Tz/fTz7CgFqInllBoC3WP+/jLbTb4gGPTy9HBSTsbDWkMdN55XlVU0mMDYAtgvlUspGw==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + dependencies: + '@typescript-eslint/types': 8.2.0 + '@typescript-eslint/visitor-keys': 8.2.0 + dev: true + + /@typescript-eslint/type-utils@8.2.0(eslint@8.57.0)(typescript@5.5.4): + resolution: {integrity: sha512-g1CfXGFMQdT5S+0PSO0fvGXUaiSkl73U1n9LTK5aRAFnPlJ8dLKkXr4AaLFvPedW8lVDoMgLLE3JN98ZZfsj0w==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: - eslint: ^7.0.0 || ^8.0.0 typescript: '*' peerDependenciesMeta: typescript: optional: true dependencies: - '@typescript-eslint/typescript-estree': 6.19.0(typescript@4.9.5) - '@typescript-eslint/utils': 6.19.0(eslint@8.56.0)(typescript@4.9.5) + '@typescript-eslint/typescript-estree': 8.2.0(typescript@5.5.4) + '@typescript-eslint/utils': 8.2.0(eslint@8.57.0)(typescript@5.5.4) debug: 4.3.4(supports-color@8.1.1) - eslint: 8.56.0 - ts-api-utils: 1.0.1(typescript@4.9.5) - typescript: 4.9.5 + ts-api-utils: 1.3.0(typescript@5.5.4) + typescript: 5.5.4 transitivePeerDependencies: + - eslint - supports-color dev: true - /@typescript-eslint/types@5.62.0: - resolution: {integrity: sha512-87NVngcbVXUahrRTqIK27gD2t5Cu1yuCXxbLcFtCzZGlfyVWWh8mLHkoxzjsB6DDNnvdL+fW8MiwPEJyGJQDgQ==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} - dev: true - /@typescript-eslint/types@6.19.0: resolution: {integrity: sha512-lFviGV/vYhOy3m8BJ/nAKoAyNhInTdXpftonhWle66XHAtT1ouBlkjL496b5H5hb8dWXHwtypTqgtb/DEa+j5A==} engines: {node: ^16.0.0 || >=18.0.0} dev: true - /@typescript-eslint/typescript-estree@5.62.0(typescript@4.9.5): - resolution: {integrity: sha512-CmcQ6uY7b9y694lKdRB8FEel7JbU/40iSAPomu++SjLMntB+2Leay2LO6i8VnJk58MtE9/nQSFIH6jpyRWyYzA==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} - peerDependencies: - typescript: '*' - peerDependenciesMeta: - typescript: - optional: true - dependencies: - '@typescript-eslint/types': 5.62.0 - '@typescript-eslint/visitor-keys': 5.62.0 - debug: 4.3.4(supports-color@8.1.1) - globby: 11.1.0 - is-glob: 4.0.3 - semver: 7.5.4 - tsutils: 3.21.0(typescript@4.9.5) - typescript: 4.9.5 - transitivePeerDependencies: - - supports-color + /@typescript-eslint/types@8.2.0: + resolution: {integrity: sha512-6a9QSK396YqmiBKPkJtxsgZZZVjYQ6wQ/TlI0C65z7vInaETuC6HAHD98AGLC8DyIPqHytvNuS8bBVvNLKyqvQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} dev: true - /@typescript-eslint/typescript-estree@6.19.0(typescript@4.9.5): + /@typescript-eslint/typescript-estree@6.19.0(typescript@5.5.4): resolution: {integrity: sha512-o/zefXIbbLBZ8YJ51NlkSAt2BamrK6XOmuxSR3hynMIzzyMY33KuJ9vuMdFSXW+H0tVvdF9qBPTHA91HDb4BIQ==} engines: {node: ^16.0.0 || >=18.0.0} peerDependencies: @@ -3195,57 +3221,67 @@ packages: is-glob: 4.0.3 minimatch: 9.0.3 semver: 7.5.4 - ts-api-utils: 1.0.1(typescript@4.9.5) - typescript: 4.9.5 + ts-api-utils: 1.0.1(typescript@5.5.4) + typescript: 5.5.4 transitivePeerDependencies: - supports-color dev: true - /@typescript-eslint/utils@5.62.0(eslint@8.56.0)(typescript@4.9.5): - resolution: {integrity: sha512-n8oxjeb5aIbPFEtmQxQYOLI0i9n5ySBEY/ZEHHZqKQSFnxio1rv6dthascc9dLuwrL0RC5mPCxB7vnAVGAYWAQ==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + /@typescript-eslint/typescript-estree@8.2.0(typescript@5.5.4): + resolution: {integrity: sha512-kiG4EDUT4dImplOsbh47B1QnNmXSoUqOjWDvCJw/o8LgfD0yr7k2uy54D5Wm0j4t71Ge1NkynGhpWdS0dEIAUA==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: - eslint: ^6.0.0 || ^7.0.0 || ^8.0.0 + typescript: '*' + peerDependenciesMeta: + typescript: + optional: true dependencies: - '@eslint-community/eslint-utils': 4.4.0(eslint@8.56.0) - '@types/json-schema': 7.0.14 - '@types/semver': 7.5.0 - '@typescript-eslint/scope-manager': 5.62.0 - '@typescript-eslint/types': 5.62.0 - '@typescript-eslint/typescript-estree': 5.62.0(typescript@4.9.5) - eslint: 8.56.0 - eslint-scope: 5.1.1 - semver: 7.5.4 + '@typescript-eslint/types': 8.2.0 + '@typescript-eslint/visitor-keys': 8.2.0 + debug: 4.3.4(supports-color@8.1.1) + globby: 11.1.0 + is-glob: 4.0.3 + minimatch: 9.0.5 + semver: 7.6.3 + ts-api-utils: 1.3.0(typescript@5.5.4) + typescript: 5.5.4 transitivePeerDependencies: - supports-color - - typescript dev: true - /@typescript-eslint/utils@6.19.0(eslint@8.56.0)(typescript@4.9.5): + /@typescript-eslint/utils@6.19.0(eslint@8.57.0)(typescript@5.5.4): resolution: {integrity: sha512-QR41YXySiuN++/dC9UArYOg4X86OAYP83OWTewpVx5ct1IZhjjgTLocj7QNxGhWoTqknsgpl7L+hGygCO+sdYw==} engines: {node: ^16.0.0 || >=18.0.0} peerDependencies: eslint: ^7.0.0 || ^8.0.0 dependencies: - '@eslint-community/eslint-utils': 4.4.0(eslint@8.56.0) + '@eslint-community/eslint-utils': 4.4.0(eslint@8.57.0) '@types/json-schema': 7.0.14 '@types/semver': 7.5.0 '@typescript-eslint/scope-manager': 6.19.0 '@typescript-eslint/types': 6.19.0 - '@typescript-eslint/typescript-estree': 6.19.0(typescript@4.9.5) - eslint: 8.56.0 + '@typescript-eslint/typescript-estree': 6.19.0(typescript@5.5.4) + eslint: 8.57.0 semver: 7.5.4 transitivePeerDependencies: - supports-color - typescript dev: true - /@typescript-eslint/visitor-keys@5.62.0: - resolution: {integrity: sha512-07ny+LHRzQXepkGg6w0mFY41fVUNBrL2Roj/++7V1txKugfjm/Ci/qSND03r2RhlJhJYMcTn9AhhSSqQp0Ysyw==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + /@typescript-eslint/utils@8.2.0(eslint@8.57.0)(typescript@5.5.4): + resolution: {integrity: sha512-O46eaYKDlV3TvAVDNcoDzd5N550ckSe8G4phko++OCSC1dYIb9LTc3HDGYdWqWIAT5qDUKphO6sd9RrpIJJPfg==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + eslint: ^8.57.0 || ^9.0.0 dependencies: - '@typescript-eslint/types': 5.62.0 - eslint-visitor-keys: 3.4.3 + '@eslint-community/eslint-utils': 4.4.0(eslint@8.57.0) + '@typescript-eslint/scope-manager': 8.2.0 + '@typescript-eslint/types': 8.2.0 + '@typescript-eslint/typescript-estree': 8.2.0(typescript@5.5.4) + eslint: 8.57.0 + transitivePeerDependencies: + - supports-color + - typescript dev: true /@typescript-eslint/visitor-keys@6.19.0: @@ -3256,6 +3292,14 @@ packages: eslint-visitor-keys: 3.4.3 dev: true + /@typescript-eslint/visitor-keys@8.2.0: + resolution: {integrity: sha512-sbgsPMW9yLvS7IhCi8IpuK1oBmtbWUNP+hBdwl/I9nzqVsszGnNGti5r9dUtF5RLivHUFFIdRvLiTsPhzSyJ3Q==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + dependencies: + '@typescript-eslint/types': 8.2.0 + eslint-visitor-keys: 3.4.3 + dev: true + /@ungap/structured-clone@1.2.0: resolution: {integrity: sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==} dev: true @@ -3314,6 +3358,13 @@ packages: engines: {node: '>=0.4.0'} dev: true + /acorn-walk@8.3.3: + resolution: {integrity: sha512-MxXdReSRhGO7VlFe1bRG/oI7/mdLV9B9JJT0N8vZOhF7gFRR5l3M8W9G8JxmKV+JC5mGqJ0QvqfSOLsCPa4nUw==} + engines: {node: '>=0.4.0'} + dependencies: + acorn: 8.11.3 + dev: true + /acorn@7.4.0: resolution: {integrity: sha512-+G7P8jJmCHr+S+cLfQxygbWhXy+8YTVGzAkpEbcLo2mLoL7tij/VG41QSHACSf5QgYRhMZYHuNc6drJaO0Da+w==} engines: {node: '>=0.4.0'} @@ -3425,6 +3476,10 @@ packages: resolution: {integrity: sha512-Of/R0wqp83cgHozfIYLbBMnej79U/SVGOOyuB3VVFv1NRM/PSFMK12x9KVtiYzJqmnU5WR2qp0Z5rHb7sWGnFQ==} dev: true + /arg@4.1.3: + resolution: {integrity: sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==} + dev: true + /argparse@1.0.10: resolution: {integrity: sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==} dependencies: @@ -3599,7 +3654,7 @@ packages: resolution: {integrity: sha512-3YDiu347mtVtjpyV3u5kVqQLP242c06zwDOgpeRnybmXlYYsLbtTrUBUm8i8srONt+FWobl5aibnU1030PeeuA==} dev: true - /babel-eslint@10.1.0(eslint@8.56.0): + /babel-eslint@10.1.0(eslint@8.57.0): resolution: {integrity: sha512-ifWaTHQ0ce+448CYop8AdrQiBsGrnC+bMgfyKFdi6EsPLTAWG+QfyDeM6OH+FmWnKvEq5NnBMLvlBUPKQZoDSg==} engines: {node: '>=6'} deprecated: babel-eslint is now @babel/eslint-parser. This package will no longer receive updates. @@ -3610,7 +3665,7 @@ packages: '@babel/parser': 7.23.0 '@babel/traverse': 7.23.2 '@babel/types': 7.23.6 - eslint: 8.56.0 + eslint: 8.57.0 eslint-visitor-keys: 1.3.0 resolve: 1.22.8 transitivePeerDependencies: @@ -3884,12 +3939,12 @@ packages: resolution: {integrity: sha512-vHdS19CnY3hwiNdkaqk93DvjVLfbEcI8mys4UjuWrlX1haDmroo8o4xCzh4wD6DGV6HxRCyauwhHRqMTfERtjw==} dev: true - /body-parser@1.20.1: - resolution: {integrity: sha512-jWi7abTbYwajOytWCQc37VulmWiRae5RyTpaCyDcS5/lMdtwSz5lOpDE67srw/HYe35f1z3fDQw+3txg7gNtWw==} + /body-parser@1.20.2: + resolution: {integrity: sha512-ml9pReCu3M61kGlqoTm2umSXTlRTuGTx0bfYj+uIUKKYycG5NtSbeetV3faSU6R7ajOPw0g/J1PvK4qNy7s5bA==} engines: {node: '>= 0.8', npm: 1.2.8000 || >= 1.4.16} dependencies: bytes: 3.1.2 - content-type: 1.0.4 + content-type: 1.0.5 debug: 2.6.9 depd: 2.0.0 destroy: 1.2.0 @@ -3897,7 +3952,7 @@ packages: iconv-lite: 0.4.24 on-finished: 2.4.1 qs: 6.11.0 - raw-body: 2.5.1 + raw-body: 2.5.2 type-is: 1.6.18 unpipe: 1.0.0 transitivePeerDependencies: @@ -4120,14 +4175,6 @@ packages: supports-color: 7.1.0 dev: true - /chalk@4.1.1: - resolution: {integrity: sha512-diHzdDKxcU+bAsUboHLPEDQiw0qEe0qd7SYUn3HgcFlWgbDcfLGswOHYeGrHKzG9z6UYf01d9VFMfZxPM1xZSg==} - engines: {node: '>=10'} - dependencies: - ansi-styles: 4.2.1 - supports-color: 7.1.0 - dev: true - /chalk@4.1.2: resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==} engines: {node: '>=10'} @@ -4380,6 +4427,11 @@ packages: engines: {node: '>= 0.6'} dev: true + /content-type@1.0.5: + resolution: {integrity: sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==} + engines: {node: '>= 0.6'} + dev: true + /convert-source-map@1.7.0: resolution: {integrity: sha512-4FJkXzKXEDB1snCFZlLP4gpC3JILicCpGbzG9f9G7tGqGCzETQ2hWPrcinA9oU4wtf2biUaEH5065UnMeR33oA==} dependencies: @@ -4395,8 +4447,8 @@ packages: engines: {node: '>= 0.6'} dev: true - /cookie@0.5.0: - resolution: {integrity: sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw==} + /cookie@0.6.0: + resolution: {integrity: sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw==} engines: {node: '>= 0.6'} dev: true @@ -4427,6 +4479,10 @@ packages: yaml: 1.10.0 dev: true + /create-require@1.1.1: + resolution: {integrity: sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==} + dev: true + /cross-spawn@6.0.5: resolution: {integrity: sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==} engines: {node: '>=4.8'} @@ -4781,8 +4837,8 @@ packages: engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0} dev: true - /diff-sequences@29.4.3: - resolution: {integrity: sha512-ofrBgwpPhCD85kMKtE9RYFFq6OC1A89oW2vvgWZNCwxrUpRUILopY7lsYyMDSjc8g6U6aiO0Qubg6r4Wgt5ZnA==} + /diff-sequences@29.6.3: + resolution: {integrity: sha512-EjePK1srD3P08o2j4f0ExnylqRs5B9tJjcp9t1krH2qRi8CCdsYfwe9JgSLurFBWwq4uOlipzfk5fHNvwFKr8Q==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} dev: true @@ -5017,16 +5073,16 @@ packages: source-map: 0.6.1 dev: true - /eslint-config-prettier@8.5.0(eslint@8.56.0): + /eslint-config-prettier@8.5.0(eslint@8.57.0): resolution: {integrity: sha512-obmWKLUNCnhtQRKc+tmnYuQl0pFU1ibYJQ5BGhTVB08bHe9wC8qUeG7c08dj9XX+AuPj1YSGSQIHl1pnDHZR0Q==} hasBin: true peerDependencies: eslint: '>=7.0.0' dependencies: - eslint: 8.56.0 + eslint: 8.57.0 dev: true - /eslint-plugin-compat@4.1.4(eslint@8.56.0): + /eslint-plugin-compat@4.1.4(eslint@8.57.0): resolution: {integrity: sha512-RxySWBmzfIROLFKgeJBJue2BU/6vM2KJWXWAUq+oW4QtrsZXRxbjgxmO1OfF3sHcRuuIenTS/wgo3GyUWZF24w==} engines: {node: '>=14.x'} peerDependencies: @@ -5037,18 +5093,18 @@ packages: ast-metadata-inferer: 0.8.0 browserslist: 4.21.7 caniuse-lite: 1.0.30001580 - eslint: 8.56.0 + eslint: 8.57.0 find-up: 5.0.0 lodash.memoize: 4.1.2 semver: 7.3.8 dev: true - /eslint-plugin-jest@27.2.3(@typescript-eslint/eslint-plugin@6.19.0)(eslint@8.56.0)(jest@27.5.1)(typescript@4.9.5): - resolution: {integrity: sha512-sRLlSCpICzWuje66Gl9zvdF6mwD5X86I4u55hJyFBsxYOsBCmT5+kSUjf+fkFWVMMgpzNEupjW8WzUqi83hJAQ==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + /eslint-plugin-jest@28.8.0(@typescript-eslint/eslint-plugin@8.2.0)(eslint@8.57.0)(jest@27.5.1)(typescript@5.5.4): + resolution: {integrity: sha512-Tubj1hooFxCl52G4qQu0edzV/+EZzPUeN8p2NnW5uu4fbDs+Yo7+qDVDc4/oG3FbCqEBmu/OC3LSsyiU22oghw==} + engines: {node: ^16.10.0 || ^18.12.0 || >=20.0.0} peerDependencies: - '@typescript-eslint/eslint-plugin': ^5.0.0 || ^6.0.0 - eslint: ^7.0.0 || ^8.0.0 + '@typescript-eslint/eslint-plugin': ^6.0.0 || ^7.0.0 || ^8.0.0 + eslint: ^7.0.0 || ^8.0.0 || ^9.0.0 jest: '*' peerDependenciesMeta: '@typescript-eslint/eslint-plugin': @@ -5056,10 +5112,10 @@ packages: jest: optional: true dependencies: - '@typescript-eslint/eslint-plugin': 6.19.0(@typescript-eslint/parser@6.19.0)(eslint@8.56.0)(typescript@4.9.5) - '@typescript-eslint/utils': 5.62.0(eslint@8.56.0)(typescript@4.9.5) - eslint: 8.56.0 - jest: 27.5.1 + '@typescript-eslint/eslint-plugin': 8.2.0(@typescript-eslint/parser@8.2.0)(eslint@8.57.0)(typescript@5.5.4) + '@typescript-eslint/utils': 6.19.0(eslint@8.57.0)(typescript@5.5.4) + eslint: 8.57.0 + jest: 27.5.1(ts-node@10.9.2) transitivePeerDependencies: - supports-color - typescript @@ -5070,7 +5126,7 @@ packages: engines: {node: '>=5.0.0'} dev: true - /eslint-plugin-prettier@4.2.1(eslint-config-prettier@8.5.0)(eslint@8.56.0)(prettier@2.7.1): + /eslint-plugin-prettier@4.2.1(eslint-config-prettier@8.5.0)(eslint@8.57.0)(prettier@2.7.1): resolution: {integrity: sha512-f/0rXLXUt0oFYs8ra4w49wYZBG5GKZpAYsJSm6rnYL5uVDjd+zowwMwVZHnAjf4edNrKpCDYfXDgmRE/Ak7QyQ==} engines: {node: '>=12.0.0'} peerDependencies: @@ -5081,22 +5137,22 @@ packages: eslint-config-prettier: optional: true dependencies: - eslint: 8.56.0 - eslint-config-prettier: 8.5.0(eslint@8.56.0) + eslint: 8.57.0 + eslint-config-prettier: 8.5.0(eslint@8.57.0) prettier: 2.7.1 prettier-linter-helpers: 1.0.0 dev: true - /eslint-plugin-react-hooks@4.6.0(eslint@8.56.0): + /eslint-plugin-react-hooks@4.6.0(eslint@8.57.0): resolution: {integrity: sha512-oFc7Itz9Qxh2x4gNHStv3BqJq54ExXmfC+a1NjAta66IAN87Wu0R/QArgIS9qKzX3dXKPI9H5crl9QchNMY9+g==} engines: {node: '>=10'} peerDependencies: eslint: ^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0 dependencies: - eslint: 8.56.0 + eslint: 8.57.0 dev: true - /eslint-plugin-react@7.30.1(eslint@8.56.0): + /eslint-plugin-react@7.30.1(eslint@8.57.0): resolution: {integrity: sha512-NbEvI9jtqO46yJA3wcRF9Mo0lF9T/jhdHqhCHXiXtD+Zcb98812wvokjWpU7Q4QH5edo6dmqrukxVvWWXHlsUg==} engines: {node: '>=4'} peerDependencies: @@ -5105,7 +5161,7 @@ packages: array-includes: 3.1.5 array.prototype.flatmap: 1.3.0 doctrine: 2.1.0 - eslint: 8.56.0 + eslint: 8.57.0 estraverse: 5.3.0 jsx-ast-utils: 3.2.0 minimatch: 3.1.2 @@ -5119,14 +5175,6 @@ packages: string.prototype.matchall: 4.0.7 dev: true - /eslint-scope@5.1.1: - resolution: {integrity: sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==} - engines: {node: '>=8.0.0'} - dependencies: - esrecurse: 4.3.0 - estraverse: 4.3.0 - dev: true - /eslint-scope@7.2.2: resolution: {integrity: sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} @@ -5145,15 +5193,15 @@ packages: engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} dev: true - /eslint@8.56.0: - resolution: {integrity: sha512-Go19xM6T9puCOWntie1/P997aXxFsOi37JIHRWI514Hc6ZnaHGKY9xFhrU65RT6CcBEzZoGG1e6Nq+DT04ZtZQ==} + /eslint@8.57.0: + resolution: {integrity: sha512-dZ6+mexnaTIbSBZWgou51U6OmzIhYM2VcNdtiTtI7qPNZm35Akpr0f6vtw3w1Kmn5PYo+tZVfh13WrhpS6oLqQ==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} hasBin: true dependencies: - '@eslint-community/eslint-utils': 4.4.0(eslint@8.56.0) + '@eslint-community/eslint-utils': 4.4.0(eslint@8.57.0) '@eslint-community/regexpp': 4.10.0 '@eslint/eslintrc': 2.1.4 - '@eslint/js': 8.56.0 + '@eslint/js': 8.57.0 '@humanwhocodes/config-array': 0.11.14 '@humanwhocodes/module-importer': 1.0.1 '@nodelib/fs.walk': 1.2.8 @@ -5227,11 +5275,6 @@ packages: estraverse: 5.3.0 dev: true - /estraverse@4.3.0: - resolution: {integrity: sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==} - engines: {node: '>=4.0'} - dev: true - /estraverse@5.3.0: resolution: {integrity: sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==} engines: {node: '>=4.0'} @@ -5376,27 +5419,27 @@ packages: jest-message-util: 27.5.1 dev: true - /expect@29.5.0: - resolution: {integrity: sha512-yM7xqUrCO2JdpFo4XpM82t+PJBFybdqoQuJLDGeDX2ij8NZzqRHyu3Hp188/JX7SWqud+7t4MUdvcgGBICMHZg==} + /expect@29.7.0: + resolution: {integrity: sha512-2Zks0hf1VLFYI1kbh0I5jP3KHHyCHpkfyHBzsSXRFgl/Bg9mWYfMW8oD+PdMPlEwy5HNsR9JutYy6pMeOh61nw==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} dependencies: - '@jest/expect-utils': 29.5.0 - jest-get-type: 29.4.3 - jest-matcher-utils: 29.5.0 - jest-message-util: 29.5.0 - jest-util: 29.5.0 + '@jest/expect-utils': 29.7.0 + jest-get-type: 29.6.3 + jest-matcher-utils: 29.7.0 + jest-message-util: 29.7.0 + jest-util: 29.7.0 dev: true - /express@4.18.2: - resolution: {integrity: sha512-5/PsL6iGPdfQ/lKM1UuielYgv3BUoJfz1aUwU9vHZ+J7gyvwdQXFEBIEIaxeGf0GIcreATNyBExtalisDbuMqQ==} + /express@4.19.2: + resolution: {integrity: sha512-5T6nhjsT+EOMzuck8JjBHARTHfMht0POzlA60WV2pMD3gyXw2LZnZ+ueGdNxG+0calOJcWKbpFcuzLZ91YWq9Q==} engines: {node: '>= 0.10.0'} dependencies: accepts: 1.3.8 array-flatten: 1.1.1 - body-parser: 1.20.1 + body-parser: 1.20.2 content-disposition: 0.5.4 content-type: 1.0.4 - cookie: 0.5.0 + cookie: 0.6.0 cookie-signature: 1.0.6 debug: 2.6.9 depd: 2.0.0 @@ -5967,8 +6010,8 @@ packages: lodash: 4.17.21 dev: true - /graphql@16.6.0: - resolution: {integrity: sha512-KPIBPDlW7NxrbT/eh4qPXz5FiFdL5UbaA0XUNz2Rp3Z3hqBSkbj0GVjwFDztsWVauZUWsbKHgMg++sk8UX0bkw==} + /graphql@16.9.0: + resolution: {integrity: sha512-GGTKBX4SD7Wdb8mqeDLni2oaRGYQWjWHGKPQ24ZMnUtKfcsVoiv4uX8+LJr1K6U5VW2Lu1BwJnj7uiori0YtRw==} engines: {node: ^12.22.0 || ^14.16.0 || ^16.0.0 || >=17.0.0} dev: true @@ -6069,8 +6112,8 @@ packages: function-bind: 1.1.2 dev: true - /headers-polyfill@3.1.2: - resolution: {integrity: sha512-tWCK4biJ6hcLqTviLXVR9DTRfYGQMXEIUj3gwJ2rZ5wO/at3XtkI4g8mCvFdUF9l1KMBNCfmNAdnahm1cgavQA==} + /headers-polyfill@3.2.5: + resolution: {integrity: sha512-tUCGvt191vNSQgttSyJoibR+VO+I6+iCHIUdhzEMJKE+EAL8BwCN7fUOZlY4ofOelNHsK+gEjxB/B+9N3EWtdA==} dev: true /highlight-es@1.0.3: @@ -6189,6 +6232,11 @@ packages: engines: {node: '>= 4'} dev: true + /ignore@5.3.2: + resolution: {integrity: sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==} + engines: {node: '>= 4'} + dev: true + /image-q@1.1.1: resolution: {integrity: sha512-zgWEeg+5KV7fILRUxkmPr/Sykz9wP22n2OfBtuzURc7jZ0D5esuw6xFfKWug3fYUEZ78+ECNKjjYmEUqxFtk+Q==} engines: {node: '>=0.9.0'} @@ -6831,7 +6879,7 @@ packages: '@jest/environment': 27.5.1 '@jest/test-result': 27.5.1 '@jest/types': 27.5.1 - '@types/node': 14.0.27 + '@types/node': 22.5.0 chalk: 4.1.2 co: 4.6.0 dedent: 0.7.0 @@ -6851,7 +6899,7 @@ packages: - supports-color dev: true - /jest-cli@27.5.1: + /jest-cli@27.5.1(ts-node@10.9.2): resolution: {integrity: sha512-Hc6HOOwYq4/74/c62dEE3r5elx8wjYqxY0r0G/nFrLDPMFRu6RA/u8qINOIkvhxG7mMQ5EJsOGfRpI8L6eFUVw==} engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0} hasBin: true @@ -6861,14 +6909,14 @@ packages: node-notifier: optional: true dependencies: - '@jest/core': 27.5.1 + '@jest/core': 27.5.1(ts-node@10.9.2) '@jest/test-result': 27.5.1 '@jest/types': 27.5.1 chalk: 4.1.2 exit: 0.1.2 graceful-fs: 4.2.9 import-local: 3.0.2 - jest-config: 27.5.1 + jest-config: 27.5.1(ts-node@10.9.2) jest-util: 27.5.1 jest-validate: 27.5.1 prompts: 2.4.2 @@ -6881,7 +6929,7 @@ packages: - utf-8-validate dev: true - /jest-config@27.5.1: + /jest-config@27.5.1(ts-node@10.9.2): resolution: {integrity: sha512-5sAsjm6tGdsVbW9ahcChPAFCk4IlkQUknH5AvKjuLTSlcO/wCZKyFdn7Rg0EkC+OGgWODEy2hDpWB1PgzH0JNA==} engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0} peerDependencies: @@ -6914,6 +6962,7 @@ packages: pretty-format: 27.5.1 slash: 3.0.0 strip-json-comments: 3.1.1 + ts-node: 10.9.2(@types/node@22.5.0)(typescript@5.5.4) transitivePeerDependencies: - bufferutil - canvas @@ -6931,14 +6980,14 @@ packages: pretty-format: 27.5.1 dev: true - /jest-diff@29.5.0: - resolution: {integrity: sha512-LtxijLLZBduXnHSniy0WMdaHjmQnt3g5sa16W4p0HqukYTTsyTW3GD1q41TyGl5YFXj/5B2U6dlh5FM1LIMgxw==} + /jest-diff@29.7.0: + resolution: {integrity: sha512-LMIgiIrhigmPrs03JHpxUh2yISK3vLFPkAodPeo0+BuF7wA2FoQbkEg1u8gBYBThncu7e1oEDUfIXVuTqLRUjw==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} dependencies: chalk: 4.1.2 - diff-sequences: 29.4.3 - jest-get-type: 29.4.3 - pretty-format: 29.5.0 + diff-sequences: 29.6.3 + jest-get-type: 29.6.3 + pretty-format: 29.7.0 dev: true /jest-docblock@27.5.1: @@ -6966,7 +7015,7 @@ packages: '@jest/environment': 27.5.1 '@jest/fake-timers': 27.5.1 '@jest/types': 27.5.1 - '@types/node': 14.0.27 + '@types/node': 22.5.0 jest-mock: 27.5.1 jest-util: 27.5.1 jsdom: 16.7.0 @@ -6984,7 +7033,7 @@ packages: '@jest/environment': 27.5.1 '@jest/fake-timers': 27.5.1 '@jest/types': 27.5.1 - '@types/node': 14.0.27 + '@types/node': 22.5.0 jest-mock: 27.5.1 jest-util: 27.5.1 dev: true @@ -6994,8 +7043,8 @@ packages: engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0} dev: true - /jest-get-type@29.4.3: - resolution: {integrity: sha512-J5Xez4nRRMjk8emnTpWrlkyb9pfRQQanDrvWHhsR1+VUfbwxi30eVcZFlcdGInRibU4G5LwHXpI7IRHU0CY+gg==} + /jest-get-type@29.6.3: + resolution: {integrity: sha512-zrteXnqYxfQh7l5FHyL38jL39di8H8rHoecLH3JNxH3BwOrBsNeabdap5e0I23lD4HHI8W5VFBZqG4Eaq5LNcw==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} dev: true @@ -7005,7 +7054,7 @@ packages: dependencies: '@jest/types': 26.6.2 '@types/graceful-fs': 4.1.3 - '@types/node': 14.0.27 + '@types/node': 22.5.0 anymatch: 3.1.3 fb-watchman: 2.0.1 graceful-fs: 4.2.9 @@ -7028,7 +7077,7 @@ packages: dependencies: '@jest/types': 27.5.1 '@types/graceful-fs': 4.1.3 - '@types/node': 14.0.27 + '@types/node': 22.5.0 anymatch: 3.1.3 fb-watchman: 2.0.1 graceful-fs: 4.2.9 @@ -7050,7 +7099,7 @@ packages: '@jest/source-map': 27.5.1 '@jest/test-result': 27.5.1 '@jest/types': 27.5.1 - '@types/node': 14.0.27 + '@types/node': 22.5.0 chalk: 4.1.2 co: 4.6.0 expect: 27.5.1 @@ -7085,14 +7134,14 @@ packages: pretty-format: 27.5.1 dev: true - /jest-matcher-utils@29.5.0: - resolution: {integrity: sha512-lecRtgm/rjIK0CQ7LPQwzCs2VwW6WAahA55YBuI+xqmhm7LAaxokSB8C97yJeYyT+HvQkH741StzpU41wohhWw==} + /jest-matcher-utils@29.7.0: + resolution: {integrity: sha512-sBkD+Xi9DtcChsI3L3u0+N0opgPYnCRPtGcQYrgXmR+hmt/fYfWAL0xRXYU8eWOdfuLgBe0YCW3AFtnRLagq/g==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} dependencies: chalk: 4.1.2 - jest-diff: 29.5.0 - jest-get-type: 29.4.3 - pretty-format: 29.5.0 + jest-diff: 29.7.0 + jest-get-type: 29.6.3 + pretty-format: 29.7.0 dev: true /jest-message-util@27.5.1: @@ -7110,17 +7159,17 @@ packages: stack-utils: 2.0.5 dev: true - /jest-message-util@29.5.0: - resolution: {integrity: sha512-Kijeg9Dag6CKtIDA7O21zNTACqD5MD/8HfIV8pdD94vFyFuer52SigdC3IQMhab3vACxXMiFk+yMHNdbqtyTGA==} + /jest-message-util@29.7.0: + resolution: {integrity: sha512-GBEV4GRADeP+qtB2+6u61stea8mGcOT4mCtrYISZwfu9/ISHFJ/5zOMXYbpBE9RsS5+Gb63DW4FgmnKJ79Kf6w==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} dependencies: '@babel/code-frame': 7.23.5 - '@jest/types': 29.5.0 + '@jest/types': 29.6.3 '@types/stack-utils': 2.0.1 chalk: 4.1.2 graceful-fs: 4.2.9 micromatch: 4.0.4 - pretty-format: 29.5.0 + pretty-format: 29.7.0 slash: 3.0.0 stack-utils: 2.0.5 dev: true @@ -7130,7 +7179,7 @@ packages: engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0} dependencies: '@jest/types': 27.5.1 - '@types/node': 14.0.27 + '@types/node': 22.5.0 dev: true /jest-pnp-resolver@1.2.2(jest-resolve@27.5.1): @@ -7191,7 +7240,7 @@ packages: '@jest/test-result': 27.5.1 '@jest/transform': 27.5.1 '@jest/types': 27.5.1 - '@types/node': 14.0.27 + '@types/node': 22.5.0 chalk: 4.1.2 emittery: 0.8.1 graceful-fs: 4.2.9 @@ -7248,7 +7297,7 @@ packages: resolution: {integrity: sha512-S5wqyz0DXnNJPd/xfIzZ5Xnp1HrJWBczg8mMfMpN78OJ5eDxXyf+Ygld9wX1DnUWbIbhM1YDY95NjR4CBXkb2g==} engines: {node: '>= 10.14.2'} dependencies: - '@types/node': 14.0.27 + '@types/node': 22.5.0 graceful-fs: 4.2.9 dev: true @@ -7256,7 +7305,7 @@ packages: resolution: {integrity: sha512-jZCyo6iIxO1aqUxpuBlwTDMkzOAJS4a3eYz3YzgxxVQFwLeSA7Jfq5cbqCY+JLvTDrWirgusI/0KwxKMgrdf7w==} engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0} dependencies: - '@types/node': 14.0.27 + '@types/node': 22.5.0 graceful-fs: 4.2.9 dev: true @@ -7295,7 +7344,7 @@ packages: engines: {node: '>= 10.14.2'} dependencies: '@jest/types': 26.6.2 - '@types/node': 14.0.27 + '@types/node': 22.5.0 chalk: 4.1.2 graceful-fs: 4.2.9 is-ci: 2.0.0 @@ -7307,19 +7356,19 @@ packages: engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0} dependencies: '@jest/types': 27.5.1 - '@types/node': 14.0.27 + '@types/node': 22.5.0 chalk: 4.1.2 ci-info: 3.3.0 graceful-fs: 4.2.9 picomatch: 2.3.1 dev: true - /jest-util@29.5.0: - resolution: {integrity: sha512-RYMgG/MTadOr5t8KdhejfvUU82MxsCu5MF6KuDUHl+NuwzUt+Sm6jJWxTJVrDR1j5M/gJVCPKQEpWXY+yIQ6lQ==} + /jest-util@29.7.0: + resolution: {integrity: sha512-z6EbKajIpqGKU56y5KBUgy1dt1ihhQJgWzUlZHArA/+X2ad7Cb5iF+AK1EWVL/Bo7Rz9uurpqw6SiBCefUbCGA==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} dependencies: - '@jest/types': 29.5.0 - '@types/node': 14.0.27 + '@jest/types': 29.6.3 + '@types/node': 22.5.0 chalk: 4.1.2 ci-info: 3.3.0 graceful-fs: 4.2.9 @@ -7344,7 +7393,7 @@ packages: dependencies: '@jest/test-result': 27.5.1 '@jest/types': 27.5.1 - '@types/node': 14.0.27 + '@types/node': 22.5.0 ansi-escapes: 4.3.2 chalk: 4.1.2 jest-util: 27.5.1 @@ -7355,7 +7404,7 @@ packages: resolution: {integrity: sha512-KWYVV1c4i+jbMpaBC+U++4Va0cp8OisU185o73T1vo99hqi7w8tSJfUXYswwqqrjzwxa6KpRK54WhPvwf5w6PQ==} engines: {node: '>= 10.13.0'} dependencies: - '@types/node': 14.0.27 + '@types/node': 22.5.0 merge-stream: 2.0.0 supports-color: 7.1.0 dev: true @@ -7364,12 +7413,12 @@ packages: resolution: {integrity: sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg==} engines: {node: '>= 10.13.0'} dependencies: - '@types/node': 14.0.27 + '@types/node': 22.5.0 merge-stream: 2.0.0 supports-color: 8.1.1 dev: true - /jest@27.5.1: + /jest@27.5.1(ts-node@10.9.2): resolution: {integrity: sha512-Yn0mADZB89zTtjkPJEXwrac3LHudkQMR+Paqa8uxJHCBr9agxztUifWCyiYrjhMPBoUVBjyny0I7XH6ozDr7QQ==} engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0} hasBin: true @@ -7379,9 +7428,9 @@ packages: node-notifier: optional: true dependencies: - '@jest/core': 27.5.1 + '@jest/core': 27.5.1(ts-node@10.9.2) import-local: 3.0.2 - jest-cli: 27.5.1 + jest-cli: 27.5.1(ts-node@10.9.2) transitivePeerDependencies: - bufferutil - canvas @@ -7920,6 +7969,10 @@ packages: semver: 6.3.0 dev: true + /make-error@1.3.6: + resolution: {integrity: sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==} + dev: true + /makeerror@1.0.11: resolution: {integrity: sha512-M/XvMZ6oK4edXjvg/ZYyzByg8kjpVrF/m0x3wbhOlzJfsQgFkqP1rJnLnJExOcslmLSSeLiN6NmF+cBoKJHGTg==} dependencies: @@ -8063,6 +8116,13 @@ packages: brace-expansion: 2.0.1 dev: true + /minimatch@9.0.5: + resolution: {integrity: sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==} + engines: {node: '>=16 || 14 >=14.17'} + dependencies: + brace-expansion: 2.0.1 + dev: true + /minimist@1.2.8: resolution: {integrity: sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==} dev: true @@ -8114,27 +8174,27 @@ packages: resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} dev: true - /msw@1.2.1(typescript@4.9.5): - resolution: {integrity: sha512-bF7qWJQSmKn6bwGYVPXOxhexTCGD5oJSZg8yt8IBClxvo3Dx/1W0zqE1nX9BSWmzRsCKWfeGWcB/vpqV6aclpw==} + /msw@1.3.3(typescript@5.5.4): + resolution: {integrity: sha512-CiPyRFiYJCXYyH/vwxT7m+sa4VZHuUH6cGwRBj0kaTjBGpsk4EnL47YzhoA859htVCF2vzqZuOsomIUlFqg9GQ==} engines: {node: '>=14'} hasBin: true requiresBuild: true peerDependencies: - typescript: '>= 4.4.x <= 5.0.x' + typescript: '>= 4.4.x' peerDependenciesMeta: typescript: optional: true dependencies: '@mswjs/cookies': 0.2.2 - '@mswjs/interceptors': 0.17.9 + '@mswjs/interceptors': 0.17.10 '@open-draft/until': 1.0.3 '@types/cookie': 0.4.1 '@types/js-levenshtein': 1.1.1 - chalk: 4.1.1 + chalk: 4.1.2 chokidar: 3.5.3 cookie: 0.4.2 - graphql: 16.6.0 - headers-polyfill: 3.1.2 + graphql: 16.9.0 + headers-polyfill: 3.2.5 inquirer: 8.2.5 is-node-process: 1.2.0 js-levenshtein: 1.1.6 @@ -8143,7 +8203,7 @@ packages: path-to-regexp: 6.2.1 strict-event-emitter: 0.4.6 type-fest: 2.19.0 - typescript: 4.9.5 + typescript: 5.5.4 yargs: 17.7.2 transitivePeerDependencies: - encoding @@ -8805,6 +8865,15 @@ packages: react-is: 18.2.0 dev: true + /pretty-format@29.7.0: + resolution: {integrity: sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + '@jest/schemas': 29.6.3 + ansi-styles: 5.2.0 + react-is: 18.2.0 + dev: true + /pretty-format@3.8.0: resolution: {integrity: sha512-WuxUnVtlWL1OfZFQFuqvnvs6MiAGk9UNsBostyBOB0Is9wb5uRESevA6rnl/rkksXaGX3GzZhPup5d6Vp1nFew==} dev: true @@ -8928,8 +8997,8 @@ packages: engines: {node: '>= 0.6'} dev: true - /raw-body@2.5.1: - resolution: {integrity: sha512-qqJBtEyVgS0ZmPGdCFPWJ3FreoqvG4MVQln/kCgF7Olq95IbOp0/BWyMwbdtn4VTvkM8Y7khCQ2Xgk/tcrCXig==} + /raw-body@2.5.2: + resolution: {integrity: sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA==} engines: {node: '>= 0.8'} dependencies: bytes: 3.1.2 @@ -9262,7 +9331,7 @@ packages: glob: 7.1.6 dev: true - /rollup-plugin-dts@6.1.0(rollup@4.9.6)(typescript@4.9.5): + /rollup-plugin-dts@6.1.0(rollup@4.9.6)(typescript@5.5.4): resolution: {integrity: sha512-ijSCPICkRMDKDLBK9torss07+8dl9UpY9z1N/zTeA1cIqdzMlpkV3MOOC7zukyvQfDyxa1s3Dl2+DeiP/G6DOw==} engines: {node: '>=16'} peerDependencies: @@ -9271,7 +9340,7 @@ packages: dependencies: magic-string: 0.30.5 rollup: 4.9.6 - typescript: 4.9.5 + typescript: 5.5.4 optionalDependencies: '@babel/code-frame': 7.23.5 dev: true @@ -9463,6 +9532,12 @@ packages: lru-cache: 6.0.0 dev: true + /semver@7.6.3: + resolution: {integrity: sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==} + engines: {node: '>=10'} + hasBin: true + dev: true + /send@0.18.0: resolution: {integrity: sha512-qqWzuOjSFOuqPjFe4NOsMLafToQQwBSOEpS+FwEt3A2V3vKubTquT3vmLTQpFgMXp8AlFWFuP1qKaJZOtPpVXg==} engines: {node: '>= 0.8.0'} @@ -10357,13 +10432,53 @@ packages: utf8-byte-length: 1.0.4 dev: true - /ts-api-utils@1.0.1(typescript@4.9.5): + /ts-api-utils@1.0.1(typescript@5.5.4): resolution: {integrity: sha512-lC/RGlPmwdrIBFTX59wwNzqh7aR2otPNPR/5brHZm/XKFYKsfqxihXUe9pU3JI+3vGkl+vyCoNNnPhJn3aLK1A==} engines: {node: '>=16.13.0'} peerDependencies: typescript: '>=4.2.0' dependencies: - typescript: 4.9.5 + typescript: 5.5.4 + dev: true + + /ts-api-utils@1.3.0(typescript@5.5.4): + resolution: {integrity: sha512-UQMIo7pb8WRomKR1/+MFVLTroIvDVtMX3K6OUir8ynLyzB8Jeriont2bTAtmNPa1ekAgN7YPDyf6V+ygrdU+eQ==} + engines: {node: '>=16'} + peerDependencies: + typescript: '>=4.2.0' + dependencies: + typescript: 5.5.4 + dev: true + + /ts-node@10.9.2(@types/node@22.5.0)(typescript@5.5.4): + resolution: {integrity: sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ==} + hasBin: true + peerDependencies: + '@swc/core': '>=1.2.50' + '@swc/wasm': '>=1.2.50' + '@types/node': '*' + typescript: '>=2.7' + peerDependenciesMeta: + '@swc/core': + optional: true + '@swc/wasm': + optional: true + dependencies: + '@cspotcode/source-map-support': 0.8.1 + '@tsconfig/node10': 1.0.11 + '@tsconfig/node12': 1.0.11 + '@tsconfig/node14': 1.0.3 + '@tsconfig/node16': 1.0.4 + '@types/node': 22.5.0 + acorn: 8.11.3 + acorn-walk: 8.3.3 + arg: 4.1.3 + create-require: 1.1.1 + diff: 4.0.2 + make-error: 1.3.6 + typescript: 5.5.4 + v8-compile-cache-lib: 3.0.1 + yn: 3.1.1 dev: true /tslib@1.13.0: @@ -10374,16 +10489,6 @@ packages: resolution: {integrity: sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg==} dev: true - /tsutils@3.21.0(typescript@4.9.5): - resolution: {integrity: sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==} - engines: {node: '>= 6'} - peerDependencies: - typescript: '>=2.8.0 || >= 3.2.0-dev || >= 3.3.0-dev || >= 3.4.0-dev || >= 3.5.0-dev || >= 3.6.0-dev || >= 3.6.0-beta || >= 3.7.0-dev || >= 3.7.0-beta' - dependencies: - tslib: 1.13.0 - typescript: 4.9.5 - dev: true - /tunnel-agent@0.6.0: resolution: {integrity: sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w==} dependencies: @@ -10448,9 +10553,9 @@ packages: hasBin: true dev: true - /typescript@4.9.5: - resolution: {integrity: sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g==} - engines: {node: '>=4.2.0'} + /typescript@5.5.4: + resolution: {integrity: sha512-Mtq29sKDAEYP7aljRgtPOpTvOfbwRWlS6dPRzwjdE+C0R4brX/GUyhHSecbHMFLNBLcJIPt9nl9yG5TZ1weH+Q==} + engines: {node: '>=14.17'} hasBin: true dev: true @@ -10463,6 +10568,10 @@ packages: which-boxed-primitive: 1.0.2 dev: true + /undici-types@6.19.8: + resolution: {integrity: sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw==} + dev: true + /unicode-canonical-property-names-ecmascript@2.0.0: resolution: {integrity: sha512-yY5PpDlfVIU5+y/BSCxAJRBIS1Zc2dDG3Ujq+sR0U+JjUevW2JhocOF+soROYDSaAezOzOKuyyixhD6mBknSmQ==} engines: {node: '>=4'} @@ -10618,6 +10727,10 @@ packages: hasBin: true dev: true + /v8-compile-cache-lib@3.0.1: + resolution: {integrity: sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==} + dev: true + /v8-to-istanbul@8.1.1: resolution: {integrity: sha512-FGtKtv3xIpR6BYhvgH8MI/y78oT7d8Au3ww4QIxymrCtZEh5b8gCw2siywE+puhEmuWKDtmfrvF5UlB298ut3w==} engines: {node: '>=10.12.0'} @@ -10927,6 +11040,11 @@ packages: fd-slicer: 1.1.0 dev: true + /yn@3.1.1: + resolution: {integrity: sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==} + engines: {node: '>=6'} + dev: true + /yocto-queue@0.1.0: resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==} engines: {node: '>=10'} diff --git a/scripts/run-testcafe-localhost.mjs b/scripts/run-testcafe-localhost.mjs new file mode 100644 index 000000000..9d6751666 --- /dev/null +++ b/scripts/run-testcafe-localhost.mjs @@ -0,0 +1,38 @@ +import * as child_process from 'child_process' + +const currentEnv = process.env +export const { + POSTHOG_PROJECT_KEY, + POSTHOG_API_KEY, + POSTHOG_API_HOST = 'http://localhost:8000', + POSTHOG_API_PROJECT = '1', +} = currentEnv + +const browser = process.argv[2] || 'chrome' +const args = process.argv.slice(3) + +async function main() { + if (!POSTHOG_API_KEY) { + throw new Error('POSTHOG_API_KEY env variable is required (create a new all access API key at http://localhost:8000/project/1/settings/user-api-keys)') + } + if (!POSTHOG_PROJECT_KEY) { + throw new Error('POSTHOG_PROJECT_KEY env variable is required (see Project API Key http://localhost:8000/project/1/settings/project)') + } + + console.log('Running testcafe tests on localhost') + child_process.execSync(`pnpm testcafe ${browser} ${args.join(' ')}`, { + env: { + ...currentEnv, + POSTHOG_API_KEY, + POSTHOG_PROJECT_KEY, + POSTHOG_API_HOST, + POSTHOG_API_PROJECT, + }, + stdio: 'inherit', + }) +} + +main().catch((error) => { + console.error(error) + process.exit(1) +}) \ No newline at end of file diff --git a/scripts/run-testcafe-with-retries.mjs b/scripts/run-testcafe-with-retries.mjs deleted file mode 100644 index 7dc34efd6..000000000 --- a/scripts/run-testcafe-with-retries.mjs +++ /dev/null @@ -1,31 +0,0 @@ -import yargs from "yargs" - -import {spawnSync} from "child_process" - - -const main = async () => { - const argv= yargs(process.argv).argv - const attempts = argv.attempts || 3 - const browser = argv.browser - if (!browser) { - throw new Error("Missing browser argument") - } - - - for (let i = 0; i < attempts; i++) { - console.log(`Attempt ${i + 1} of a maximum of ${attempts} attempts`) - const result = spawnSync("pnpm", ["testcafe", browser], {stdio: "inherit"}) - if (result.status === 0) { - console.log("Test succeeded") - return - } - console.log("Test failed") - } - throw new Error(`Test failed after ${attempts} attempts`) -} - - -main().catch(e => { - console.error(e) - process.exit(1) -}) diff --git a/src/__tests__/personProcessing.test.ts b/src/__tests__/personProcessing.test.ts index ae22a0128..35531594d 100644 --- a/src/__tests__/personProcessing.test.ts +++ b/src/__tests__/personProcessing.test.ts @@ -33,7 +33,7 @@ jest.mock('../utils/globals', () => { } }) -// eslint-disable-next-line @typescript-eslint/no-var-requires +// eslint-disable-next-line @typescript-eslint/no-require-imports const { mockURLGetter, mockReferrerGetter } = require('../utils/globals') describe('person processing', () => { diff --git a/src/entrypoints/recorder.ts b/src/entrypoints/recorder.ts index 5376dc8db..221abc2cd 100644 --- a/src/entrypoints/recorder.ts +++ b/src/entrypoints/recorder.ts @@ -203,7 +203,7 @@ function _tryReadXHRBody({ if (isObject(body)) { try { return JSON.stringify(body) - } catch (e) { + } catch { return '[SessionReplay] Failed to stringify response object' } } diff --git a/src/extensions/exception-autocapture/error-conversion.ts b/src/extensions/exception-autocapture/error-conversion.ts index 56beaefa8..26398a9b5 100644 --- a/src/extensions/exception-autocapture/error-conversion.ts +++ b/src/extensions/exception-autocapture/error-conversion.ts @@ -46,7 +46,7 @@ export function parseStackFrames(ex: Error & { framesToPop?: number; stacktrace? try { return defaultStackParser(stacktrace, popSize) - } catch (e) { + } catch { // no-empty } @@ -204,7 +204,7 @@ export function unhandledRejectionToProperties([ev]: [ev: PromiseRejectionEvent] else if ('detail' in ev && 'reason' in (ev as any).detail) { error = (ev as any).detail.reason } - } catch (_oO) { + } catch { // no-empty } diff --git a/src/extensions/exception-autocapture/type-checking.ts b/src/extensions/exception-autocapture/type-checking.ts index 389ddd61e..01016ee6f 100644 --- a/src/extensions/exception-autocapture/type-checking.ts +++ b/src/extensions/exception-autocapture/type-checking.ts @@ -11,7 +11,7 @@ export function isPlainObject(candidate: unknown): candidate is Record | undefined constructor(private readonly instance: PostHog) { this._enabledServerSide = !!this.instance.persistence?.props[WEB_VITALS_ENABLED_SERVER_SIDE] diff --git a/src/heatmaps.ts b/src/heatmaps.ts index 257ff9c5e..7333df7b4 100644 --- a/src/heatmaps.ts +++ b/src/heatmaps.ts @@ -51,7 +51,7 @@ export class Heatmaps { // TODO: Periodically flush this if no other event has taken care of it private buffer: HeatmapEventBuffer - private _flushInterval: number | null = null + private _flushInterval: ReturnType | null = null constructor(instance: PostHog) { this.instance = instance diff --git a/src/request.ts b/src/request.ts index e594c892c..5719300cd 100644 --- a/src/request.ts +++ b/src/request.ts @@ -95,7 +95,7 @@ const xhr = (options: RequestOptions) => { if (req.status === 200) { try { response.json = JSON.parse(req.responseText) - } catch (e) { + } catch { // logger.error(e) } } @@ -177,7 +177,7 @@ const _sendBeacon = (options: RequestOptions) => { // sendBeacon requires a blob so we convert it const sendBeaconBody = typeof body === 'string' ? new Blob([body], { type: contentType }) : body navigator!.sendBeacon!(url, sendBeaconBody) - } catch (e) { + } catch { // send beacon is a best-effort, fire-and-forget mechanism on page unload, // we don't want to throw errors here } diff --git a/src/storage.ts b/src/storage.ts index 4f363eca8..9f67f19f3 100644 --- a/src/storage.ts +++ b/src/storage.ts @@ -110,7 +110,7 @@ export const cookieStore: PersistentStore = { return decodeURIComponent(c.substring(nameEQ.length, c.length)) } } - } catch (err) {} + } catch {} return null }, @@ -118,7 +118,7 @@ export const cookieStore: PersistentStore = { let cookie try { cookie = JSON.parse(cookieStore.get(name)) || {} - } catch (err) { + } catch { // noop } return cookie @@ -160,7 +160,7 @@ export const cookieStore: PersistentStore = { document.cookie = new_cookie_val return new_cookie_val - } catch (err) { + } catch { return } }, @@ -168,7 +168,7 @@ export const cookieStore: PersistentStore = { remove: function (name, cross_subdomain) { try { cookieStore.set(name, '', -1, cross_subdomain) - } catch (err) { + } catch { return } }, @@ -192,7 +192,7 @@ export const localStore: PersistentStore = { supported = false } localStore.remove(key) - } catch (err) { + } catch { supported = false } } else { @@ -222,7 +222,7 @@ export const localStore: PersistentStore = { parse: function (name) { try { return JSON.parse(localStore.get(name)) || {} - } catch (err) { + } catch { // noop } return null @@ -258,11 +258,11 @@ export const localPlusCookieStore: PersistentStore = { try { // See if there's a cookie stored with data. cookieProperties = cookieStore.parse(name) || {} - } catch (err) {} + } catch {} const value = extend(cookieProperties, JSON.parse(localStore.get(name) || '{}')) localStore.set(name, value) return value - } catch (err) { + } catch { // noop } return null @@ -345,7 +345,7 @@ export const sessionStore: PersistentStore = { sessionStorageSupported = false } sessionStore.remove(key) - } catch (err) { + } catch { sessionStorageSupported = false } } else { @@ -370,7 +370,7 @@ export const sessionStore: PersistentStore = { parse: function (name) { try { return JSON.parse(sessionStore.get(name)) || null - } catch (err) { + } catch { // noop } return null diff --git a/src/types.ts b/src/types.ts index cc359bb0a..adf912856 100644 --- a/src/types.ts +++ b/src/types.ts @@ -398,7 +398,7 @@ export interface PersistentStore { remove: (name: string, cross_subdomain?: boolean) => void } -// eslint-disable-next-line @typescript-eslint/ban-types +// eslint-disable-next-line @typescript-eslint/no-empty-object-type export type Breaker = {} export type EventHandler = (event: Event) => boolean | void diff --git a/src/utils/index.ts b/src/utils/index.ts index 1d02849c4..61261fae2 100644 --- a/src/utils/index.ts +++ b/src/utils/index.ts @@ -111,7 +111,7 @@ export function entries(obj: Record): [string, T][] { export const isValidRegex = function (str: string): boolean { try { new RegExp(str) - } catch (error) { + } catch { return false } return true @@ -129,7 +129,7 @@ export const timestamp = function (): number { export const trySafe = function (fn: () => T): T | undefined { try { return fn() - } catch (e) { + } catch { return undefined } } @@ -149,7 +149,7 @@ export const safewrap = function any = (...args: } as F } -// eslint-disable-next-line @typescript-eslint/ban-types +// eslint-disable-next-line @typescript-eslint/no-unsafe-function-type export const safewrapClass = function (klass: Function, functions: string[]): void { for (let i = 0; i < functions.length; i++) { klass.prototype[functions[i]] = safewrap(klass.prototype[functions[i]]) diff --git a/src/utils/request-utils.ts b/src/utils/request-utils.ts index 01cb90569..6fe822bd5 100644 --- a/src/utils/request-utils.ts +++ b/src/utils/request-utils.ts @@ -67,7 +67,7 @@ export const getQueryParam = function (url: string, param: string): string { let result = keyValuePair[1] try { result = decodeURIComponent(result) - } catch (err) { + } catch { logger.error('Skipping decoding for malformed query param: ' + result) } return result.replace(/\+/g, ' ') diff --git a/testcafe/check-testcafe-results.js b/testcafe/check-testcafe-results.js new file mode 100644 index 000000000..e65c214f1 --- /dev/null +++ b/testcafe/check-testcafe-results.js @@ -0,0 +1,67 @@ +// This script checks the events produced by running the testcafe tests and asserts that the events have shown up in +// our production US cloud posthog. The actual assert functions live alongside the tests themselves, which save the test +// information to a file. This script reads those files, and then runs relevant assert functions. + +// This happens after the testcafe tests have all finished, so that we are not waiting on ingestion lag per test, only +// once when all tests have finished. + +// Some hackiness follows, allowing us to import the assert function from a test file without running the tests +// themselves: +const testCafeMock = { + test: () => testCafeMock, + page: () => testCafeMock, + fixture: () => testCafeMock, + requestHooks: () => testCafeMock, + afterEach: () => testCafeMock, +} +// eslint-disable-next-line no-undef +globalThis.fixture = () => testCafeMock +// eslint-disable-next-line no-undef +globalThis.test = () => testCafeMock +import { + assertConfigOptionsChangeAutocaptureBehaviourAccordingly, + assertAutocapturedEventsWorkAndAreAccessibleViaApi, + assertCustomEventsWorkAndAreAccessibleViaApi, +} from './e2e.spec' +// end of hackiness + +import { getResultsJsonFiles, log, error, POSTHOG_API_PROJECT } from './helpers' +const asserts = { + assertConfigOptionsChangeAutocaptureBehaviourAccordingly, + assertAutocapturedEventsWorkAndAreAccessibleViaApi, + assertCustomEventsWorkAndAreAccessibleViaApi, +} +async function main() { + // eslint-disable-next-line no-console + log(` +Waiting for events from tests to appear in PostHog. +You can manually confirm whether the events have shown up at https://us.posthog.com/project/${POSTHOG_API_PROJECT}/activity/explore +If they seem to be failing unexpectedly, check grafana for ingestion lag at https://grafana.prod-us.posthog.dev/d/homepage/homepage +`) + // each test will put a results.json file in this folder, so let's list all the files in this folder + const files = getResultsJsonFiles() + + if (files.length !== 3) { + throw new Error(`Expected 3 results files, got ${JSON.stringify(files)}`) + } + log(JSON.stringify(files, null, 2)) + + // the deadline is the same for each assert, as the ingestion lag will be happening in parallel + const deadline = Date.now() + 1000 * 60 * 30 // 30 minutes + + for (const file of files) { + const testSessionId = file.testSessionId + const assertFunction = asserts[file.assert] + log(`Asserting ${file.assert} for test session ${testSessionId}`, assertFunction, file) + if (!testSessionId || !assertFunction) { + throw new Error(`Invalid results file: ${file}`) + } + await assertFunction(testSessionId, deadline) + } +} + +main().catch((e) => { + error(e) + // eslint-disable-next-line no-undef + process.exit(1) +}) diff --git a/testcafe/e2e.spec.js b/testcafe/e2e.spec.js index 4ce39936b..a26ceec7e 100644 --- a/testcafe/e2e.spec.js +++ b/testcafe/e2e.spec.js @@ -1,5 +1,15 @@ import { t } from 'testcafe' -import { retryUntilResults, queryAPI, initPosthog, captureLogger, staticFilesMock } from './helpers' +import { + captureLogger, + capturesMap, + initPosthog, + isLoaded, + queryAPI, + retryUntilResults, + staticFilesMock, + writeResultsJsonFile, +} from './helpers' +import { expect } from 'expect' // eslint-disable-next-line no-undef fixture('posthog.js capture') @@ -18,89 +28,134 @@ fixture('posthog.js capture') }) test('Custom events work and are accessible via /api/event', async (t) => { - const testSessionId = await initPosthog() + const testSessionId = await initPosthog(t.testRun.test.name) await t .wait(5000) + .expect(isLoaded()) + .ok() .click('[data-cy-custom-event-button]') - .wait(5000) + .wait(10000) + .expect(capturesMap()) + .contains({ + $pageview: 1, + $autocapture: 1, + 'custom-event': 1, + }) .expect(captureLogger.count(() => true)) .gte(1) // Check no requests failed await t.expect(captureLogger.count(({ response }) => response.statusCode !== 200)).eql(0) - const results = await retryUntilResults(() => queryAPI(testSessionId), 3) - - await t.expect(results.length).eql(3) - await t.expect(results.filter(({ event }) => event === 'custom-event').length).eql(1) - await t.expect(results.filter(({ event }) => event === '$pageview').length).eql(1) - await t.expect(results.filter(({ event }) => event === '$autocapture').length).eql(1) + writeResultsJsonFile(t.testRun.test.name, testSessionId, assertCustomEventsWorkAndAreAccessibleViaApi) }) +export async function assertCustomEventsWorkAndAreAccessibleViaApi(testSessionId, deadline) { + const results = await retryUntilResults(() => queryAPI(testSessionId), 3, { deadline }) + expect(results.length).toEqual(3) + expect(results.filter(({ event }) => event === 'custom-event').length).toEqual(1) + expect(results.filter(({ event }) => event === '$pageview').length).toEqual(1) + expect(results.filter(({ event }) => event === '$autocapture').length).toEqual(1) +} + test('Autocaptured events work and are accessible via /api/event', async (t) => { - const testSessionId = await initPosthog() + const testSessionId = await initPosthog(t.testRun.test.name) await t .wait(5000) + .expect(isLoaded()) + .ok() .click('[data-cy-link-mask-text]') .click('[data-cy-button-sensitive-attributes]') - .wait(5000) + .wait(10000) + .expect(capturesMap()) + .contains({ + $pageview: 1, + $autocapture: 2, + }) .expect(captureLogger.count(() => true)) .gte(2) + writeResultsJsonFile(t.testRun.test.name, testSessionId, assertAutocapturedEventsWorkAndAreAccessibleViaApi) + // Check no requests failed await t.expect(captureLogger.count(({ response }) => response.statusCode !== 200)).eql(0) +}) - const results = await retryUntilResults(() => queryAPI(testSessionId), 3) +export async function assertAutocapturedEventsWorkAndAreAccessibleViaApi(testSessionId, deadline) { + const results = await retryUntilResults(() => queryAPI(testSessionId), 3, { + deadline, + }) + expect(results.filter(({ event }) => event === '$pageview').length).toEqual(1) const autocapturedEvents = results.filter((e) => e.event === '$autocapture') - await t.expect(autocapturedEvents.length).eql(2) + await expect(autocapturedEvents.length).toEqual(2) const autocapturedLinkClickEvents = autocapturedEvents.filter((e) => e.elements[0].tag_name === 'a') const autocapturedButtonClickEvents = autocapturedEvents.filter((e) => e.elements[0].tag_name === 'button') - await t.expect(autocapturedButtonClickEvents.length).eql(1) - await t.expect(autocapturedLinkClickEvents.length).eql(1) + await expect(autocapturedButtonClickEvents.length).toEqual(1) + await expect(autocapturedLinkClickEvents.length).toEqual(1) const autocapturedButtonElement = autocapturedButtonClickEvents[0].elements[0] const autocapturedLinkElement = autocapturedLinkClickEvents[0].elements[0] // Captures text content if mask_all_text isn't set - await t.expect(autocapturedLinkElement['text']).eql('Sensitive text!') + await expect(autocapturedLinkElement['text']).toEqual('Sensitive text!') const attrKeys = Object.keys(autocapturedButtonElement.attributes) attrKeys.sort() - await t - .expect(attrKeys) - .eql(['attr__class', 'attr__data-cy-button-sensitive-attributes', 'attr__data-sensitive', 'attr__id']) -}) + + expect(attrKeys).toEqual([ + 'attr__class', + 'attr__data-cy-button-sensitive-attributes', + 'attr__data-sensitive', + 'attr__id', + ]) +} test('Config options change autocapture behavior accordingly', async (t) => { - const testSessionId = await initPosthog({ mask_all_text: true, mask_all_element_attributes: true }) + const testSessionId = await initPosthog(t.testRun.test.name, { + mask_all_text: true, + mask_all_element_attributes: true, + }) await t .wait(5000) + .expect(isLoaded()) + .ok() .click('[data-cy-link-mask-text]') .click('[data-cy-button-sensitive-attributes]') - .wait(5000) + .wait(10000) + .expect(capturesMap()) + .contains({ + $pageview: 1, + $autocapture: 2, + }) .expect(captureLogger.count(() => true)) .gte(2) // Check no requests failed await t.expect(captureLogger.count(({ response }) => response.statusCode !== 200)).eql(0) - const results = await retryUntilResults(() => queryAPI(testSessionId), 3) + writeResultsJsonFile(t.testRun.test.name, testSessionId, assertConfigOptionsChangeAutocaptureBehaviourAccordingly) +}) + +export async function assertConfigOptionsChangeAutocaptureBehaviourAccordingly(testSessionId, deadline) { + const results = await retryUntilResults(() => queryAPI(testSessionId), 3, { + deadline, + }) const autocapturedEvents = results.filter((e) => e.event === '$autocapture') - await t.expect(autocapturedEvents.length).eql(2) + await expect(autocapturedEvents.length).toEqual(2) const autocapturedLinkElement = autocapturedEvents.filter((e) => e.elements[0].tag_name === 'a')[0].elements[0] const autocapturedButtonElement = autocapturedEvents.filter((e) => e.elements[0].tag_name === 'button')[0] .elements[0] // mask_all_text does not set $el_text - await t.expect(autocapturedLinkElement['text']).eql(null) + await expect(autocapturedLinkElement['text']).toEqual(null) // mask_all_element_attributes does not capture any attributes at all from all elements - await t.expect(Object.keys(autocapturedButtonElement.attributes).length).eql(0) -}) + await expect(Object.keys(autocapturedButtonElement.attributes).length).toEqual(0) +} diff --git a/testcafe/helpers.js b/testcafe/helpers.js index 9dff4711e..e321dc21c 100644 --- a/testcafe/helpers.js +++ b/testcafe/helpers.js @@ -1,17 +1,21 @@ -import fs from 'fs' +import * as fs from 'fs' import path from 'path' -import { RequestLogger, RequestMock, ClientFunction } from 'testcafe' +import { ClientFunction, RequestLogger, RequestMock } from 'testcafe' import fetch from 'node-fetch' // NOTE: These tests are run against a dedicated test project in PostHog cloud // but can be overridden to call a local API when running locally +// User admin for the test project: https://us.posthog.com/admin/posthog/organization/0182397e-3df4-0000-52e3-d890b5a16955/change/ // eslint-disable-next-line no-undef const currentEnv = process.env -const { +export const { POSTHOG_PROJECT_KEY, POSTHOG_API_KEY, - POSTHOG_API_HOST = 'https://app.posthog.com', + POSTHOG_API_HOST = 'https://us.i.posthog.com', POSTHOG_API_PROJECT = '11213', + BRANCH_NAME, + RUN_ID, + BROWSER, } = currentEnv const HEADERS = { Authorization: `Bearer ${POSTHOG_API_KEY}` } @@ -39,18 +43,13 @@ export const staticFilesMock = RequestMock() res.setBody(html) }) -export const initPosthog = (config) => { - return ClientFunction((configParams = {}) => { - const testSessionId = Math.round(Math.random() * 10000000000).toString() - configParams.debug = true - window.posthog.init(configParams.api_key, configParams) - window.posthog.register({ - testSessionId, - }) +export const initPosthog = (testName, config) => { + let testSessionId = Math.round(Math.random() * 10000000000).toString() + log(`Initializing posthog with testSessionId "${testSessionId}"`) - return testSessionId - })({ + const posthogConfig = { ...config, + debug: true, api_host: POSTHOG_API_HOST, api_key: POSTHOG_PROJECT_KEY, bootstrap: { @@ -58,39 +57,108 @@ export const initPosthog = (config) => { isIdentifiedID: true, }, opt_out_useragent_filter: true, - }) + request_batching: false, + } + + const register = { + testSessionId, + testName, + testBranchName: BRANCH_NAME, + testRunId: RUN_ID, + testBrowser: BROWSER, + } + + return ClientFunction( + (clientPosthogConfig = {}) => { + clientPosthogConfig.loaded = () => { + window.loaded = true + window.fullCaptures = [] + } + clientPosthogConfig._onCapture = (_, event) => { + window.fullCaptures.push(event) + } + window.posthog.init(clientPosthogConfig.api_key, clientPosthogConfig) + window.posthog.register(register) + + return testSessionId + }, + { + dependencies: { + register, + testSessionId, + }, + } + )(posthogConfig) } -// NOTE: Ingestion delays events by up to 60 seconds for new IDs hence we need to wait at least 60 seconds -// This is annoying but essentially we are allowing up to 3 minutes for the test to complete -export async function retryUntilResults(operation, target_results, limit = 18, delay = 15000) { - const attempt = (count, resolve, reject) => { - if (count === limit) { - return reject(new Error(`Failed to fetch results in ${limit} attempts`)) +export const isLoaded = ClientFunction(() => !!window.loaded) +export const numCaptures = ClientFunction(() => window.captures.length) + +export const capturesMap = ClientFunction(() => { + const map = {} + window.fullCaptures.forEach((capture) => { + const eventName = capture.event + if (!map[eventName]) { + map[eventName] = 0 } + map[eventName]++ + }) + return map +}) - setTimeout(() => { - operation() - .then((results) => { - if (results.length >= target_results) { - resolve(results) - } else { - // eslint-disable-next-line no-console - console.log(`Expected ${target_results} results, got ${results.length} (attempt ${count})`) - attempt(count + 1, resolve, reject) - } - }) - .catch(reject) - }, delay) - } +// test code, doesn't need to be IE11 compatible +// eslint-disable-next-line compat/compat +export const delay = (ms) => new Promise((resolve) => setTimeout(resolve, ms)) + +// NOTE: This is limited by the real production ingestion lag, which you can see in grafana is usually +// in the low minutes https://grafana.prod-us.posthog.dev/d/homepage/homepage +// This means that this test can fail if the ingestion lag is higher than the timeout, so we're pretty +// generous with the timeout here. +export async function retryUntilResults( + operation, + target_results, + { deadline = undefined, polling_interval_seconds = 30, max_allowed_api_errors = 5 } = {} +) { + const start = Date.now() + deadline = deadline ?? start + 10 * 60 * 1000 // default to 10 minutes + let api_errors = 0 + let attempts = 0 + let last_api_error = null + let elapsedSeconds = 0 - // new Promise isn't supported in IE11, but we don't care in these tests - // eslint-disable-next-line compat/compat - return new Promise((...args) => attempt(0, ...args)) + do { + attempts++ + let results + try { + results = await operation() + } catch (err) { + api_errors++ + last_api_error = err + error('API Error:', err) + } + if (results) { + elapsedSeconds = Math.floor((Date.now() - start) / 1000) + if (results.length >= target_results) { + log( + `Got correct number of results (${target_results}) after ${elapsedSeconds} seconds (attempt ${attempts})` + ) + return results + } else { + log(`Expected ${target_results} results, got ${results.length} (attempt ${attempts})`) + } + } + await delay(polling_interval_seconds * 1000) + } while (api_errors < max_allowed_api_errors && Date.now() <= deadline) + + if (api_errors >= max_allowed_api_errors && last_api_error) { + throw last_api_error + } + throw new Error(`Timed out after ${elapsedSeconds} seconds (attempt ${attempts})`) } export async function queryAPI(testSessionId) { - const url = `${POSTHOG_API_HOST}/api/projects/${POSTHOG_API_PROJECT}/events?properties=[{"key":"testSessionId","value":["${testSessionId}"],"operator":"exact","type":"event"}]` + const yesterday = new Date(Date.now() - 24 * 60 * 60 * 1000).toISOString() + const url = `${POSTHOG_API_HOST}/api/projects/${POSTHOG_API_PROJECT}/events?properties=[{"key":"testSessionId","value":["${testSessionId}"],"operator":"exact","type":"event"}]&after=${yesterday}` const response = await fetch(url, { headers: HEADERS, }) @@ -98,11 +166,40 @@ export async function queryAPI(testSessionId) { const data = await response.text() if (!response.ok) { - // eslint-disable-next-line no-console - console.error('Bad Response', response.status, data) + error('Bad Response', response.status, data) throw new Error('Bad Response') } const { results } = JSON.parse(data) return results } + +export function log(...args) { + // eslint-disable-next-line no-console + console.log(new Date().toISOString(), ...args) +} + +export function error(...args) { + // eslint-disable-next-line no-console + console.error(new Date().toISOString(), ...args) +} + +export function santizeTestName(testName) { + return `${testName.replaceAll(/[/ ]/g, '_')}.results.json` +} + +export function writeResultsJsonFile(testName, testSessionId, assertFunction) { + fs.writeFileSync( + path.join(__dirname, `${santizeTestName(testName)}.results.json`), + JSON.stringify({ testSessionId, assert: assertFunction.name }) + ) +} +export function getResultsJsonFiles() { + return fs + .readdirSync(__dirname) + .filter((file) => file.endsWith('.results.json')) + .map((file) => { + const data = fs.readFileSync(path.join(__dirname, file)) + return JSON.parse(data.toString()) + }) +} diff --git a/testcafe/tsconfig.json b/testcafe/tsconfig.json new file mode 100644 index 000000000..b13dfae2a --- /dev/null +++ b/testcafe/tsconfig.json @@ -0,0 +1,10 @@ +{ + "extends": "../tsconfig.json", + "compilerOptions": { + "target": "esnext", + "module": "commonjs", + "checkJs": false, + "skipLibCheck": true + }, + "include": ["./**/*"] +}