diff --git a/.env.githubci b/.env.githubci index 16cc268ef..57acd31df 100644 --- a/.env.githubci +++ b/.env.githubci @@ -50,7 +50,7 @@ SCYLLA_HAS_ENTERPRISE_FEATURES='false' GRAPHQL_MAX_DEPTH=10 EXPOSE_SENSITIVE_IMPLEMENTATION_DETAILS_IN_ERRORS=true ALLOW_USER_INPUT_LOCALHOST_URIS=true -DATABASE_PRINT_LOGS=true +DATABASE_PRINT_LOGS=false GROQ_SECRET_KEY= diff --git a/.github/workflows/apply_pr_checks.yaml b/.github/workflows/apply_pr_checks.yaml index 0ae20b88d..748773710 100644 --- a/.github/workflows/apply_pr_checks.yaml +++ b/.github/workflows/apply_pr_checks.yaml @@ -277,6 +277,9 @@ jobs: - name: Lint client run: docker compose run --rm --quiet-pull client npm run lint + - name: Test client + run: docker compose run --rm --quiet-pull client npm run test:prepush + - name: Build client run: docker compose run --rm --quiet-pull client npm run build diff --git a/.github/workflows/e2e.yaml b/.github/workflows/e2e.yaml index 0daab5af4..5211d630d 100644 --- a/.github/workflows/e2e.yaml +++ b/.github/workflows/e2e.yaml @@ -82,6 +82,7 @@ jobs: cp server/.env.example server/.env cp db/.env.example db/.env cp client/.env.example client/.env + sed -i "s/^ITEM_QUEUE_TRAFFIC_PERCENTAGE=.*/ITEM_QUEUE_TRAFFIC_PERCENTAGE='1'/" server/.env - name: Install dependencies run: | @@ -144,7 +145,7 @@ jobs: PLAYWRIGHT_BASE_URL: http://localhost:3000 - name: Upload Playwright report - if: ${{ !cancelled() }} + if: ${{ failure() }} uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5.0.0 with: name: playwright-report diff --git a/.github/workflows/zizmor.yml b/.github/workflows/zizmor.yml index 792fc9b80..13ba49c35 100644 --- a/.github/workflows/zizmor.yml +++ b/.github/workflows/zizmor.yml @@ -3,8 +3,10 @@ name: GitHub Actions security analysis on: push: branches: ['main'] + paths: ['.github/workflows/**'] pull_request: branches: ['**'] + paths: ['.github/workflows/**'] concurrency: group: zizmor-${{ github.ref }} diff --git a/.nvmrc b/.nvmrc index b832e4001..ca5c35005 100644 --- a/.nvmrc +++ b/.nvmrc @@ -1 +1 @@ -24.16.0 +24.18.0 diff --git a/AGENTS.md b/AGENTS.md index ea82166a6..c3906e604 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -200,6 +200,8 @@ Two things differ from a local dev setup: ## Human-approval-required actions +Routine local setup and verification commands, including `npm ci` and existing build/test/lint/format/check scripts, do not require approval; the gates below apply to the changes being made, not merely to running commands. + Stop and get explicit human approval before: - Changing license headers, copyright notices, or any legal text (including `LICENSE`). @@ -208,7 +210,7 @@ Stop and get explicit human approval before: - Deleting or renaming an existing GraphQL type or field — this breaks cached Apollo client state and any downstream consumer. Additive changes are usually safe; removals need a migration plan. - Rewiring `server/iocContainer` in a way that changes service lifecycles or startup order — cascading effects on tests and boot. - Auth, session, or request middleware (under `server/api.ts`) — security-sensitive; prefer a small, reviewable PR with explicit callouts. -- Adding, removing, or upgrading any library or package (including transitive dependencies in `package-lock.json`) — confirm licenses are compatible with Apache 2.0 and that there are no known CVEs. +- Adding, removing, or upgrading any dependency (including transitive dependencies in `package-lock.json`) — confirm licenses are compatible with Apache 2.0 and that there are no known CVEs. - Multi-thousand-line diffs — ROOST policy is that reviewers can digest the change. Split into reviewable PRs; regenerated codegen and lockfile bumps are the only exceptions. ## Commit attribution diff --git a/Dockerfile b/Dockerfile index 9ee88eaac..0395f1e53 100644 --- a/Dockerfile +++ b/Dockerfile @@ -7,7 +7,7 @@ # Docker's cache will let us skip installs when the dependencies haven't changed. # We build on debian because it has fewer dependency issues than Alpine for our # native modules, and we don't really care about the larger image size. -FROM node:24.14.1-bullseye-slim AS server_base +FROM node:24.18.0-bullseye-slim AS server_base WORKDIR /app RUN apt-get update && apt-get install -y --no-install-recommends git && rm -rf /var/lib/apt/lists/* @@ -19,7 +19,7 @@ FROM server_base AS build_backend RUN npm run build # make a shared layer that can be the base for worker and api images. -FROM node:24.14.1-bullseye-slim AS backend_base +FROM node:24.18.0-bullseye-slim AS backend_base WORKDIR /app RUN apt-get update && apt-get install dumb-init COPY --from=build_backend ["/app/package.json", "/app/package-lock.json", "./"] diff --git a/client/.dockerignore b/client/.dockerignore index 3246d9258..e898e62dc 100644 --- a/client/.dockerignore +++ b/client/.dockerignore @@ -10,3 +10,4 @@ !tailwind.config.js !tsconfig.json !nginx.conf +!tsconfig.test.json diff --git a/client/.eslintrc.cjs b/client/.eslintrc.cjs index e4993e921..f99eb067b 100644 --- a/client/.eslintrc.cjs +++ b/client/.eslintrc.cjs @@ -23,7 +23,7 @@ module.exports = { extends: ['plugin:react/recommended', 'plugin:react-hooks/recommended'], parser: '@typescript-eslint/parser', parserOptions: { - project: ['./tsconfig.json'], + project: ['./tsconfig.json', './tsconfig.test.json'], tsconfigRootDir: __dirname, }, ignorePatterns: [ diff --git a/client/Dockerfile b/client/Dockerfile index 6e4425520..dcfff602c 100644 --- a/client/Dockerfile +++ b/client/Dockerfile @@ -1,4 +1,4 @@ -FROM node:24.14.1-bullseye-slim AS client_base +FROM node:24.18.0-bullseye-slim AS client_base WORKDIR /app # ARG is used to get the release id into the ENV from the command line, and then @@ -20,7 +20,7 @@ ARG VITE_OTEL_EXPORTER_OTLP_TRACES_ENDPOINT ENV DISABLE_ESLINT_PLUGIN=true RUN NODE_OPTIONS="--max-old-space-size=5250" VITE_OTEL_EXPORTER_OTLP_TRACES_ENDPOINT=$VITE_OTEL_EXPORTER_OTLP_TRACES_ENDPOINT npm run build -FROM nginx:1.27-bookworm AS serve +FROM nginx:1.29.1-bookworm AS serve COPY --from=build /app/build /usr/share/nginx/html COPY nginx.conf /etc/nginx/conf.d/default.conf EXPOSE 80 diff --git a/client/eslint/__tests__/customRules.test.js b/client/eslint/__tests__/customRules.test.js index bdd26c4c6..7b8f1bd7f 100644 --- a/client/eslint/__tests__/customRules.test.js +++ b/client/eslint/__tests__/customRules.test.js @@ -1,22 +1,24 @@ -const { Linter } = require('eslint'); -const rule = require('../no-casting-in-getFieldValueForRole'); +import tsParser from '@typescript-eslint/parser'; +import { Linter } from 'eslint'; -const linter = new Linter(); +import rule from '../no-casting-in-getFieldValueForRole.js'; + +const linter = new Linter({ configType: 'flat' }); const runLint = (code) => { - const messages = linter.verify(code, { - plugins: { - custom: { - rules: { - 'no-casting-in-getFieldValueForRole': rule, - }, + const messages = linter.verify(code, [ + { + plugins: { + local: { rules: { 'no-casting-in-getFieldValueForRole': rule } }, + }, + rules: { 'local/no-casting-in-getFieldValueForRole': 'error' }, + languageOptions: { + parser: tsParser, + ecmaVersion: 2015, + sourceType: 'module', }, }, - rules: { - 'custom/no-casting-in-getFieldValueForRole': 'error', - }, - languageOptions: { ecmaVersion: 2015, sourceType: 'module' }, - }); + ]); return messages; }; diff --git a/client/package-lock.json b/client/package-lock.json index dc54525be..05b4af2a3 100644 --- a/client/package-lock.json +++ b/client/package-lock.json @@ -66,44 +66,44 @@ "web-vitals": "^1.0.1" }, "devDependencies": { - "@eslint/compat": "^2.0.3", - "@eslint/eslintrc": "^3.3.5", + "@eslint/compat": "^2.1.0", + "@eslint/eslintrc": "^3.3.6", "@faker-js/faker": "^8.4.1", "@storybook/react": "^9.1.20", "@storybook/react-vite": "^9.1.20", - "@testing-library/jest-dom": "^5.11.9", + "@testing-library/jest-dom": "^5.17.0", "@testing-library/react": "^11.2.3", "@testing-library/user-event": "^12.6.0", - "@types/google.maps": "^3.48.7", - "@types/latlon-geohash": "^2.0.0", - "@types/lodash": "^4.14.181", + "@types/google.maps": "^3.65.2", + "@types/latlon-geohash": "^2.0.4", + "@types/lodash": "^4.17.24", "@types/react": "^18.0.0", - "@types/react-beautiful-dnd": "^13.1.4", - "@types/react-csv": "^1.1.3", + "@types/react-beautiful-dnd": "^13.1.8", + "@types/react-csv": "^1.1.10", "@types/react-dom": "^18.0.0", "@types/react-syntax-highlighter": "^13.5.2", - "@types/react-table": "^7.7.5", - "@typescript-eslint/eslint-plugin": "^8.57.2", + "@types/react-table": "^7.7.20", + "@typescript-eslint/eslint-plugin": "^8.64.0", "@typescript-eslint/parser": "^8.57.2", - "@vitejs/plugin-react": "^5.1.4", - "autoprefixer": "^10.4.7", - "eslint": "^9.39.4", + "@vitejs/plugin-react": "^5.2.0", + "autoprefixer": "^10.5.4", + "eslint": "^9.39.5", "eslint-plugin-custom-rules": "file:./eslint", "eslint-plugin-react": "^7.30.1", - "eslint-plugin-react-hooks": "^7.0.1", + "eslint-plugin-react-hooks": "^7.1.1", "jsdom": "^29.1.1", - "knip": "^6.16.1", - "postcss": "^8.5.10", + "knip": "^6.27.0", + "postcss": "^8.5.23", "prop-types": "^15.8.1", "source-map-explorer": "^2.5.3", "storybook": "^9.1.20", "tailwindcss": "^3.4.19", - "typescript": "^5.3.2", - "vite": "^7.3.5", + "typescript": "^5.9.3", + "vite": "^7.3.6", "vite-plugin-commonjs": "^0.10.4", "vite-plugin-svgr": "^4.5.0", "vite-tsconfig-paths": "^6.1.1", - "vitest": "^4.1.0" + "vitest": "^4.1.10" } }, "eslint": { @@ -270,13 +270,13 @@ "license": "MIT" }, "node_modules/@babel/code-frame": { - "version": "7.29.0", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.29.0.tgz", - "integrity": "sha512-9NhCeYjq9+3uxgdtp20LSiJXJvN0FeCtNGpJxuMFZ1Kv3cWUNb6DOhJwUvcVCzKGR66cw4njwM6hrJLqgOwbcw==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.29.7.tgz", + "integrity": "sha512-Aup7aUOfpbAUg2ROOJN6Iw5f9DMBlzu0mIkm/malLQFN/YQgO48wCj0Kxa3sEHJvPVFg7siR+qRInwXd2qhQKw==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-validator-identifier": "^7.28.5", + "@babel/helper-validator-identifier": "^7.29.7", "js-tokens": "^4.0.0", "picocolors": "^1.1.1" }, @@ -285,9 +285,9 @@ } }, "node_modules/@babel/compat-data": { - "version": "7.29.0", - "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.29.0.tgz", - "integrity": "sha512-T1NCJqT/j9+cn8fvkt7jtwbLBfLC/1y1c7NtCeXFRgzGTsafi68MRv8yzkYSapBnFA6L3U2VSc02ciDzoAJhJg==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.29.7.tgz", + "integrity": "sha512-locTkQyKvwIEgBzVrn8693ebc97F2U8ZHjbXwDXJ5Fn2TCpNwTlKcaKLkdHop5c/icOFE7qt7Q9JC5hnKNa6Gg==", "dev": true, "license": "MIT", "engines": { @@ -295,21 +295,21 @@ } }, "node_modules/@babel/core": { - "version": "7.29.0", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.29.0.tgz", - "integrity": "sha512-CGOfOJqWjg2qW/Mb6zNsDm+u5vFQ8DxXfbM09z69p5Z6+mE1ikP2jUXw+j42Pf1XTYED2Rni5f95npYeuwMDQA==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.29.7.tgz", + "integrity": "sha512-RgHBCvtjbOK2gXSNBNIkNoEc9qoVEtau3hj8gEqKQuL3HZAibKarWFEI3Lfm6EYKkLalOh8eSrj9b+ch9H/VBA==", "dev": true, "license": "MIT", "dependencies": { - "@babel/code-frame": "^7.29.0", - "@babel/generator": "^7.29.0", - "@babel/helper-compilation-targets": "^7.28.6", - "@babel/helper-module-transforms": "^7.28.6", - "@babel/helpers": "^7.28.6", - "@babel/parser": "^7.29.0", - "@babel/template": "^7.28.6", - "@babel/traverse": "^7.29.0", - "@babel/types": "^7.29.0", + "@babel/code-frame": "^7.29.7", + "@babel/generator": "^7.29.7", + "@babel/helper-compilation-targets": "^7.29.7", + "@babel/helper-module-transforms": "^7.29.7", + "@babel/helpers": "^7.29.7", + "@babel/parser": "^7.29.7", + "@babel/template": "^7.29.7", + "@babel/traverse": "^7.29.7", + "@babel/types": "^7.29.7", "@jridgewell/remapping": "^2.3.5", "convert-source-map": "^2.0.0", "debug": "^4.1.0", @@ -341,14 +341,14 @@ } }, "node_modules/@babel/generator": { - "version": "7.29.1", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.29.1.tgz", - "integrity": "sha512-qsaF+9Qcm2Qv8SRIMMscAvG4O3lJ0F1GuMo5HR/Bp02LopNgnZBC/EkbevHFeGs4ls/oPz9v+Bsmzbkbe+0dUw==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.29.7.tgz", + "integrity": "sha512-DkXD5OJQaAQIdZ1bt3UZdEnHAn9Imd3IVBdX03UFe+ony9Ojw5pzr9YVKGDY1jt+Gcn/FnGkNf8r+Vj5NOJWtQ==", "dev": true, "license": "MIT", "dependencies": { - "@babel/parser": "^7.29.0", - "@babel/types": "^7.29.0", + "@babel/parser": "^7.29.7", + "@babel/types": "^7.29.7", "@jridgewell/gen-mapping": "^0.3.12", "@jridgewell/trace-mapping": "^0.3.28", "jsesc": "^3.0.2" @@ -358,14 +358,14 @@ } }, "node_modules/@babel/helper-compilation-targets": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.28.6.tgz", - "integrity": "sha512-JYtls3hqi15fcx5GaSNL7SCTJ2MNmjrkHXg4FSpOA/grxK8KwyZ5bubHsCq8FXCkua6xhuaaBit+3b7+VZRfcA==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.29.7.tgz", + "integrity": "sha512-wem6WaBj4NaVYVdNhLPPVacES6ZJ+KBBfSkTMD3YZxbP3rm3Di85tJU5ljaUNhaOynt+Aj0xruhYuzQBt8n71g==", "dev": true, "license": "MIT", "dependencies": { - "@babel/compat-data": "^7.28.6", - "@babel/helper-validator-option": "^7.27.1", + "@babel/compat-data": "^7.29.7", + "@babel/helper-validator-option": "^7.29.7", "browserslist": "^4.24.0", "lru-cache": "^5.1.1", "semver": "^6.3.1" @@ -379,14 +379,15 @@ "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", "dev": true, + "license": "ISC", "bin": { "semver": "bin/semver.js" } }, "node_modules/@babel/helper-globals": { - "version": "7.28.0", - "resolved": "https://registry.npmjs.org/@babel/helper-globals/-/helper-globals-7.28.0.tgz", - "integrity": "sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-globals/-/helper-globals-7.29.7.tgz", + "integrity": "sha512-3nQVUAtvkKH9zahfWgw96Jc/uFOmjACE1kQz82E2lqWmHBgjzbNlsC22nuQTfahmWeQtTq5nQ/4Nnd2A1wj4zA==", "dev": true, "license": "MIT", "engines": { @@ -394,29 +395,29 @@ } }, "node_modules/@babel/helper-module-imports": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.28.6.tgz", - "integrity": "sha512-l5XkZK7r7wa9LucGw9LwZyyCUscb4x37JWTPz7swwFE/0FMQAGpiWUZn8u9DzkSBWEcK25jmvubfpw2dnAMdbw==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.29.7.tgz", + "integrity": "sha512-ejHwrQQYcm9xnTivShn2IDOlIzInN34AXskvq9QicvCtEzq1Vzclu/tKF8Jq1Cg8JG2GL6/EmjgsCT7lXepE3g==", "dev": true, "license": "MIT", "dependencies": { - "@babel/traverse": "^7.28.6", - "@babel/types": "^7.28.6" + "@babel/traverse": "^7.29.7", + "@babel/types": "^7.29.7" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-module-transforms": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.28.6.tgz", - "integrity": "sha512-67oXFAYr2cDLDVGLXTEABjdBJZ6drElUSI7WKp70NrpyISso3plG9SAGEF6y7zbha/wOzUByWWTJvEDVNIUGcA==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.29.7.tgz", + "integrity": "sha512-UPUVSyXbOh627KiCIGQSgwWzGeBKLkaJ9PJEdrngIwMSzxLR4jS4+f1f1jb7VzBbg8nFLaYotvVPFCTqdrmTAg==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-module-imports": "^7.28.6", - "@babel/helper-validator-identifier": "^7.28.5", - "@babel/traverse": "^7.28.6" + "@babel/helper-module-imports": "^7.29.7", + "@babel/helper-validator-identifier": "^7.29.7", + "@babel/traverse": "^7.29.7" }, "engines": { "node": ">=6.9.0" @@ -436,9 +437,9 @@ } }, "node_modules/@babel/helper-string-parser": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz", - "integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.29.7.tgz", + "integrity": "sha512-Pb5ijPrZ89GDH8223L4UP8i6QApWxs04RbPQJTeWDV0/keR2E36MeKnyr6LYmUUvqRRI+Iv87SuF1W6ErINzYw==", "dev": true, "license": "MIT", "engines": { @@ -446,9 +447,9 @@ } }, "node_modules/@babel/helper-validator-identifier": { - "version": "7.28.5", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.28.5.tgz", - "integrity": "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.29.7.tgz", + "integrity": "sha512-qehxGkRj55h/ff8EMaJ+cYhyaKlHIxqYDn682wQD7RNp9UujOQsHog2uS0r2vzr4pW+sXf90NeeayjcNaX3fFg==", "dev": true, "license": "MIT", "engines": { @@ -456,9 +457,9 @@ } }, "node_modules/@babel/helper-validator-option": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.27.1.tgz", - "integrity": "sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.29.7.tgz", + "integrity": "sha512-N9ZErrD+yW5geCDtBqnOoxmR8+tNKiGuxKlDpuJxfsqpa2dFcexaziGAE/qoHLiDDreVNMupxGmSoNlyvsA3gw==", "dev": true, "license": "MIT", "engines": { @@ -466,27 +467,27 @@ } }, "node_modules/@babel/helpers": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.28.6.tgz", - "integrity": "sha512-xOBvwq86HHdB7WUDTfKfT/Vuxh7gElQ+Sfti2Cy6yIWNW05P8iUslOVcZ4/sKbE+/jQaukQAdz/gf3724kYdqw==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.29.7.tgz", + "integrity": "sha512-1k2lAGRMfHTcwuNYcCNUmaUffmQv8KWMfh2iJUUeRlwlwH4FdNG7mfPI10NPfLHJFThE4Tyr4mv7kTNZOiPuBg==", "dev": true, "license": "MIT", "dependencies": { - "@babel/template": "^7.28.6", - "@babel/types": "^7.28.6" + "@babel/template": "^7.29.7", + "@babel/types": "^7.29.7" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/parser": { - "version": "7.29.0", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.29.0.tgz", - "integrity": "sha512-IyDgFV5GeDUVX4YdF/3CPULtVGSXXMLh1xVIgdCgxApktqnQV0r7/8Nqthg+8YLGaAtdyIlo2qIdZrbCv4+7ww==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.29.7.tgz", + "integrity": "sha512-hnORnjP/1P/zFEndoeX+n+t1RwWRJiJpM/jO7FW32Kn9r5+sJB2JWOdYo4L6k78j15eCwY3Gm/7364B1EMwtNg==", "dev": true, "license": "MIT", "dependencies": { - "@babel/types": "^7.29.0" + "@babel/types": "^7.29.7" }, "bin": { "parser": "bin/babel-parser.js" @@ -550,33 +551,33 @@ } }, "node_modules/@babel/template": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.28.6.tgz", - "integrity": "sha512-YA6Ma2KsCdGb+WC6UpBVFJGXL58MDA6oyONbjyF/+5sBgxY/dwkhLogbMT2GXXyU84/IhRw/2D1Os1B/giz+BQ==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.29.7.tgz", + "integrity": "sha512-puq+Gf35oI24FeN11LkoUQFqv9uwNeWpxXZi/Ji3rRIoKAzKnxRaZ+Gkj0vKS9ZCiTESfng1N9LyOyXvo+m+Gg==", "dev": true, "license": "MIT", "dependencies": { - "@babel/code-frame": "^7.28.6", - "@babel/parser": "^7.28.6", - "@babel/types": "^7.28.6" + "@babel/code-frame": "^7.29.7", + "@babel/parser": "^7.29.7", + "@babel/types": "^7.29.7" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/traverse": { - "version": "7.29.0", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.29.0.tgz", - "integrity": "sha512-4HPiQr0X7+waHfyXPZpWPfWL/J7dcN1mx9gL6WdQVMbPnF3+ZhSMs8tCxN7oHddJE9fhNE7+lxdnlyemKfJRuA==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.29.7.tgz", + "integrity": "sha512-EhlfNQtZ+NK22w5BM61ciuiq1m58ed33Wr1Xan//ZRTy6hgjnwyCffRYwzsGXdASJSUJ1guZILsErh1eQcl+zw==", "dev": true, "license": "MIT", "dependencies": { - "@babel/code-frame": "^7.29.0", - "@babel/generator": "^7.29.0", - "@babel/helper-globals": "^7.28.0", - "@babel/parser": "^7.29.0", - "@babel/template": "^7.28.6", - "@babel/types": "^7.29.0", + "@babel/code-frame": "^7.29.7", + "@babel/generator": "^7.29.7", + "@babel/helper-globals": "^7.29.7", + "@babel/parser": "^7.29.7", + "@babel/template": "^7.29.7", + "@babel/types": "^7.29.7", "debug": "^4.3.1" }, "engines": { @@ -584,14 +585,14 @@ } }, "node_modules/@babel/types": { - "version": "7.29.0", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.29.0.tgz", - "integrity": "sha512-LwdZHpScM4Qz8Xw2iKSzS+cfglZzJGvofQICy7W7v4caru4EaAmyUuO6BGrbyQ2mYV11W0U8j5mBhd14dd3B0A==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.29.7.tgz", + "integrity": "sha512-4zBIxpPzowiZpusoFkyGVwakdRJUyuH5PxQ/PrqghfdFWWasvnCdPfQXHrenDai+gyLARulZjZowCOj6fjT4pA==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-string-parser": "^7.27.1", - "@babel/helper-validator-identifier": "^7.28.5" + "@babel/helper-string-parser": "^7.29.7", + "@babel/helper-validator-identifier": "^7.29.7" }, "engines": { "node": ">=6.9.0" @@ -759,21 +760,21 @@ } }, "node_modules/@emnapi/core": { - "version": "1.10.0", - "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.10.0.tgz", - "integrity": "sha512-yq6OkJ4p82CAfPl0u9mQebQHKPJkY7WrIuk205cTYnYe+k2Z8YBh11FrbRG/H6ihirqcacOgl2BIO8oyMQLeXw==", + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.11.1.tgz", + "integrity": "sha512-RSvbQmHzdKzNsLYa/wHrbc3KN4sYLKAdPZxqiM2HATqv/SBk2/ENSHpvXGaLOMcsAyz0poEGqkmmKYG3OWiJEQ==", "dev": true, "license": "MIT", "optional": true, "dependencies": { - "@emnapi/wasi-threads": "1.2.1", + "@emnapi/wasi-threads": "1.2.2", "tslib": "^2.4.0" } }, "node_modules/@emnapi/runtime": { - "version": "1.10.0", - "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.10.0.tgz", - "integrity": "sha512-ewvYlk86xUoGI0zQRNq/mC+16R1QeDlKQy21Ki3oSYXNgLb45GV1P6A0M+/s6nyCuNDqe5VpaY84BzXGwVbwFA==", + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.11.1.tgz", + "integrity": "sha512-vgj7R3y3Wgx24IQaGPA/R6YFXLHVMOZ0uVEyIQPaWs+rd1AzfEMXlAC22FYwO1XkKR6NPsq7mUandH8oIRdZFw==", "dev": true, "license": "MIT", "optional": true, @@ -782,9 +783,9 @@ } }, "node_modules/@emnapi/wasi-threads": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.2.1.tgz", - "integrity": "sha512-uTII7OYF+/Mes/MrcIOYp5yOtSMLBWSIoLPpcgwipoiKbli6k322tcoFsxoIIxPDqW01SQGAgko4EzZi2BNv2w==", + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.2.2.tgz", + "integrity": "sha512-c95qOXkHdydNKhscBTebqEC1CVAZpyqOfVfBzQ1qgzyl3gfeldUjIggDbIZgDKsHLgnsM+igH7TJ/eAasaVuMA==", "dev": true, "license": "MIT", "optional": true, @@ -1264,13 +1265,13 @@ } }, "node_modules/@eslint/compat": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/@eslint/compat/-/compat-2.0.3.tgz", - "integrity": "sha512-SjIJhGigp8hmd1YGIBwh7Ovri7Kisl42GYFjrOyHhtfYGGoLW6teYi/5p8W50KSsawUPpuLOSmsq1bD0NGQLBw==", + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@eslint/compat/-/compat-2.1.0.tgz", + "integrity": "sha512-LgaSCymEpw7tF53xvDw9SNsraPb1IBHxpdABIOM0hW8UAlP8znrjYtuxfR58FSJ3L9BhwD+FaPRFQpZq84Nh6g==", "dev": true, "license": "Apache-2.0", "dependencies": { - "@eslint/core": "^1.1.1" + "@eslint/core": "^1.2.1" }, "engines": { "node": "^20.19.0 || ^22.13.0 || >=24" @@ -1326,9 +1327,9 @@ } }, "node_modules/@eslint/core": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@eslint/core/-/core-1.1.1.tgz", - "integrity": "sha512-QUPblTtE51/7/Zhfv8BDwO0qkkzQL7P/aWWbqcf4xWLEYn1oKjdO0gglQBB4GAsu7u6wjijbCmzsUTy6mnk6oQ==", + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@eslint/core/-/core-1.2.1.tgz", + "integrity": "sha512-MwcE1P+AZ4C6DWlpin/OmOA54mmIZ/+xZuJiQd4SyB29oAJjN30UW9wkKNptW2ctp4cEsvhlLY/CsQ1uoHDloQ==", "dev": true, "license": "Apache-2.0", "dependencies": { @@ -1339,9 +1340,9 @@ } }, "node_modules/@eslint/eslintrc": { - "version": "3.3.5", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.3.5.tgz", - "integrity": "sha512-4IlJx0X0qftVsN5E+/vGujTRIFtwuLbNsVUe7TO6zYPDR1O6nFwvwhIKEKSrl6dZchmYBITazxKoUYOjdtjlRg==", + "version": "3.3.6", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.3.6.tgz", + "integrity": "sha512-l2Ul9PrHsPCKcEY/ac7VgFj9D80C7S68sOKc618SyHDPK36s1XcFebXY0iTzUVn4Yq+YbwvSnDmCz9yxjX+QrA==", "dev": true, "license": "MIT", "dependencies": { @@ -1351,7 +1352,7 @@ "globals": "^14.0.0", "ignore": "^5.2.0", "import-fresh": "^3.2.1", - "js-yaml": "^4.1.1", + "js-yaml": "^4.3.0", "minimatch": "^3.1.5", "strip-json-comments": "^3.1.1" }, @@ -1363,9 +1364,9 @@ } }, "node_modules/@eslint/js": { - "version": "9.39.4", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.39.4.tgz", - "integrity": "sha512-nE7DEIchvtiFTwBw4Lfbu59PG+kCofhjsKaCWzxTpt4lfRjRMqG6uMBzKXuEcyXhOHoUp9riAm7/aWYGhXZ9cw==", + "version": "9.39.5", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.39.5.tgz", + "integrity": "sha512-QywQuszQh77pIXCsq998c8hbhSTI/azTty1Z6N53dmAudKHhy573j3yvRLsX2BSp8YpLtoCEG8E9DJe+8zUh4A==", "dev": true, "license": "MIT", "engines": { @@ -1708,9 +1709,9 @@ } }, "node_modules/@joshwooding/vite-plugin-react-docgen-typescript/node_modules/brace-expansion": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.3.tgz", - "integrity": "sha512-MCV/fYJEbqx68aE58kv2cA/kiky1G8vux3OR6/jbS+jIMe/6fJWa0DTzJU7dqijOWYwHi1t29FlfYI9uytqlpA==", + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.1.3.tgz", + "integrity": "sha512-DRdx5neNsG/QXbniLFWi2YmC/68oeOOmKz6zOjVk6ZS1ZLXgLIKqVEc6hWsmkjBbgii0SwaBTcJ5XKj5gzY/4A==", "dev": true, "license": "MIT", "dependencies": { @@ -1822,14 +1823,14 @@ } }, "node_modules/@napi-rs/wasm-runtime": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-1.1.4.tgz", - "integrity": "sha512-3NQNNgA1YSlJb/kMH1ildASP9HW7/7kYnRI2szWJaofaS1hWmbGI4H+d3+22aGzXXN9IJ+n+GiFVcGipJP18ow==", + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-1.1.6.tgz", + "integrity": "sha512-ZLv/JdUfkvOy9eCnnBaGfiO+XimbjebAeO+MRQqD/B+FR1tnRN0tpKSJHRbE8sFfS6aqsXZ67TQjfwfsxULVbg==", "dev": true, "license": "MIT", "optional": true, "dependencies": { - "@tybys/wasm-util": "^0.10.1" + "@tybys/wasm-util": "^0.10.3" }, "funding": { "type": "github", @@ -2202,9 +2203,9 @@ } }, "node_modules/@oxc-parser/binding-android-arm-eabi": { - "version": "0.133.0", - "resolved": "https://registry.npmjs.org/@oxc-parser/binding-android-arm-eabi/-/binding-android-arm-eabi-0.133.0.tgz", - "integrity": "sha512-l/44caGse+VpnY9gx0yvvc5QnnG3yG1FO3KZgYvNL1GZrfK86zIwAOgGEVlxDyRymzrU/KHiblPFpevKOmJmUA==", + "version": "0.137.0", + "resolved": "https://registry.npmjs.org/@oxc-parser/binding-android-arm-eabi/-/binding-android-arm-eabi-0.137.0.tgz", + "integrity": "sha512-KDs+0VPdEmasOkpuJHW9V5WCF+cvYdMQv2Jd+aJXt+cxIx12NToRQRbXaRwUEDsZw+/jMk81Ve8ZFbjUkJTOwA==", "cpu": [ "arm" ], @@ -2219,9 +2220,9 @@ } }, "node_modules/@oxc-parser/binding-android-arm64": { - "version": "0.133.0", - "resolved": "https://registry.npmjs.org/@oxc-parser/binding-android-arm64/-/binding-android-arm64-0.133.0.tgz", - "integrity": "sha512-KUHmPMziLBp4u+zbrLdB7iWS7KshuZe+RAp7ELnY9SI9nNXBZ+dp8fiBqWOxhXqn+FQg3a4UcQhwmsJOKV8Jjg==", + "version": "0.137.0", + "resolved": "https://registry.npmjs.org/@oxc-parser/binding-android-arm64/-/binding-android-arm64-0.137.0.tgz", + "integrity": "sha512-WhALNzfy3x/RfC6bsqX+csavuUY0yHHE7XfgPE5M542uhoBZUUoGTPG+nkMbGoG4+gcfss5s7urMyn5QBHu0sw==", "cpu": [ "arm64" ], @@ -2236,9 +2237,9 @@ } }, "node_modules/@oxc-parser/binding-darwin-arm64": { - "version": "0.133.0", - "resolved": "https://registry.npmjs.org/@oxc-parser/binding-darwin-arm64/-/binding-darwin-arm64-0.133.0.tgz", - "integrity": "sha512-q8dWmnU/8ea2tga9w2f1PinQ5rcMPDUGkF64T189b65YMjUomET4oy5oRldOr4AwOQkneOG/Zttnz1Dvrc62wg==", + "version": "0.137.0", + "resolved": "https://registry.npmjs.org/@oxc-parser/binding-darwin-arm64/-/binding-darwin-arm64-0.137.0.tgz", + "integrity": "sha512-bFPr5hgmNMOMoyPTGtdsK4Ug21RovIPojRMgDDhSp1LtCnc/DkLwGONKjgRjszg677RlGnkYSviQ8hHaUPOVYA==", "cpu": [ "arm64" ], @@ -2253,9 +2254,9 @@ } }, "node_modules/@oxc-parser/binding-darwin-x64": { - "version": "0.133.0", - "resolved": "https://registry.npmjs.org/@oxc-parser/binding-darwin-x64/-/binding-darwin-x64-0.133.0.tgz", - "integrity": "sha512-cOKeIELIB2bJnCKwqx4Rdj+1Lss/U6uCbLxRySZrhyOOQa1flKhwZFjEHRHxk8fU1NKmhK5OnTdPQ4CpjuFuVw==", + "version": "0.137.0", + "resolved": "https://registry.npmjs.org/@oxc-parser/binding-darwin-x64/-/binding-darwin-x64-0.137.0.tgz", + "integrity": "sha512-CL5dMm1asqXIDZHg14FLxj3Mc36w8PI7xCWh1uA4is6z8g2XrIILoTcQYOxDbwzuk34RDPX5IAGUxZr6LA9KAg==", "cpu": [ "x64" ], @@ -2270,9 +2271,9 @@ } }, "node_modules/@oxc-parser/binding-freebsd-x64": { - "version": "0.133.0", - "resolved": "https://registry.npmjs.org/@oxc-parser/binding-freebsd-x64/-/binding-freebsd-x64-0.133.0.tgz", - "integrity": "sha512-OpaSv4pW3KgFrMYQxTaS0aOE4T1DQF3qZE/4B6uqqv1KgPWWd4UQhJALi8PJPX1RRV5K7ThKXRfF7qGg2+3l1A==", + "version": "0.137.0", + "resolved": "https://registry.npmjs.org/@oxc-parser/binding-freebsd-x64/-/binding-freebsd-x64-0.137.0.tgz", + "integrity": "sha512-79h8rYGnSlKPGWo7mHr2ixO6ea7aW8B0CT965SZ8SLbNnCOH5aOYBTeVXUY6eMvEaiLyWr8Skuiugr5pDYgLGw==", "cpu": [ "x64" ], @@ -2287,9 +2288,9 @@ } }, "node_modules/@oxc-parser/binding-linux-arm-gnueabihf": { - "version": "0.133.0", - "resolved": "https://registry.npmjs.org/@oxc-parser/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-0.133.0.tgz", - "integrity": "sha512-JGK1wlGrGwxBIlVSF7KWTX1/ru6BEtf28fRROztDRkLfiW+Kxa4onnriezMIiogfn9hVw2KzYcKiLjkLR2ns8A==", + "version": "0.137.0", + "resolved": "https://registry.npmjs.org/@oxc-parser/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-0.137.0.tgz", + "integrity": "sha512-ASgmlSimhGyr0lksgVIo6hibz1obnDq4qJbiMX/AzltfgPnanRrzG1Q+23g8ljOHOjv6dsznkUuCYL3gg0sY1Q==", "cpu": [ "arm" ], @@ -2304,9 +2305,9 @@ } }, "node_modules/@oxc-parser/binding-linux-arm-musleabihf": { - "version": "0.133.0", - "resolved": "https://registry.npmjs.org/@oxc-parser/binding-linux-arm-musleabihf/-/binding-linux-arm-musleabihf-0.133.0.tgz", - "integrity": "sha512-yuZO533Ftonxn/iyoqQzURzLQHMspvsIyfiCSNi1t/ER4eIQaR0SsmUOUm5b/lmSig7IWIUa5/BrbEkAPwcilQ==", + "version": "0.137.0", + "resolved": "https://registry.npmjs.org/@oxc-parser/binding-linux-arm-musleabihf/-/binding-linux-arm-musleabihf-0.137.0.tgz", + "integrity": "sha512-AU2J9aa22Sx32wRGnDjybOU9TQXXQUud5sdUi+ZB0XxwM8aToWLweV+yA0wlQm0yIUVqljquqoHCYEq9II8gJQ==", "cpu": [ "arm" ], @@ -2321,13 +2322,16 @@ } }, "node_modules/@oxc-parser/binding-linux-arm64-gnu": { - "version": "0.133.0", - "resolved": "https://registry.npmjs.org/@oxc-parser/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-0.133.0.tgz", - "integrity": "sha512-hvpbqT5pN2rR+3+xtWeizwfR/aZ0vGceg6TqYMl+ToxMpk9/tmnX7kSvQnfEUkoua8mhogzvIKsAkn0wxgblBA==", + "version": "0.137.0", + "resolved": "https://registry.npmjs.org/@oxc-parser/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-0.137.0.tgz", + "integrity": "sha512-GdEtiG89yMr7XkUGxifgodXEEm2f+xW2f9CpDjlgAnBOwhTmrpQMvhOGobLVKUyzf/qHBXW16smk5zbF3nZU6w==", "cpu": [ "arm64" ], "dev": true, + "libc": [ + "glibc" + ], "license": "MIT", "optional": true, "os": [ @@ -2338,13 +2342,16 @@ } }, "node_modules/@oxc-parser/binding-linux-arm64-musl": { - "version": "0.133.0", - "resolved": "https://registry.npmjs.org/@oxc-parser/binding-linux-arm64-musl/-/binding-linux-arm64-musl-0.133.0.tgz", - "integrity": "sha512-wJQGamIosQBoJHW9+S5XxrtKRo3eyJxsnS1XCPrqN0LHi8uw1pTqqTfn3t/NVuvbBg7Pumn4ez9Eidgcn0xbEg==", + "version": "0.137.0", + "resolved": "https://registry.npmjs.org/@oxc-parser/binding-linux-arm64-musl/-/binding-linux-arm64-musl-0.137.0.tgz", + "integrity": "sha512-EGJ+Bs8iXx8KBH8DQ5BLoEm5lnHaYjlh4/8j8vFhrr/6z4tqONy5BZDzLpKmmNWlN6Hlc5r8YOuBVHqZ9vRFEQ==", "cpu": [ "arm64" ], "dev": true, + "libc": [ + "musl" + ], "license": "MIT", "optional": true, "os": [ @@ -2355,13 +2362,16 @@ } }, "node_modules/@oxc-parser/binding-linux-ppc64-gnu": { - "version": "0.133.0", - "resolved": "https://registry.npmjs.org/@oxc-parser/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-0.133.0.tgz", - "integrity": "sha512-Koaz32/O5+abIfrNGdyndgRvdOZ9jEf5/z3Ep9h3h2QWpdDiUQpVwgH0OcMXCs+l9aXxPLtkupqyVig9W6FDKw==", + "version": "0.137.0", + "resolved": "https://registry.npmjs.org/@oxc-parser/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-0.137.0.tgz", + "integrity": "sha512-vzFUQENy/fnbSe5DZWovq6tIBc1uhuMztanSW6rz1e9WdQE4gHwYuD7ZII6JnrJifd1R3RSoqiZbgRFlVL2tYQ==", "cpu": [ "ppc64" ], "dev": true, + "libc": [ + "glibc" + ], "license": "MIT", "optional": true, "os": [ @@ -2372,13 +2382,16 @@ } }, "node_modules/@oxc-parser/binding-linux-riscv64-gnu": { - "version": "0.133.0", - "resolved": "https://registry.npmjs.org/@oxc-parser/binding-linux-riscv64-gnu/-/binding-linux-riscv64-gnu-0.133.0.tgz", - "integrity": "sha512-R4vOjWzxhnNWHnVLeiB6jNuIifdy9vcMXZGPc7StXcxBovI+U2zg1QhZ9o8OjV80oGivs1lX5NfPLzk4IPqlRA==", + "version": "0.137.0", + "resolved": "https://registry.npmjs.org/@oxc-parser/binding-linux-riscv64-gnu/-/binding-linux-riscv64-gnu-0.137.0.tgz", + "integrity": "sha512-SfVI14HBQs9gtLcUD5hTt5hsNbdrqSUNg9S8muN+LhVQ5nf1WwH3hAoK6B9NKgdYgWAQSXFXGiiBedQ4r/BKuw==", "cpu": [ "riscv64" ], "dev": true, + "libc": [ + "glibc" + ], "license": "MIT", "optional": true, "os": [ @@ -2389,13 +2402,16 @@ } }, "node_modules/@oxc-parser/binding-linux-riscv64-musl": { - "version": "0.133.0", - "resolved": "https://registry.npmjs.org/@oxc-parser/binding-linux-riscv64-musl/-/binding-linux-riscv64-musl-0.133.0.tgz", - "integrity": "sha512-iwgBNUTHiMdxARLYuM0SBlnYeb19iw1Ea5M+4ERZupCsBMLArti6FyZ6UfFjJxIiTDr2oW2DGQFxlQVQ/dW9rA==", + "version": "0.137.0", + "resolved": "https://registry.npmjs.org/@oxc-parser/binding-linux-riscv64-musl/-/binding-linux-riscv64-musl-0.137.0.tgz", + "integrity": "sha512-e7Ppy4FCIFNQxT/ikSeIWFoQ0l+N9vgtRBtLcyZXeolTzApyVoPqEXsYPrcdM/9i0Bwk8knvYd37vaEMxHyi6g==", "cpu": [ "riscv64" ], "dev": true, + "libc": [ + "musl" + ], "license": "MIT", "optional": true, "os": [ @@ -2406,13 +2422,16 @@ } }, "node_modules/@oxc-parser/binding-linux-s390x-gnu": { - "version": "0.133.0", - "resolved": "https://registry.npmjs.org/@oxc-parser/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-0.133.0.tgz", - "integrity": "sha512-ZwZNo8FZmB/gVfboQl+wXilBigGl+6nQQs+nITOeAP/HcAOjiHl6XZJL9F/KXNEspODQcbjAiyjUbeCJd9a0fA==", + "version": "0.137.0", + "resolved": "https://registry.npmjs.org/@oxc-parser/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-0.137.0.tgz", + "integrity": "sha512-Bho5qFwdhqsIFR7gipYEUlqvi3SRrY8sugxXig380MIaakBB1PyU9+7dBiBVScfImTNWhijUxdBwqrprGdq5WA==", "cpu": [ "s390x" ], "dev": true, + "libc": [ + "glibc" + ], "license": "MIT", "optional": true, "os": [ @@ -2423,13 +2442,16 @@ } }, "node_modules/@oxc-parser/binding-linux-x64-gnu": { - "version": "0.133.0", - "resolved": "https://registry.npmjs.org/@oxc-parser/binding-linux-x64-gnu/-/binding-linux-x64-gnu-0.133.0.tgz", - "integrity": "sha512-govCvWx1dBlED3uu4qXctxpRcouu9I8Kn+DBktGCl760JtlGJzc9l/OmPJKlYWSbrRqKkMZehNeZ/4Wfma7uSA==", + "version": "0.137.0", + "resolved": "https://registry.npmjs.org/@oxc-parser/binding-linux-x64-gnu/-/binding-linux-x64-gnu-0.137.0.tgz", + "integrity": "sha512-36mGWtg7PyFzjJwGDkH6/F4o2nIDEoKXLPr/X/lwqklkomQwJJt1I5GJVmGhovUEmgPK5WAeAZMqlFCehwiy9Q==", "cpu": [ "x64" ], "dev": true, + "libc": [ + "glibc" + ], "license": "MIT", "optional": true, "os": [ @@ -2440,13 +2462,16 @@ } }, "node_modules/@oxc-parser/binding-linux-x64-musl": { - "version": "0.133.0", - "resolved": "https://registry.npmjs.org/@oxc-parser/binding-linux-x64-musl/-/binding-linux-x64-musl-0.133.0.tgz", - "integrity": "sha512-ssTlpXD5Mq9uCssDJPzlRWqBt4Y7Zzd9i+XZhWmK/9Y6KUIuAxVYTYiI8lxcGWi0+3/Cz4A8q9UrD4NK9Y2j7g==", + "version": "0.137.0", + "resolved": "https://registry.npmjs.org/@oxc-parser/binding-linux-x64-musl/-/binding-linux-x64-musl-0.137.0.tgz", + "integrity": "sha512-/Jqx6+N7A44n2BdvUr7pXhVr2vFjs6WGH3unZRczwrfiH0H1zY0QwKQMG/dtRiTlKGDKGukznPT8lx84/oEsZg==", "cpu": [ "x64" ], "dev": true, + "libc": [ + "musl" + ], "license": "MIT", "optional": true, "os": [ @@ -2457,9 +2482,9 @@ } }, "node_modules/@oxc-parser/binding-openharmony-arm64": { - "version": "0.133.0", - "resolved": "https://registry.npmjs.org/@oxc-parser/binding-openharmony-arm64/-/binding-openharmony-arm64-0.133.0.tgz", - "integrity": "sha512-51aByfXhPtLEdWG4a2Ihdw6cPWV1ei1AarALpFdDP8MLWDLE2NuUMgbo3DERR2Kt8fT/ok1GUvBiLxVGke9uUQ==", + "version": "0.137.0", + "resolved": "https://registry.npmjs.org/@oxc-parser/binding-openharmony-arm64/-/binding-openharmony-arm64-0.137.0.tgz", + "integrity": "sha512-9Uj0qHNNl+OgT1UTGwF7ixIXU6T1u2SbMidmgPy/h1h/fl2gRS6YpAxxY1gwHofcWjoTwkoMFd8xs5Vuj6GOFA==", "cpu": [ "arm64" ], @@ -2474,9 +2499,9 @@ } }, "node_modules/@oxc-parser/binding-wasm32-wasi": { - "version": "0.133.0", - "resolved": "https://registry.npmjs.org/@oxc-parser/binding-wasm32-wasi/-/binding-wasm32-wasi-0.133.0.tgz", - "integrity": "sha512-2e16tkKp+wDO2GTAmXfxbBcCmGEaFPIJEIRBBmVKNVXSc8/fJsSIaBGyFTPHM9ST5GNWgJcYIt94rDTks+PLwA==", + "version": "0.137.0", + "resolved": "https://registry.npmjs.org/@oxc-parser/binding-wasm32-wasi/-/binding-wasm32-wasi-0.137.0.tgz", + "integrity": "sha512-gW2vfkytNGgMVADiuzdvOfw0mWG9za20F/1fCJsif5aBMAvWJTSbpIXbIe0XkOe0VENk+PadpQ7cZgUy2sUJcA==", "cpu": [ "wasm32" ], @@ -2484,18 +2509,18 @@ "license": "MIT", "optional": true, "dependencies": { - "@emnapi/core": "1.10.0", - "@emnapi/runtime": "1.10.0", - "@napi-rs/wasm-runtime": "^1.1.4" + "@emnapi/core": "1.11.1", + "@emnapi/runtime": "1.11.1", + "@napi-rs/wasm-runtime": "^1.1.5" }, "engines": { "node": "^20.19.0 || >=22.12.0" } }, "node_modules/@oxc-parser/binding-win32-arm64-msvc": { - "version": "0.133.0", - "resolved": "https://registry.npmjs.org/@oxc-parser/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-0.133.0.tgz", - "integrity": "sha512-KPTNDKbxH1cglrqTyVeXHb4Pk4oksz8EcE1/v8zqU7N4UXbiHfA/IwtXZ2U77fnRAWBbgVkl/lZbL7o3hRdejg==", + "version": "0.137.0", + "resolved": "https://registry.npmjs.org/@oxc-parser/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-0.137.0.tgz", + "integrity": "sha512-x+pFANF0yL5uK/6T7lu6SlR5qid6sp//eZXKLq5iNsIE+EQg6EaS8/wsW7E91nXXjpnPhSoMOHXShSVhGRdn8w==", "cpu": [ "arm64" ], @@ -2510,9 +2535,9 @@ } }, "node_modules/@oxc-parser/binding-win32-ia32-msvc": { - "version": "0.133.0", - "resolved": "https://registry.npmjs.org/@oxc-parser/binding-win32-ia32-msvc/-/binding-win32-ia32-msvc-0.133.0.tgz", - "integrity": "sha512-Una1bNYv9zCavQrfnDR9wuZVB3itLjCEH4Oz7i6CwAJN/Xq9b+zbbcxmvdkKvvJt4Ngc/MBmIYlbLo3zS4TQ0A==", + "version": "0.137.0", + "resolved": "https://registry.npmjs.org/@oxc-parser/binding-win32-ia32-msvc/-/binding-win32-ia32-msvc-0.137.0.tgz", + "integrity": "sha512-sQUqym80PFi6McRsIqfJrSu2JrSClEZIXXD+/FjAFoULEKzOPsldIdFBG96xdX8aVMzCNQ9792FPx3MfkEIrFA==", "cpu": [ "ia32" ], @@ -2527,9 +2552,9 @@ } }, "node_modules/@oxc-parser/binding-win32-x64-msvc": { - "version": "0.133.0", - "resolved": "https://registry.npmjs.org/@oxc-parser/binding-win32-x64-msvc/-/binding-win32-x64-msvc-0.133.0.tgz", - "integrity": "sha512-kjBhCiOGSYTwDJQuuZa7a94JbP8htWu7J0X1KwH74kV2K5eYf6eyJRYmkpCDvr0XEL8tMxYI4WU1VekblFCLgg==", + "version": "0.137.0", + "resolved": "https://registry.npmjs.org/@oxc-parser/binding-win32-x64-msvc/-/binding-win32-x64-msvc-0.137.0.tgz", + "integrity": "sha512-2AsevxlvNN4WKxpEn3RtqD5zbqMaXF+T7JXblsP4gVuY+vC9dXS4ED/PwfRCliFqoeisYS3Iro4DHzxr0TEvVA==", "cpu": [ "x64" ], @@ -2544,9 +2569,9 @@ } }, "node_modules/@oxc-project/types": { - "version": "0.133.0", - "resolved": "https://registry.npmjs.org/@oxc-project/types/-/types-0.133.0.tgz", - "integrity": "sha512-KzkdCd6Uxqnf6l3HOw1xfatAlUURA0g14cvBYFyJ5SaNOQbOUvBr9PKArcPcrNIeRsBdgcUzOGrhKveVpvOIGA==", + "version": "0.137.0", + "resolved": "https://registry.npmjs.org/@oxc-project/types/-/types-0.137.0.tgz", + "integrity": "sha512-WT+Gb24i8hmvo85AIv2oEYouEXkRlKAlT9WaCa3TfLgNCN+GhrJOGZuIlMouAh38Qe4QOx26eUOVsq70qXrywA==", "dev": true, "license": "MIT", "funding": { @@ -2554,9 +2579,9 @@ } }, "node_modules/@oxc-resolver/binding-android-arm-eabi": { - "version": "11.20.0", - "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-android-arm-eabi/-/binding-android-arm-eabi-11.20.0.tgz", - "integrity": "sha512-IjfWOXRgJFNdORDl+Uf1aibNgZY2guOD3zmOhx1BGVb/MIiqlFTdmjpQNplSN58lhWehnX4UNqC3QwpUo8pjJg==", + "version": "11.21.3", + "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-android-arm-eabi/-/binding-android-arm-eabi-11.21.3.tgz", + "integrity": "sha512-eNU11A2WNizh04v3uyaJCootrHIaS0B9aHYXvAvVnPNk4xYSjMUjHnhQ6dewPN2MRYDskV85d1N0Aw0WNWhcyg==", "cpu": [ "arm" ], @@ -2568,9 +2593,9 @@ ] }, "node_modules/@oxc-resolver/binding-android-arm64": { - "version": "11.20.0", - "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-android-arm64/-/binding-android-arm64-11.20.0.tgz", - "integrity": "sha512-QqslZAuFQG8Q9xm7JuIn8JUbvywhSBMVhuQHtYW+auirZJloS41oxUUaBXk7uUhZJgp44c5zQLeVvmFaDQB+2Q==", + "version": "11.21.3", + "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-android-arm64/-/binding-android-arm64-11.21.3.tgz", + "integrity": "sha512-8Q+ZjTLvn2dIcWsrmhdrEihm7q+ag/k+mkry7Z+t0QbbHaVxXQfvH9AewyVMh/WrpEKhQ3DDgx9fYbqeCpeOEw==", "cpu": [ "arm64" ], @@ -2582,9 +2607,9 @@ ] }, "node_modules/@oxc-resolver/binding-darwin-arm64": { - "version": "11.20.0", - "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-darwin-arm64/-/binding-darwin-arm64-11.20.0.tgz", - "integrity": "sha512-MUcavykj2ewlR+kc5arpg4tC2RvzJkUxWtNv74pf7lcNk00GpIpN43vXMj+j6r4eMmfZhlb8hueKoIb8e9kAGQ==", + "version": "11.21.3", + "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-darwin-arm64/-/binding-darwin-arm64-11.21.3.tgz", + "integrity": "sha512-wkh0qKZGHXVUDxFw3oA1TXnU2BDYY/r775oJflGeIr8uDPPoN2pk8gijQIzYRT6hoql/lg3+Tx/SaTn9e2/aGg==", "cpu": [ "arm64" ], @@ -2596,9 +2621,9 @@ ] }, "node_modules/@oxc-resolver/binding-darwin-x64": { - "version": "11.20.0", - "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-darwin-x64/-/binding-darwin-x64-11.20.0.tgz", - "integrity": "sha512-BGB16nRUK5Etiv//ihPyzj8Lj1px0mhh4YIfe0FDf045ywknfSm0GEbiRESpr6Q4K82AvnyaRIhhluHByvS4bg==", + "version": "11.21.3", + "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-darwin-x64/-/binding-darwin-x64-11.21.3.tgz", + "integrity": "sha512-HbNc23FAQYbuyDV2vBWMez4u4mrsm5RAkniGZAWqr6lYZ3N4beeqIb776jzwRl8qL2zRhHVXpUj97X0QgogVzg==", "cpu": [ "x64" ], @@ -2610,9 +2635,9 @@ ] }, "node_modules/@oxc-resolver/binding-freebsd-x64": { - "version": "11.20.0", - "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-freebsd-x64/-/binding-freebsd-x64-11.20.0.tgz", - "integrity": "sha512-JZgtePaqj3qmD5XFHJaSLWzHRxQu0LaPkdoM1KJXYADvAaa83ijXHclV3ej3CueeW0wxfIAbGCZVP45J0CA7uQ==", + "version": "11.21.3", + "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-freebsd-x64/-/binding-freebsd-x64-11.21.3.tgz", + "integrity": "sha512-K6xNsTUPEUdfrn0+kbMq5nOUB5w1C5pavPQngt4TM2FpN91lP0PBe2srSpamb4d69O7h86oAi/qWX/kZNRSjkw==", "cpu": [ "x64" ], @@ -2624,9 +2649,9 @@ ] }, "node_modules/@oxc-resolver/binding-linux-arm-gnueabihf": { - "version": "11.20.0", - "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-11.20.0.tgz", - "integrity": "sha512-hOQ/p3ry3v3SchUBXicrrnszaI/UmYzM4wtS4RGfwgVUX7a+HbyQSzJ5aOzu+o6XZkFkS3ZXN4PZAzhOb77OSg==", + "version": "11.21.3", + "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-11.21.3.tgz", + "integrity": "sha512-VcFmOpcpWX1zoEy8M58tR2M9YxM+Z9RuQhqAx5q0CTmrruaP7Gveejg75hzd/5sg5nk9G3aLALEa3hE2FsmmTQ==", "cpu": [ "arm" ], @@ -2638,9 +2663,9 @@ ] }, "node_modules/@oxc-resolver/binding-linux-arm-musleabihf": { - "version": "11.20.0", - "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-linux-arm-musleabihf/-/binding-linux-arm-musleabihf-11.20.0.tgz", - "integrity": "sha512-2ArPksaw0AqeuGBfoS715VF+JvJQAhD2niWgjE5hVO+L+nAfikVQopvngCMX9x4BD8itWoQ3dnikrQyl5Ho5Jg==", + "version": "11.21.3", + "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-linux-arm-musleabihf/-/binding-linux-arm-musleabihf-11.21.3.tgz", + "integrity": "sha512-quVoxFLBy43hWaQbbDtQNRwAX5vX76mv7n64icAtQcJ3eNgVeblqmkupF/hAneNthdqSlnd1sTjb3aQSaDPaCQ==", "cpu": [ "arm" ], @@ -2652,13 +2677,16 @@ ] }, "node_modules/@oxc-resolver/binding-linux-arm64-gnu": { - "version": "11.20.0", - "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-11.20.0.tgz", - "integrity": "sha512-0bJnmYFp62JdZ4nVMDUZ/C58BCZOCcqgKtnUlp7L9Ojf/czIN+3j72YlLPeWLkzlr6SlYvIQA4SGV/HyO0d+qg==", + "version": "11.21.3", + "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-11.21.3.tgz", + "integrity": "sha512-X0AqNZgcD07Q4V3RDK18/vYOj/HQT/FnmEFGYS2jTWqY7JO13ryE3TEs3eAIgUJhBnNkpEaiXqz3VK8M7qQhWQ==", "cpu": [ "arm64" ], "dev": true, + "libc": [ + "glibc" + ], "license": "MIT", "optional": true, "os": [ @@ -2666,13 +2694,16 @@ ] }, "node_modules/@oxc-resolver/binding-linux-arm64-musl": { - "version": "11.20.0", - "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-linux-arm64-musl/-/binding-linux-arm64-musl-11.20.0.tgz", - "integrity": "sha512-wKHHzPKZo7Ufhv/Bt6yxT7FOgnIgW4gwXcJUipkShGp68W3wGVqvr1Sr0fY65lN0Oy6y41+g2kIDvkgZaMMUkw==", + "version": "11.21.3", + "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-linux-arm64-musl/-/binding-linux-arm64-musl-11.21.3.tgz", + "integrity": "sha512-YkaQnaKYdbuaXvRt5Qd0GpbihzVnyfR6z1SpYfIUC6RTu4NF7lDKPjVkYb+jRI2gedVO2rVpN35Y6akG6ud4Lw==", "cpu": [ "arm64" ], "dev": true, + "libc": [ + "musl" + ], "license": "MIT", "optional": true, "os": [ @@ -2680,13 +2711,16 @@ ] }, "node_modules/@oxc-resolver/binding-linux-ppc64-gnu": { - "version": "11.20.0", - "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-11.20.0.tgz", - "integrity": "sha512-RN8goF7Ie0B79L4i4G6OeBocTgSC56vJbQ65VJje+oXnldVpLnOU7j/AQ/dP94TcCS+Yh6WG8u3Qt4ETteXFNQ==", + "version": "11.21.3", + "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-11.21.3.tgz", + "integrity": "sha512-gB9HwhrPiFqUzDeEq+y/CgAijz1YdI6BnXz5GaH2Pa9cWdutchlkGFAiAuGb/PjVQpiK6NFKzFuztxrweoit7A==", "cpu": [ "ppc64" ], "dev": true, + "libc": [ + "glibc" + ], "license": "MIT", "optional": true, "os": [ @@ -2694,13 +2728,16 @@ ] }, "node_modules/@oxc-resolver/binding-linux-riscv64-gnu": { - "version": "11.20.0", - "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-linux-riscv64-gnu/-/binding-linux-riscv64-gnu-11.20.0.tgz", - "integrity": "sha512-5l1yU6/xQEqLZRzxqmMxJfWPslpwCmBsdDGaBvABPehxquCXDC7dd7oraNdKSJUMDXSM7VvVj8H2D2FTjU7oWw==", + "version": "11.21.3", + "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-linux-riscv64-gnu/-/binding-linux-riscv64-gnu-11.21.3.tgz", + "integrity": "sha512-zjDWBlYk8QGv0H8dsPUWqkfjYIIjG2TvspGkzXL0eImbgxtZorA/klKeHyolevoT3Kvbi+1iMr9Lhrh7jf54Og==", "cpu": [ "riscv64" ], "dev": true, + "libc": [ + "glibc" + ], "license": "MIT", "optional": true, "os": [ @@ -2708,13 +2745,16 @@ ] }, "node_modules/@oxc-resolver/binding-linux-riscv64-musl": { - "version": "11.20.0", - "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-linux-riscv64-musl/-/binding-linux-riscv64-musl-11.20.0.tgz", - "integrity": "sha512-xHEvkbgz6UC+A3JOyDQy76LkUaxsNSfIr3/GV8slwZsnuooJiIB34gzJfsyvR4JdCYNUUPsRJc/w/oWkODu+hg==", + "version": "11.21.3", + "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-linux-riscv64-musl/-/binding-linux-riscv64-musl-11.21.3.tgz", + "integrity": "sha512-4UfsQvacV388y1zpXL7C1x1FNYaV52JtuNRiuzrfQA2z1z6ElVrsidkGsrvQ5EgeSq1Pj7kaKqrgGkvFuxJ/tw==", "cpu": [ "riscv64" ], "dev": true, + "libc": [ + "musl" + ], "license": "MIT", "optional": true, "os": [ @@ -2722,13 +2762,16 @@ ] }, "node_modules/@oxc-resolver/binding-linux-s390x-gnu": { - "version": "11.20.0", - "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-11.20.0.tgz", - "integrity": "sha512-aWPDUUmSeyHvlW+SoEUd+JIJsQhVhu6a5tBpDRMu058naPAchTgAVGCFy35zjbnFlt0i8hLWziff6HX0D3LU4g==", + "version": "11.21.3", + "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-11.21.3.tgz", + "integrity": "sha512-b5uH+HKH0MP5mNBYaK75SKsJbw52URqrx2LavYdq6wb0l3ExAG5niYRP9DWUNHdKilpaBVM2bXk9HNWrH3ew7Q==", "cpu": [ "s390x" ], "dev": true, + "libc": [ + "glibc" + ], "license": "MIT", "optional": true, "os": [ @@ -2736,13 +2779,16 @@ ] }, "node_modules/@oxc-resolver/binding-linux-x64-gnu": { - "version": "11.20.0", - "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-linux-x64-gnu/-/binding-linux-x64-gnu-11.20.0.tgz", - "integrity": "sha512-x2YeSimvhJjKLVD8KSu8f/rqU1potcdEMkApIPJqjZWN7c2Fpt4g2X32WDg1p+XDAmyT7nuQGe0vnhvXeLbH+g==", + "version": "11.21.3", + "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-linux-x64-gnu/-/binding-linux-x64-gnu-11.21.3.tgz", + "integrity": "sha512-PjYlmilBpNRh2ntXNYAK3Am5w/nPfEpnU/96iNx7CI8EzAn12J4JRiec63wHJTH31nLoCNxBg/829pN+3CfG3Q==", "cpu": [ "x64" ], "dev": true, + "libc": [ + "glibc" + ], "license": "MIT", "optional": true, "os": [ @@ -2750,13 +2796,16 @@ ] }, "node_modules/@oxc-resolver/binding-linux-x64-musl": { - "version": "11.20.0", - "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-linux-x64-musl/-/binding-linux-x64-musl-11.20.0.tgz", - "integrity": "sha512-kcRLEIxpZefeYfLChjpgFf3ilBzRDZ+yobMrpRsQlSrxuFGtm3U6PMU7AaEpMqo3NfDGVyJJseAjnRLzMFHjwQ==", + "version": "11.21.3", + "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-linux-x64-musl/-/binding-linux-x64-musl-11.21.3.tgz", + "integrity": "sha512-QTBAb7JuHlZ7JUEyM8UiQi2f7m/L4swBhP2TNpYIDc9Wp/wRw1G/8sl6i13aIzQAXH7LKIm294LeOHd0lQR8zA==", "cpu": [ "x64" ], "dev": true, + "libc": [ + "musl" + ], "license": "MIT", "optional": true, "os": [ @@ -2764,9 +2813,9 @@ ] }, "node_modules/@oxc-resolver/binding-openharmony-arm64": { - "version": "11.20.0", - "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-openharmony-arm64/-/binding-openharmony-arm64-11.20.0.tgz", - "integrity": "sha512-HHcfnApSZGtKhTiHqe8OZruOZe5XuFQH5/E0Yhj3u8fnFvzkM4/k6WjacUf4SvA0SPEAbfbgYmVPuo0VX/fIBQ==", + "version": "11.21.3", + "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-openharmony-arm64/-/binding-openharmony-arm64-11.21.3.tgz", + "integrity": "sha512-4j1DFwjwv36ec9kds0jU/ucQ5Ha4ERO/H95BxR5JFf0kqUUAJ1kwII7XhTc1vZrkdJkvLGC9Q2MbpObpum8RBg==", "cpu": [ "arm64" ], @@ -2778,9 +2827,9 @@ ] }, "node_modules/@oxc-resolver/binding-wasm32-wasi": { - "version": "11.20.0", - "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-wasm32-wasi/-/binding-wasm32-wasi-11.20.0.tgz", - "integrity": "sha512-Tn0y1XOFYHNfK1wp1Z5QK8Rcld/bsOwRISQXfqAZ5IBpv8Gz1IvV39fUWNprqNdRizgcvFhOzWwFun2zkJsyBg==", + "version": "11.21.3", + "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-wasm32-wasi/-/binding-wasm32-wasi-11.21.3.tgz", + "integrity": "sha512-i8oluoel5kru/j1WNrjmQSiA3GQ7wvIYVR1IwIoZtKogAhya2iub+ZKIeSIkcJOrnzQ18Tzl/F+kL3fYOxZLvA==", "cpu": [ "wasm32" ], @@ -2788,18 +2837,41 @@ "license": "MIT", "optional": true, "dependencies": { - "@emnapi/core": "1.10.0", - "@emnapi/runtime": "1.10.0", - "@napi-rs/wasm-runtime": "^1.1.4" + "@emnapi/core": "1.11.0", + "@emnapi/runtime": "1.11.0", + "@napi-rs/wasm-runtime": "^1.1.5" }, "engines": { "node": ">=14.0.0" } }, + "node_modules/@oxc-resolver/binding-wasm32-wasi/node_modules/@emnapi/core": { + "version": "1.11.0", + "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.11.0.tgz", + "integrity": "sha512-l9Oo58x0HOP5znGzVhYW9U3e5wVuA4LAZU2AGezTmkhO1CgQRFDhDg4nneHsu/t3WniXg9QrG2nIXL/ZS8ln8Q==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@emnapi/wasi-threads": "1.2.2", + "tslib": "^2.4.0" + } + }, + "node_modules/@oxc-resolver/binding-wasm32-wasi/node_modules/@emnapi/runtime": { + "version": "1.11.0", + "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.11.0.tgz", + "integrity": "sha512-55coeOFKHv1ywEcUXJtWU5f+Jr/W5tZDvZig8DLKSwUN1JpROQ4rk/SNOQiFWmaR/VKF4zuFyW1B8JduOSv6Pg==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, "node_modules/@oxc-resolver/binding-win32-arm64-msvc": { - "version": "11.20.0", - "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-11.20.0.tgz", - "integrity": "sha512-qPi25YNPe4YenS8MgsQU2+bIFHxxpLx1LVna2444cEHqNPhNjvWf9zqj4aWE43H9LpAsTmkkAlA3eL5ElBU3mA==", + "version": "11.21.3", + "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-11.21.3.tgz", + "integrity": "sha512-M/8dw8dD6aOs+NlPJax401CZB9I7Aut84isQLgALGGwke4Afvw+/7yYhZb94yXf6t2sPLhQLmSmtSV+2FhsOWg==", "cpu": [ "arm64" ], @@ -2811,9 +2883,9 @@ ] }, "node_modules/@oxc-resolver/binding-win32-x64-msvc": { - "version": "11.20.0", - "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-win32-x64-msvc/-/binding-win32-x64-msvc-11.20.0.tgz", - "integrity": "sha512-Wb14jWEW8huH6It9F6sXd9vrYmIS7pMrgkU6sxpLxkP+9z+wRgs71hUEhRpcn8FOXAFa27FVWfY2tRpbfTzfLw==", + "version": "11.21.3", + "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-win32-x64-msvc/-/binding-win32-x64-msvc-11.21.3.tgz", + "integrity": "sha512-H7BCt/VnS9hnmMp42eGhZ99izSCRvlnWwy/N71K1/J8QoExwY4262Z8QiEkMDtduRJrztayDxETTckmUuAVL9Q==", "cpu": [ "x64" ], @@ -4371,10 +4443,11 @@ } }, "node_modules/@testing-library/jest-dom": { - "version": "5.16.5", - "resolved": "https://registry.npmjs.org/@testing-library/jest-dom/-/jest-dom-5.16.5.tgz", - "integrity": "sha512-N5ixQ2qKpi5OLYfwQmUb/5mSV9LneAcaUfp32pn4yCnpb8r/Yz0pXFPck21dIicKmi+ta5WRAknkZCfA8refMA==", + "version": "5.17.0", + "resolved": "https://registry.npmjs.org/@testing-library/jest-dom/-/jest-dom-5.17.0.tgz", + "integrity": "sha512-ynmNeT7asXyH3aSVv4vvX4Rb+0qjOhdNHnO/3vuZNqPmhDpV/+rCSGwQ7bLcmU2cJ4dvoheIO85LQj0IbJHEtg==", "dev": true, + "license": "MIT", "dependencies": { "@adobe/css-tools": "^4.0.1", "@babel/runtime": "^7.9.2", @@ -4868,9 +4941,9 @@ } }, "node_modules/@tybys/wasm-util": { - "version": "0.10.2", - "resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.10.2.tgz", - "integrity": "sha512-RoBvJ2X0wuKlWFIjrwffGw1IqZHKQqzIchKaadZZfnNpsAYp2mM0h36JtPCjNDAHGgYez/15uMBpfGwchhiMgg==", + "version": "0.10.3", + "resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.10.3.tgz", + "integrity": "sha512-F3fo1MYrRJYL3zER0OUOmkutjr1Vp23m7OsSgp7nq4SP6OqX6C/56XFIPAl5bt3zaBRjmW7SGz3u/6LwFpYcOg==", "dev": true, "license": "MIT", "optional": true, @@ -5012,10 +5085,11 @@ "license": "MIT" }, "node_modules/@types/google.maps": { - "version": "3.51.0", - "resolved": "https://registry.npmjs.org/@types/google.maps/-/google.maps-3.51.0.tgz", - "integrity": "sha512-44/oQYjc5D6kxBcI3Qk9rk3IIOMwnlEMWDV7pwPJ2YI89s5Q1OzDrFvR7QJ3LFrpVXEhig+gyagFg54+foinFg==", - "dev": true + "version": "3.65.2", + "resolved": "https://registry.npmjs.org/@types/google.maps/-/google.maps-3.65.2.tgz", + "integrity": "sha512-e52bmOhGCQSNabFpL48iQlwJybq6rfns8NUVJ20MR7CdPlHQ2RmSCnPbJfrUYJfogrE4OiHQTZ4LXpop+eer1w==", + "dev": true, + "license": "MIT" }, "node_modules/@types/hast": { "version": "3.0.4", @@ -5115,10 +5189,11 @@ "license": "MIT" }, "node_modules/@types/latlon-geohash": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@types/latlon-geohash/-/latlon-geohash-2.0.0.tgz", - "integrity": "sha512-bwTttcqf8StBA+ABJ4gxbgA+PgueUJGxjYzuWrlFfzDbIQd2FvOcE5l/0fY+BOKqmeWYAzjBQFdnKweI8KKNpA==", - "dev": true + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/@types/latlon-geohash/-/latlon-geohash-2.0.4.tgz", + "integrity": "sha512-R/wb/V8lhhI0hyRGDTkT6F4C3lynkF/D29iMSN5B4bYswaa4R+wajgcTv9z73BotGArf4Q8BnDNtmLeI+WiKkQ==", + "dev": true, + "license": "MIT" }, "node_modules/@types/linkify-it": { "version": "5.0.0", @@ -5128,10 +5203,11 @@ "peer": true }, "node_modules/@types/lodash": { - "version": "4.14.191", - "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.191.tgz", - "integrity": "sha512-BdZ5BCCvho3EIXw6wUCXHe7rS53AIDPLE+JzwgT+OsJk53oBfbSmZZ7CX4VaRoN78N+TJpFi9QPlfIVNmJYWxQ==", - "dev": true + "version": "4.17.24", + "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.17.24.tgz", + "integrity": "sha512-gIW7lQLZbue7lRSWEFql49QJJWThrTFFeIMJdp3eH4tKoxm1OvEPg02rm4wCCSHS0cL3/Fizimb35b7k8atwsQ==", + "dev": true, + "license": "MIT" }, "node_modules/@types/markdown-it": { "version": "14.1.2", @@ -5183,19 +5259,21 @@ } }, "node_modules/@types/react-beautiful-dnd": { - "version": "13.1.4", - "resolved": "https://registry.npmjs.org/@types/react-beautiful-dnd/-/react-beautiful-dnd-13.1.4.tgz", - "integrity": "sha512-4bIBdzOr0aavN+88q3C7Pgz+xkb7tz3whORYrmSj77wfVEMfiWiooIwVWFR7KM2e+uGTe5BVrXqSfb0aHeflJA==", + "version": "13.1.8", + "resolved": "https://registry.npmjs.org/@types/react-beautiful-dnd/-/react-beautiful-dnd-13.1.8.tgz", + "integrity": "sha512-E3TyFsro9pQuK4r8S/OL6G99eq7p8v29sX0PM7oT8Z+PJfZvSQTx4zTQbUJ+QZXioAF0e7TGBEcA1XhYhCweyQ==", "dev": true, + "license": "MIT", "dependencies": { "@types/react": "*" } }, "node_modules/@types/react-csv": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/@types/react-csv/-/react-csv-1.1.3.tgz", - "integrity": "sha512-dkEdyRvRpygSnNg4cyzYWSUjukIQ5lAtXJwc7BqyUfzww/Cv2dcAFGYd+sWTFpGiDNZMVPp6vVPLcAPvJID8Kg==", + "version": "1.1.10", + "resolved": "https://registry.npmjs.org/@types/react-csv/-/react-csv-1.1.10.tgz", + "integrity": "sha512-PESAyASL7Nfi/IyBR3ufd8qZkyoS+7jOylKmJxRZUZLFASLo4NZaRsJ8rNP8pCcbIziADyWBbLPD1nPddhsL4g==", "dev": true, + "license": "MIT", "dependencies": { "@types/react": "*" } @@ -5230,10 +5308,11 @@ } }, "node_modules/@types/react-table": { - "version": "7.7.14", - "resolved": "https://registry.npmjs.org/@types/react-table/-/react-table-7.7.14.tgz", - "integrity": "sha512-TYrv7onCiakaG1uAu/UpQ9FojNEt/4/ht87EgJQaEGFoWV606ZLWUZAcUHzMxgc3v1mywP1cDyz3qB4ho3hWOw==", + "version": "7.7.20", + "resolved": "https://registry.npmjs.org/@types/react-table/-/react-table-7.7.20.tgz", + "integrity": "sha512-ahMp4pmjVlnExxNwxyaDrFgmKxSbPwU23sGQw2gJK4EhCvnvmib2s/O/+y1dfV57dXOwpr2plfyBol+vEHbi2w==", "dev": true, + "license": "MIT", "dependencies": { "@types/react": "*" } @@ -5278,20 +5357,20 @@ "dev": true }, "node_modules/@typescript-eslint/eslint-plugin": { - "version": "8.57.2", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.57.2.tgz", - "integrity": "sha512-NZZgp0Fm2IkD+La5PR81sd+g+8oS6JwJje+aRWsDocxHkjyRw0J5L5ZTlN3LI1LlOcGL7ph3eaIUmTXMIjLk0w==", + "version": "8.64.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.64.0.tgz", + "integrity": "sha512-CGvQPBxN3wZLu6Rz2kFUpZeoCm78xUic92ck39KPePkO1NPOwjCqdQnm5Q87tpWw9vcBvW8XLrDXjH9PWYtJ3Q==", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/regexpp": "^4.12.2", - "@typescript-eslint/scope-manager": "8.57.2", - "@typescript-eslint/type-utils": "8.57.2", - "@typescript-eslint/utils": "8.57.2", - "@typescript-eslint/visitor-keys": "8.57.2", + "@typescript-eslint/scope-manager": "8.64.0", + "@typescript-eslint/type-utils": "8.64.0", + "@typescript-eslint/utils": "8.64.0", + "@typescript-eslint/visitor-keys": "8.64.0", "ignore": "^7.0.5", "natural-compare": "^1.4.0", - "ts-api-utils": "^2.4.0" + "ts-api-utils": "^2.5.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -5301,20 +5380,33 @@ "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "@typescript-eslint/parser": "^8.57.2", + "@typescript-eslint/parser": "^8.64.0", "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", - "typescript": ">=4.8.4 <6.0.0" + "typescript": ">=4.8.4 <6.1.0" } }, - "node_modules/@typescript-eslint/eslint-plugin/node_modules/@typescript-eslint/scope-manager": { - "version": "8.57.2", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.57.2.tgz", - "integrity": "sha512-snZKH+W4WbWkrBqj4gUNRIGb/jipDW3qMqVJ4C9rzdFc+wLwruxk+2a5D+uoFcKPAqyqEnSb4l2ULuZf95eSkw==", + "node_modules/@typescript-eslint/eslint-plugin/node_modules/ignore": { + "version": "7.0.5", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.5.tgz", + "integrity": "sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/@typescript-eslint/parser": { + "version": "8.65.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.65.0.tgz", + "integrity": "sha512-CZ4nMxWwgu1HEEFNkeaCptra9QCtkmKdgf3sWh1rl1trIhmxLilgTV4cwcbQ4wemnT4sWQN8CaKOmdYx+g2gMA==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.57.2", - "@typescript-eslint/visitor-keys": "8.57.2" + "@typescript-eslint/scope-manager": "8.65.0", + "@typescript-eslint/types": "8.65.0", + "@typescript-eslint/typescript-estree": "8.65.0", + "@typescript-eslint/visitor-keys": "8.65.0", + "debug": "^4.4.3" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -5322,14 +5414,22 @@ "funding": { "type": "opencollective", "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", + "typescript": ">=4.8.4 <6.1.0" } }, - "node_modules/@typescript-eslint/eslint-plugin/node_modules/@typescript-eslint/types": { - "version": "8.57.2", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.57.2.tgz", - "integrity": "sha512-/iZM6FnM4tnx9csuTxspMW4BOSegshwX5oBDznJ7S4WggL7Vczz5d2W11ecc4vRrQMQHXRSxzrCsyG5EsPPTbA==", + "node_modules/@typescript-eslint/parser/node_modules/@typescript-eslint/scope-manager": { + "version": "8.65.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.65.0.tgz", + "integrity": "sha512-Esbl8OSYiVxBokYgWPf7VVWg/BE798wXhimnn9ML9Pt5qoDf8bfQlgjlKXR/k98+AcNzlLKYrpCcrcuZ9DZLgg==", "dev": true, "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.65.0", + "@typescript-eslint/visitor-keys": "8.65.0" + }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, @@ -5338,22 +5438,15 @@ "url": "https://opencollective.com/typescript-eslint" } }, - "node_modules/@typescript-eslint/eslint-plugin/node_modules/@typescript-eslint/typescript-estree": { - "version": "8.57.2", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.57.2.tgz", - "integrity": "sha512-2MKM+I6g8tJxfSmFKOnHv2t8Sk3T6rF20A1Puk0svLK+uVapDZB/4pfAeB7nE83uAZrU6OxW+HmOd5wHVdXwXA==", + "node_modules/@typescript-eslint/parser/node_modules/@typescript-eslint/visitor-keys": { + "version": "8.65.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.65.0.tgz", + "integrity": "sha512-8C71BQkGjiMmXtop7pHVJu1l2NNShFdkCyD6a2ezzs5vU/L3LRtb69EtcteFwz0mYMPzIgOw0n6OV4VBUWZd7A==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/project-service": "8.57.2", - "@typescript-eslint/tsconfig-utils": "8.57.2", - "@typescript-eslint/types": "8.57.2", - "@typescript-eslint/visitor-keys": "8.57.2", - "debug": "^4.4.3", - "minimatch": "^10.2.2", - "semver": "^7.7.3", - "tinyglobby": "^0.2.15", - "ts-api-utils": "^2.4.0" + "@typescript-eslint/types": "8.65.0", + "eslint-visitor-keys": "^5.0.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -5361,22 +5454,31 @@ "funding": { "type": "opencollective", "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/parser/node_modules/eslint-visitor-keys": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-5.0.1.tgz", + "integrity": "sha512-tD40eHxA35h0PEIZNeIjkHoDR4YjjJp34biM0mDvplBe//mB+IHCqHDGV7pxF+7MklTvighcCPPZC7ynWyjdTA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" }, - "peerDependencies": { - "typescript": ">=4.8.4 <6.0.0" + "funding": { + "url": "https://opencollective.com/eslint" } }, - "node_modules/@typescript-eslint/eslint-plugin/node_modules/@typescript-eslint/utils": { - "version": "8.57.2", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.57.2.tgz", - "integrity": "sha512-krRIbvPK1ju1WBKIefiX+bngPs+odIQUtR7kymzPfo1POVw3jlF+nLkmexdSSd4UCbDcQn+wMBATOOmpBbqgKg==", + "node_modules/@typescript-eslint/project-service": { + "version": "8.65.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.65.0.tgz", + "integrity": "sha512-SxnPhbTsGahizDgbu7oqFH/xVtzIqMd/s+WtnSxNxJZJpLbdT5IPdzg8EZxO3+PoKahXmwJLeNQOpKJb3/bi7Q==", "dev": true, "license": "MIT", "dependencies": { - "@eslint-community/eslint-utils": "^4.9.1", - "@typescript-eslint/scope-manager": "8.57.2", - "@typescript-eslint/types": "8.57.2", - "@typescript-eslint/typescript-estree": "8.57.2" + "@typescript-eslint/tsconfig-utils": "^8.65.0", + "@typescript-eslint/types": "^8.65.0", + "debug": "^4.4.3" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -5386,19 +5488,18 @@ "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", - "typescript": ">=4.8.4 <6.0.0" + "typescript": ">=4.8.4 <6.1.0" } }, - "node_modules/@typescript-eslint/eslint-plugin/node_modules/@typescript-eslint/visitor-keys": { - "version": "8.57.2", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.57.2.tgz", - "integrity": "sha512-zhahknjobV2FiD6Ee9iLbS7OV9zi10rG26odsQdfBO/hjSzUQbkIYgda+iNKK1zNiW2ey+Lf8MU5btN17V3dUw==", + "node_modules/@typescript-eslint/scope-manager": { + "version": "8.64.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.64.0.tgz", + "integrity": "sha512-CXEaFdYXjSTgKhisNkwCcJwTP8Pl+fmRrEQrri4nm3vU743bALrxzLmq7fHG/7e6a5xO0lDYeURpZmBuhHk54w==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.57.2", - "eslint-visitor-keys": "^5.0.0" + "@typescript-eslint/types": "8.64.0", + "@typescript-eslint/visitor-keys": "8.64.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -5408,80 +5509,131 @@ "url": "https://opencollective.com/typescript-eslint" } }, - "node_modules/@typescript-eslint/eslint-plugin/node_modules/balanced-match": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz", - "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==", + "node_modules/@typescript-eslint/scope-manager/node_modules/@typescript-eslint/types": { + "version": "8.64.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.64.0.tgz", + "integrity": "sha512-qjhfuTfLXjA4IOzXvz0rTjT01BqEiIgPoUeMwiEjnaHKJMTNo8rH5pYW1a2L/0Dnux2fPC85AeyJoWaGa8WxTA==", "dev": true, "license": "MIT", "engines": { - "node": "18 || 20 || >=22" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/tsconfig-utils": { + "version": "8.65.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.65.0.tgz", + "integrity": "sha512-j6GzGqCiRdA7Qhur2VVmKZAkBLfnHFQfx4TaJGL9RMveZqCo48jSHHO0DTgizEnGhtWnqmbtCUSrqSkdiY/0Hg==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <6.1.0" } }, - "node_modules/@typescript-eslint/eslint-plugin/node_modules/brace-expansion": { - "version": "5.0.5", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.5.tgz", - "integrity": "sha512-VZznLgtwhn+Mact9tfiwx64fA9erHH/MCXEUfB/0bX/6Fz6ny5EGTXYltMocqg4xFAQZtnO3DHWWXi8RiuN7cQ==", + "node_modules/@typescript-eslint/type-utils": { + "version": "8.64.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.64.0.tgz", + "integrity": "sha512-XWG4Fmmv/6SvyS9nH8jWrKs6terwJvE8cyRt1CzYYqzp9OrPhCT4cMc/f7C6RZCwG+qMmiffJS1/qJP8G1URtg==", "dev": true, "license": "MIT", "dependencies": { - "balanced-match": "^4.0.2" + "@typescript-eslint/types": "8.64.0", + "@typescript-eslint/typescript-estree": "8.64.0", + "@typescript-eslint/utils": "8.64.0", + "debug": "^4.4.3", + "ts-api-utils": "^2.5.0" }, "engines": { - "node": "18 || 20 || >=22" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", + "typescript": ">=4.8.4 <6.1.0" } }, - "node_modules/@typescript-eslint/eslint-plugin/node_modules/eslint-visitor-keys": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-5.0.1.tgz", - "integrity": "sha512-tD40eHxA35h0PEIZNeIjkHoDR4YjjJp34biM0mDvplBe//mB+IHCqHDGV7pxF+7MklTvighcCPPZC7ynWyjdTA==", + "node_modules/@typescript-eslint/type-utils/node_modules/@typescript-eslint/project-service": { + "version": "8.64.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.64.0.tgz", + "integrity": "sha512-tk4WpOJ6IEbGrVHaNmM0YRrwAD3exZlIK3iadQNAxh4YKk6jvUQ4ecq18n+v7+meh+cJ3j+D8nbk8sRKhlwLQg==", "dev": true, - "license": "Apache-2.0", + "license": "MIT", + "dependencies": { + "@typescript-eslint/tsconfig-utils": "^8.64.0", + "@typescript-eslint/types": "^8.64.0", + "debug": "^4.4.3" + }, "engines": { - "node": "^20.19.0 || ^22.13.0 || >=24" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { - "url": "https://opencollective.com/eslint" + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <6.1.0" } }, - "node_modules/@typescript-eslint/eslint-plugin/node_modules/ignore": { - "version": "7.0.5", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.5.tgz", - "integrity": "sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==", + "node_modules/@typescript-eslint/type-utils/node_modules/@typescript-eslint/tsconfig-utils": { + "version": "8.64.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.64.0.tgz", + "integrity": "sha512-2yo8rRNKuzbVWQp5kslhANqZ2uDAeROQHBRZNPu8JDsHmeFNj/XJJhX/FhNUWmkHHvoNsKa6+tHJiig87EzsQw==", "dev": true, "license": "MIT", "engines": { - "node": ">= 4" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <6.1.0" } }, - "node_modules/@typescript-eslint/eslint-plugin/node_modules/minimatch": { - "version": "10.2.4", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.4.tgz", - "integrity": "sha512-oRjTw/97aTBN0RHbYCdtF1MQfvusSIBQM0IZEgzl6426+8jSC0nF1a/GmnVLpfB9yyr6g6FTqWqiZVbxrtaCIg==", + "node_modules/@typescript-eslint/type-utils/node_modules/@typescript-eslint/types": { + "version": "8.64.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.64.0.tgz", + "integrity": "sha512-qjhfuTfLXjA4IOzXvz0rTjT01BqEiIgPoUeMwiEjnaHKJMTNo8rH5pYW1a2L/0Dnux2fPC85AeyJoWaGa8WxTA==", "dev": true, - "license": "BlueOak-1.0.0", - "dependencies": { - "brace-expansion": "^5.0.2" - }, + "license": "MIT", "engines": { - "node": "18 || 20 || >=22" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { - "url": "https://github.com/sponsors/isaacs" + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" } }, - "node_modules/@typescript-eslint/parser": { - "version": "8.57.2", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.57.2.tgz", - "integrity": "sha512-30ScMRHIAD33JJQkgfGW1t8CURZtjc2JpTrq5n2HFhOefbAhb7ucc7xJwdWcrEtqUIYJ73Nybpsggii6GtAHjA==", + "node_modules/@typescript-eslint/type-utils/node_modules/@typescript-eslint/typescript-estree": { + "version": "8.64.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.64.0.tgz", + "integrity": "sha512-Pztpsn1aCE1oWDvDEfUk31nngvvF7vUB5SwHFEaZIFpvw7WJtqUHHL4plBZDA9HfWJJjL13BdG0YrJInTUvoVA==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/scope-manager": "8.57.2", - "@typescript-eslint/types": "8.57.2", - "@typescript-eslint/typescript-estree": "8.57.2", - "@typescript-eslint/visitor-keys": "8.57.2", - "debug": "^4.4.3" + "@typescript-eslint/project-service": "8.64.0", + "@typescript-eslint/tsconfig-utils": "8.64.0", + "@typescript-eslint/types": "8.64.0", + "@typescript-eslint/visitor-keys": "8.64.0", + "debug": "^4.4.3", + "minimatch": "^10.2.2", + "semver": "^7.7.3", + "tinyglobby": "^0.2.15", + "ts-api-utils": "^2.5.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -5491,32 +5643,52 @@ "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", - "typescript": ">=4.8.4 <6.0.0" + "typescript": ">=4.8.4 <6.1.0" } }, - "node_modules/@typescript-eslint/parser/node_modules/@typescript-eslint/scope-manager": { - "version": "8.57.2", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.57.2.tgz", - "integrity": "sha512-snZKH+W4WbWkrBqj4gUNRIGb/jipDW3qMqVJ4C9rzdFc+wLwruxk+2a5D+uoFcKPAqyqEnSb4l2ULuZf95eSkw==", + "node_modules/@typescript-eslint/type-utils/node_modules/balanced-match": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz", + "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "18 || 20 || >=22" + } + }, + "node_modules/@typescript-eslint/type-utils/node_modules/brace-expansion": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.8.tgz", + "integrity": "sha512-JZyDyq3D4AUifKTPOB7DELf6XsB3WdPuNxCtob1vFXPsSXhdAiHBWJ/tJ8HAc9aH84BK+5JFZLNkJKx3G9kzQg==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.57.2", - "@typescript-eslint/visitor-keys": "8.57.2" + "balanced-match": "^4.0.2" }, "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + "node": "20 || >=22" + } + }, + "node_modules/@typescript-eslint/type-utils/node_modules/minimatch": { + "version": "10.2.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.5.tgz", + "integrity": "sha512-MULkVLfKGYDFYejP07QOurDLLQpcjk7Fw+7jXS2R2czRQzR56yHRveU5NDJEOviH+hETZKSkIk5c+T23GjFUMg==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "brace-expansion": "^5.0.5" + }, + "engines": { + "node": "18 || 20 || >=22" }, "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" + "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/@typescript-eslint/parser/node_modules/@typescript-eslint/types": { - "version": "8.57.2", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.57.2.tgz", - "integrity": "sha512-/iZM6FnM4tnx9csuTxspMW4BOSegshwX5oBDznJ7S4WggL7Vczz5d2W11ecc4vRrQMQHXRSxzrCsyG5EsPPTbA==", + "node_modules/@typescript-eslint/types": { + "version": "8.65.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.65.0.tgz", + "integrity": "sha512-JSSwWNy+H0E/01jJEM+hrX6N0OFDzFzeIhHFSAS01tlVaevpG8cFyYRPhS5yjGOvBUx3sqQHVMjCL1CAZZMxBg==", "dev": true, "license": "MIT", "engines": { @@ -5527,22 +5699,22 @@ "url": "https://opencollective.com/typescript-eslint" } }, - "node_modules/@typescript-eslint/parser/node_modules/@typescript-eslint/typescript-estree": { - "version": "8.57.2", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.57.2.tgz", - "integrity": "sha512-2MKM+I6g8tJxfSmFKOnHv2t8Sk3T6rF20A1Puk0svLK+uVapDZB/4pfAeB7nE83uAZrU6OxW+HmOd5wHVdXwXA==", + "node_modules/@typescript-eslint/typescript-estree": { + "version": "8.65.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.65.0.tgz", + "integrity": "sha512-JboAE2swaYt4tb1fHhHTABE2K+OLy09XfcTbhnk4Pw96f9dd2e9iYsJ28gBggHlo5z5x1rkyWvcPoTuNTd4oGg==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/project-service": "8.57.2", - "@typescript-eslint/tsconfig-utils": "8.57.2", - "@typescript-eslint/types": "8.57.2", - "@typescript-eslint/visitor-keys": "8.57.2", + "@typescript-eslint/project-service": "8.65.0", + "@typescript-eslint/tsconfig-utils": "8.65.0", + "@typescript-eslint/types": "8.65.0", + "@typescript-eslint/visitor-keys": "8.65.0", "debug": "^4.4.3", "minimatch": "^10.2.2", "semver": "^7.7.3", "tinyglobby": "^0.2.15", - "ts-api-utils": "^2.4.0" + "ts-api-utils": "^2.5.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -5552,17 +5724,17 @@ "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "typescript": ">=4.8.4 <6.0.0" + "typescript": ">=4.8.4 <6.1.0" } }, - "node_modules/@typescript-eslint/parser/node_modules/@typescript-eslint/visitor-keys": { - "version": "8.57.2", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.57.2.tgz", - "integrity": "sha512-zhahknjobV2FiD6Ee9iLbS7OV9zi10rG26odsQdfBO/hjSzUQbkIYgda+iNKK1zNiW2ey+Lf8MU5btN17V3dUw==", + "node_modules/@typescript-eslint/typescript-estree/node_modules/@typescript-eslint/visitor-keys": { + "version": "8.65.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.65.0.tgz", + "integrity": "sha512-8C71BQkGjiMmXtop7pHVJu1l2NNShFdkCyD6a2ezzs5vU/L3LRtb69EtcteFwz0mYMPzIgOw0n6OV4VBUWZd7A==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.57.2", + "@typescript-eslint/types": "8.65.0", "eslint-visitor-keys": "^5.0.0" }, "engines": { @@ -5573,7 +5745,7 @@ "url": "https://opencollective.com/typescript-eslint" } }, - "node_modules/@typescript-eslint/parser/node_modules/balanced-match": { + "node_modules/@typescript-eslint/typescript-estree/node_modules/balanced-match": { "version": "4.0.4", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz", "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==", @@ -5583,20 +5755,20 @@ "node": "18 || 20 || >=22" } }, - "node_modules/@typescript-eslint/parser/node_modules/brace-expansion": { - "version": "5.0.5", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.5.tgz", - "integrity": "sha512-VZznLgtwhn+Mact9tfiwx64fA9erHH/MCXEUfB/0bX/6Fz6ny5EGTXYltMocqg4xFAQZtnO3DHWWXi8RiuN7cQ==", + "node_modules/@typescript-eslint/typescript-estree/node_modules/brace-expansion": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.8.tgz", + "integrity": "sha512-JZyDyq3D4AUifKTPOB7DELf6XsB3WdPuNxCtob1vFXPsSXhdAiHBWJ/tJ8HAc9aH84BK+5JFZLNkJKx3G9kzQg==", "dev": true, "license": "MIT", "dependencies": { "balanced-match": "^4.0.2" }, "engines": { - "node": "18 || 20 || >=22" + "node": "20 || >=22" } }, - "node_modules/@typescript-eslint/parser/node_modules/eslint-visitor-keys": { + "node_modules/@typescript-eslint/typescript-estree/node_modules/eslint-visitor-keys": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-5.0.1.tgz", "integrity": "sha512-tD40eHxA35h0PEIZNeIjkHoDR4YjjJp34biM0mDvplBe//mB+IHCqHDGV7pxF+7MklTvighcCPPZC7ynWyjdTA==", @@ -5609,14 +5781,14 @@ "url": "https://opencollective.com/eslint" } }, - "node_modules/@typescript-eslint/parser/node_modules/minimatch": { - "version": "10.2.4", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.4.tgz", - "integrity": "sha512-oRjTw/97aTBN0RHbYCdtF1MQfvusSIBQM0IZEgzl6426+8jSC0nF1a/GmnVLpfB9yyr6g6FTqWqiZVbxrtaCIg==", + "node_modules/@typescript-eslint/typescript-estree/node_modules/minimatch": { + "version": "10.2.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.5.tgz", + "integrity": "sha512-MULkVLfKGYDFYejP07QOurDLLQpcjk7Fw+7jXS2R2czRQzR56yHRveU5NDJEOviH+hETZKSkIk5c+T23GjFUMg==", "dev": true, "license": "BlueOak-1.0.0", "dependencies": { - "brace-expansion": "^5.0.2" + "brace-expansion": "^5.0.5" }, "engines": { "node": "18 || 20 || >=22" @@ -5625,16 +5797,17 @@ "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/@typescript-eslint/project-service": { - "version": "8.57.2", - "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.57.2.tgz", - "integrity": "sha512-FuH0wipFywXRTHf+bTTjNyuNQQsQC3qh/dYzaM4I4W0jrCqjCVuUh99+xd9KamUfmCGPvbO8NDngo/vsnNVqgw==", + "node_modules/@typescript-eslint/utils": { + "version": "8.64.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.64.0.tgz", + "integrity": "sha512-aJUGVB3+U0htrrCjoA8qukw8cm8fNCGAxK/tVoS70k8aeb7DETKeFozRiVFIwEeN9WJLsjaP3ph8I60tY2XZoQ==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/tsconfig-utils": "^8.57.2", - "@typescript-eslint/types": "^8.57.2", - "debug": "^4.4.3" + "@eslint-community/eslint-utils": "^4.9.1", + "@typescript-eslint/scope-manager": "8.64.0", + "@typescript-eslint/types": "8.64.0", + "@typescript-eslint/typescript-estree": "8.64.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -5644,27 +5817,36 @@ "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "typescript": ">=4.8.4 <6.0.0" + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", + "typescript": ">=4.8.4 <6.1.0" } }, - "node_modules/@typescript-eslint/project-service/node_modules/@typescript-eslint/types": { - "version": "8.57.2", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.57.2.tgz", - "integrity": "sha512-/iZM6FnM4tnx9csuTxspMW4BOSegshwX5oBDznJ7S4WggL7Vczz5d2W11ecc4vRrQMQHXRSxzrCsyG5EsPPTbA==", + "node_modules/@typescript-eslint/utils/node_modules/@typescript-eslint/project-service": { + "version": "8.64.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.64.0.tgz", + "integrity": "sha512-tk4WpOJ6IEbGrVHaNmM0YRrwAD3exZlIK3iadQNAxh4YKk6jvUQ4ecq18n+v7+meh+cJ3j+D8nbk8sRKhlwLQg==", "dev": true, "license": "MIT", + "dependencies": { + "@typescript-eslint/tsconfig-utils": "^8.64.0", + "@typescript-eslint/types": "^8.64.0", + "debug": "^4.4.3" + }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <6.1.0" } }, - "node_modules/@typescript-eslint/tsconfig-utils": { - "version": "8.57.2", - "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.57.2.tgz", - "integrity": "sha512-3Lm5DSM+DCowsUOJC+YqHHnKEfFh5CoGkj5Z31NQSNF4l5wdOwqGn99wmwN/LImhfY3KJnmordBq/4+VDe2eKw==", + "node_modules/@typescript-eslint/utils/node_modules/@typescript-eslint/tsconfig-utils": { + "version": "8.64.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.64.0.tgz", + "integrity": "sha512-2yo8rRNKuzbVWQp5kslhANqZ2uDAeROQHBRZNPu8JDsHmeFNj/XJJhX/FhNUWmkHHvoNsKa6+tHJiig87EzsQw==", "dev": true, "license": "MIT", "engines": { @@ -5675,43 +5857,39 @@ "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "typescript": ">=4.8.4 <6.0.0" + "typescript": ">=4.8.4 <6.1.0" } }, - "node_modules/@typescript-eslint/type-utils": { - "version": "8.57.2", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.57.2.tgz", - "integrity": "sha512-Co6ZCShm6kIbAM/s+oYVpKFfW7LBc6FXoPXjTRQ449PPNBY8U0KZXuevz5IFuuUj2H9ss40atTaf9dlGLzbWZg==", + "node_modules/@typescript-eslint/utils/node_modules/@typescript-eslint/types": { + "version": "8.64.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.64.0.tgz", + "integrity": "sha512-qjhfuTfLXjA4IOzXvz0rTjT01BqEiIgPoUeMwiEjnaHKJMTNo8rH5pYW1a2L/0Dnux2fPC85AeyJoWaGa8WxTA==", "dev": true, "license": "MIT", - "dependencies": { - "@typescript-eslint/types": "8.57.2", - "@typescript-eslint/typescript-estree": "8.57.2", - "@typescript-eslint/utils": "8.57.2", - "debug": "^4.4.3", - "ts-api-utils": "^2.4.0" - }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", - "typescript": ">=4.8.4 <6.0.0" } }, - "node_modules/@typescript-eslint/type-utils/node_modules/@typescript-eslint/scope-manager": { - "version": "8.57.2", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.57.2.tgz", - "integrity": "sha512-snZKH+W4WbWkrBqj4gUNRIGb/jipDW3qMqVJ4C9rzdFc+wLwruxk+2a5D+uoFcKPAqyqEnSb4l2ULuZf95eSkw==", + "node_modules/@typescript-eslint/utils/node_modules/@typescript-eslint/typescript-estree": { + "version": "8.64.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.64.0.tgz", + "integrity": "sha512-Pztpsn1aCE1oWDvDEfUk31nngvvF7vUB5SwHFEaZIFpvw7WJtqUHHL4plBZDA9HfWJJjL13BdG0YrJInTUvoVA==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.57.2", - "@typescript-eslint/visitor-keys": "8.57.2" + "@typescript-eslint/project-service": "8.64.0", + "@typescript-eslint/tsconfig-utils": "8.64.0", + "@typescript-eslint/types": "8.64.0", + "@typescript-eslint/visitor-keys": "8.64.0", + "debug": "^4.4.3", + "minimatch": "^10.2.2", + "semver": "^7.7.3", + "tinyglobby": "^0.2.15", + "ts-api-utils": "^2.5.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -5719,82 +5897,58 @@ "funding": { "type": "opencollective", "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <6.1.0" } }, - "node_modules/@typescript-eslint/type-utils/node_modules/@typescript-eslint/types": { - "version": "8.57.2", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.57.2.tgz", - "integrity": "sha512-/iZM6FnM4tnx9csuTxspMW4BOSegshwX5oBDznJ7S4WggL7Vczz5d2W11ecc4vRrQMQHXRSxzrCsyG5EsPPTbA==", + "node_modules/@typescript-eslint/utils/node_modules/balanced-match": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz", + "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==", "dev": true, "license": "MIT", "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" + "node": "18 || 20 || >=22" } }, - "node_modules/@typescript-eslint/type-utils/node_modules/@typescript-eslint/typescript-estree": { - "version": "8.57.2", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.57.2.tgz", - "integrity": "sha512-2MKM+I6g8tJxfSmFKOnHv2t8Sk3T6rF20A1Puk0svLK+uVapDZB/4pfAeB7nE83uAZrU6OxW+HmOd5wHVdXwXA==", + "node_modules/@typescript-eslint/utils/node_modules/brace-expansion": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.8.tgz", + "integrity": "sha512-JZyDyq3D4AUifKTPOB7DELf6XsB3WdPuNxCtob1vFXPsSXhdAiHBWJ/tJ8HAc9aH84BK+5JFZLNkJKx3G9kzQg==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/project-service": "8.57.2", - "@typescript-eslint/tsconfig-utils": "8.57.2", - "@typescript-eslint/types": "8.57.2", - "@typescript-eslint/visitor-keys": "8.57.2", - "debug": "^4.4.3", - "minimatch": "^10.2.2", - "semver": "^7.7.3", - "tinyglobby": "^0.2.15", - "ts-api-utils": "^2.4.0" + "balanced-match": "^4.0.2" }, "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "typescript": ">=4.8.4 <6.0.0" + "node": "20 || >=22" } }, - "node_modules/@typescript-eslint/type-utils/node_modules/@typescript-eslint/utils": { - "version": "8.57.2", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.57.2.tgz", - "integrity": "sha512-krRIbvPK1ju1WBKIefiX+bngPs+odIQUtR7kymzPfo1POVw3jlF+nLkmexdSSd4UCbDcQn+wMBATOOmpBbqgKg==", + "node_modules/@typescript-eslint/utils/node_modules/minimatch": { + "version": "10.2.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.5.tgz", + "integrity": "sha512-MULkVLfKGYDFYejP07QOurDLLQpcjk7Fw+7jXS2R2czRQzR56yHRveU5NDJEOviH+hETZKSkIk5c+T23GjFUMg==", "dev": true, - "license": "MIT", + "license": "BlueOak-1.0.0", "dependencies": { - "@eslint-community/eslint-utils": "^4.9.1", - "@typescript-eslint/scope-manager": "8.57.2", - "@typescript-eslint/types": "8.57.2", - "@typescript-eslint/typescript-estree": "8.57.2" + "brace-expansion": "^5.0.5" }, "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + "node": "18 || 20 || >=22" }, "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", - "typescript": ">=4.8.4 <6.0.0" + "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/@typescript-eslint/type-utils/node_modules/@typescript-eslint/visitor-keys": { - "version": "8.57.2", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.57.2.tgz", - "integrity": "sha512-zhahknjobV2FiD6Ee9iLbS7OV9zi10rG26odsQdfBO/hjSzUQbkIYgda+iNKK1zNiW2ey+Lf8MU5btN17V3dUw==", + "node_modules/@typescript-eslint/visitor-keys": { + "version": "8.64.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.64.0.tgz", + "integrity": "sha512-mrtuL8Nsn6gi2H4mo5KMTp823M+3Q19Ew/i+Zlikq20tIMm99C3Ez0dCmkWWnxut20esQvTg8aUSEhMcAOXhEw==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.57.2", + "@typescript-eslint/types": "8.64.0", "eslint-visitor-keys": "^5.0.0" }, "engines": { @@ -5805,30 +5959,21 @@ "url": "https://opencollective.com/typescript-eslint" } }, - "node_modules/@typescript-eslint/type-utils/node_modules/balanced-match": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz", - "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==", + "node_modules/@typescript-eslint/visitor-keys/node_modules/@typescript-eslint/types": { + "version": "8.64.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.64.0.tgz", + "integrity": "sha512-qjhfuTfLXjA4IOzXvz0rTjT01BqEiIgPoUeMwiEjnaHKJMTNo8rH5pYW1a2L/0Dnux2fPC85AeyJoWaGa8WxTA==", "dev": true, "license": "MIT", "engines": { - "node": "18 || 20 || >=22" - } - }, - "node_modules/@typescript-eslint/type-utils/node_modules/brace-expansion": { - "version": "5.0.5", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.5.tgz", - "integrity": "sha512-VZznLgtwhn+Mact9tfiwx64fA9erHH/MCXEUfB/0bX/6Fz6ny5EGTXYltMocqg4xFAQZtnO3DHWWXi8RiuN7cQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "balanced-match": "^4.0.2" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, - "engines": { - "node": "18 || 20 || >=22" + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" } }, - "node_modules/@typescript-eslint/type-utils/node_modules/eslint-visitor-keys": { + "node_modules/@typescript-eslint/visitor-keys/node_modules/eslint-visitor-keys": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-5.0.1.tgz", "integrity": "sha512-tD40eHxA35h0PEIZNeIjkHoDR4YjjJp34biM0mDvplBe//mB+IHCqHDGV7pxF+7MklTvighcCPPZC7ynWyjdTA==", @@ -5841,26 +5986,10 @@ "url": "https://opencollective.com/eslint" } }, - "node_modules/@typescript-eslint/type-utils/node_modules/minimatch": { - "version": "10.2.4", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.4.tgz", - "integrity": "sha512-oRjTw/97aTBN0RHbYCdtF1MQfvusSIBQM0IZEgzl6426+8jSC0nF1a/GmnVLpfB9yyr6g6FTqWqiZVbxrtaCIg==", - "dev": true, - "license": "BlueOak-1.0.0", - "dependencies": { - "brace-expansion": "^5.0.2" - }, - "engines": { - "node": "18 || 20 || >=22" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, "node_modules/@vitejs/plugin-react": { - "version": "5.1.4", - "resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-5.1.4.tgz", - "integrity": "sha512-VIcFLdRi/VYRU8OL/puL7QXMYafHmqOnwTZY50U1JPlCNj30PxCMx65c494b1K9be9hX83KVt0+gTEwTWLqToA==", + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-5.2.0.tgz", + "integrity": "sha512-YmKkfhOAi3wsB1PhJq5Scj3GXMn3WvtQ/JC0xoopuHoXSdmtdStOpFrYaT1kie2YgFBcIe64ROzMYRjCrYOdYw==", "dev": true, "license": "MIT", "dependencies": { @@ -5875,7 +6004,7 @@ "node": "^20.19.0 || >=22.12.0" }, "peerDependencies": { - "vite": "^4.2.0 || ^5.0.0 || ^6.0.0 || ^7.0.0" + "vite": "^4.2.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0" } }, "node_modules/@vitejs/plugin-react/node_modules/react-refresh": { @@ -5889,31 +6018,31 @@ } }, "node_modules/@vitest/expect": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-4.1.0.tgz", - "integrity": "sha512-EIxG7k4wlWweuCLG9Y5InKFwpMEOyrMb6ZJ1ihYu02LVj/bzUwn2VMU+13PinsjRW75XnITeFrQBMH5+dLvCDA==", + "version": "4.1.10", + "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-4.1.10.tgz", + "integrity": "sha512-YsCn+qAk1GWjQOWFEsEcL2gNQ0zmVmQu3T03qP6UyjhtmdtwtbuI+DASn/7iQB3HGTXkdBwGddzxPlmiql5vlA==", "dev": true, "license": "MIT", "dependencies": { "@standard-schema/spec": "^1.1.0", "@types/chai": "^5.2.2", - "@vitest/spy": "4.1.0", - "@vitest/utils": "4.1.0", + "@vitest/spy": "4.1.10", + "@vitest/utils": "4.1.10", "chai": "^6.2.2", - "tinyrainbow": "^3.0.3" + "tinyrainbow": "^3.1.0" }, "funding": { "url": "https://opencollective.com/vitest" } }, "node_modules/@vitest/mocker": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-4.1.0.tgz", - "integrity": "sha512-evxREh+Hork43+Y4IOhTo+h5lGmVRyjqI739Rz4RlUPqwrkFFDF6EMvOOYjTx4E8Tl6gyCLRL8Mu7Ry12a13Tw==", + "version": "4.1.10", + "resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-4.1.10.tgz", + "integrity": "sha512-v0xaezt+DKEmKfaxg133ldzADrwLGd7Ze1MfQQTYfvs8OqZIwbxyxaYURivwV7sWy5fqn3rH5uOrSp07bp44Ow==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/spy": "4.1.0", + "@vitest/spy": "4.1.10", "estree-walker": "^3.0.3", "magic-string": "^0.30.21" }, @@ -5922,7 +6051,7 @@ }, "peerDependencies": { "msw": "^2.4.9", - "vite": "^6.0.0 || ^7.0.0 || ^8.0.0-0" + "vite": "^6.0.0 || ^7.0.0 || ^8.0.0" }, "peerDependenciesMeta": { "msw": { @@ -5934,26 +6063,26 @@ } }, "node_modules/@vitest/pretty-format": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-4.1.0.tgz", - "integrity": "sha512-3RZLZlh88Ib0J7NQTRATfc/3ZPOnSUn2uDBUoGNn5T36+bALixmzphN26OUD3LRXWkJu4H0s5vvUeqBiw+kS0A==", + "version": "4.1.10", + "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-4.1.10.tgz", + "integrity": "sha512-W1HsjSH4MXQ9YfmmhLAoIYf1HRfekQCGngeIgcei6MP5QQGWUe0gkopdZQaVCFO+JDJMrAJGwa5pRpNpvy4P8Q==", "dev": true, "license": "MIT", "dependencies": { - "tinyrainbow": "^3.0.3" + "tinyrainbow": "^3.1.0" }, "funding": { "url": "https://opencollective.com/vitest" } }, "node_modules/@vitest/runner": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-4.1.0.tgz", - "integrity": "sha512-Duvx2OzQ7d6OjchL+trw+aSrb9idh7pnNfxrklo14p3zmNL4qPCDeIJAK+eBKYjkIwG96Bc6vYuxhqDXQOWpoQ==", + "version": "4.1.10", + "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-4.1.10.tgz", + "integrity": "sha512-IKI6kpIH+LmpROplyLwBBaCfMgOZOMsygVa6BARD6ahA04VRuJSa6OaVG7kRvSEMD870Vd91rSSw0eegtWyLGg==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/utils": "4.1.0", + "@vitest/utils": "4.1.10", "pathe": "^2.0.3" }, "funding": { @@ -5961,14 +6090,14 @@ } }, "node_modules/@vitest/snapshot": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-4.1.0.tgz", - "integrity": "sha512-0Vy9euT1kgsnj1CHttwi9i9o+4rRLEaPRSOJ5gyv579GJkNpgJK+B4HSv/rAWixx2wdAFci1X4CEPjiu2bXIMg==", + "version": "4.1.10", + "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-4.1.10.tgz", + "integrity": "sha512-xRkfOT1qpTAi/Ti4Y1LtfRc3kEuqxGw59eN2jN9pRWMtS/XDevekhcFSqvQqjUNGksfjMJu3Y+oJ+4Ypn2OaJw==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/pretty-format": "4.1.0", - "@vitest/utils": "4.1.0", + "@vitest/pretty-format": "4.1.10", + "@vitest/utils": "4.1.10", "magic-string": "^0.30.21", "pathe": "^2.0.3" }, @@ -5977,9 +6106,9 @@ } }, "node_modules/@vitest/spy": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-4.1.0.tgz", - "integrity": "sha512-pz77k+PgNpyMDv2FV6qmk5ZVau6c3R8HC8v342T2xlFxQKTrSeYw9waIJG8KgV9fFwAtTu4ceRzMivPTH6wSxw==", + "version": "4.1.10", + "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-4.1.10.tgz", + "integrity": "sha512-PLf/Ugvoq5wO/b4rwYCR1h2PSIdXz7wnkQFMiUpLdtM7l6pqVFcQIBEHyT1+l+cj7mNwAfZHzqXqDyjvOuwbDw==", "dev": true, "license": "MIT", "funding": { @@ -5987,15 +6116,15 @@ } }, "node_modules/@vitest/utils": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-4.1.0.tgz", - "integrity": "sha512-XfPXT6a8TZY3dcGY8EdwsBulFCIw+BeeX0RZn2x/BtiY/75YGh8FeWGG8QISN/WhaqSrE2OrlDgtF8q5uhOTmw==", + "version": "4.1.10", + "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-4.1.10.tgz", + "integrity": "sha512-fy9am/HWxbaGt/Sawrp90vt6Y6jQwf1RX77cz3uwoJwJVMli/e1IEwRPnMNJ7vKfPTwo0diXifkpPvwH9v7nGA==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/pretty-format": "4.1.0", + "@vitest/pretty-format": "4.1.10", "convert-source-map": "^2.0.0", - "tinyrainbow": "^3.0.3" + "tinyrainbow": "^3.1.0" }, "funding": { "url": "https://opencollective.com/vitest" @@ -6407,9 +6536,9 @@ "integrity": "sha512-7HhHjtERjqlNbZtqNqy2rckN/SpOOlmDliet+lP7k+eKZEjPk3DgyeU9lIXLdeLz0uBbbVp+9Qdow9wJWgwwfg==" }, "node_modules/autoprefixer": { - "version": "10.4.13", - "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.13.tgz", - "integrity": "sha512-49vKpMqcZYsJjwotvt4+h/BCjJVnhGwcLpDt5xkcaOG3eLrG/HUYLagrihYsQ+qrIBgIzX1Rw7a6L8I/ZA1Atg==", + "version": "10.5.4", + "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.5.4.tgz", + "integrity": "sha512-MaU0U/za7N3r6brxD4YB/l4NSrFzLPlANv6wEuQVaIPlD3L4W9rFcQPbL/EilY9BHhHvhfcz3gInDLrEtWT4EA==", "dev": true, "funding": [ { @@ -6419,14 +6548,18 @@ { "type": "tidelift", "url": "https://tidelift.com/funding/github/npm/autoprefixer" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" } ], + "license": "MIT", "dependencies": { - "browserslist": "^4.21.4", - "caniuse-lite": "^1.0.30001426", - "fraction.js": "^4.2.0", - "normalize-range": "^0.1.2", - "picocolors": "^1.0.0", + "browserslist": "^4.28.6", + "caniuse-lite": "^1.0.30001806", + "fraction.js": "^5.3.4", + "picocolors": "^1.1.1", "postcss-value-parser": "^4.2.0" }, "bin": { @@ -6461,13 +6594,16 @@ "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" }, "node_modules/baseline-browser-mapping": { - "version": "2.9.19", - "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.9.19.tgz", - "integrity": "sha512-ipDqC8FrAl/76p2SSWKSI+H9tFwm7vYqXQrItCuiVPt26Km0jS+NzSsBWAaBusvSbQcfJG+JitdMm+wZAgTYqg==", + "version": "2.11.5", + "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.11.5.tgz", + "integrity": "sha512-xJo6a6YZnwZfnyGmQKWMbVOcii7XRibjOskRh+WJ9UHQoX16xrQrcIgAMQOzfvs8XiLMx6ih/fsLPF73iY2D1A==", "dev": true, "license": "Apache-2.0", "bin": { - "baseline-browser-mapping": "dist/cli.js" + "baseline-browser-mapping": "dist/cli.cjs" + }, + "engines": { + "node": ">=6.0.0" } }, "node_modules/better-opn": { @@ -6510,9 +6646,9 @@ } }, "node_modules/brace-expansion": { - "version": "1.1.13", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.13.tgz", - "integrity": "sha512-9ZLprWS6EENmhEOpjCYW2c8VkmOvckIJZfkr7rBW6dObmfgJ/L1GpSYW5Hpo9lDz4D1+n0Ckz8rU7FwHDQiG/w==", + "version": "1.1.16", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.16.tgz", + "integrity": "sha512-IDw48K2/2kRkg9LdJxurvq3lV3aBgq0REY89duEqFRthjlPdXHKMj7EnQOXVckxzgisinf3nHfrcE2FufFLXMw==", "license": "MIT", "dependencies": { "balanced-match": "^1.0.0", @@ -6547,9 +6683,9 @@ } }, "node_modules/browserslist": { - "version": "4.28.1", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.28.1.tgz", - "integrity": "sha512-ZC5Bd0LgJXgwGqUknZY/vkUQ04r8NXnJZ3yYi4vDmSiZmC/pdSN0NbNRPxZpbtO4uAfDUAFffO8IZoM3Gj8IkA==", + "version": "4.28.7", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.28.7.tgz", + "integrity": "sha512-JxV13hNrFxqjOc8alRbq9dK1MM79NEXYpma2B2J4wAtpWS5zIEIKqWPGCl7N4o7Uc7B7itylh7SuDujATRyyTw==", "dev": true, "funding": [ { @@ -6567,11 +6703,11 @@ ], "license": "MIT", "dependencies": { - "baseline-browser-mapping": "^2.9.0", - "caniuse-lite": "^1.0.30001759", - "electron-to-chromium": "^1.5.263", - "node-releases": "^2.0.27", - "update-browserslist-db": "^1.2.0" + "baseline-browser-mapping": "^2.10.44", + "caniuse-lite": "^1.0.30001806", + "electron-to-chromium": "^1.5.393", + "node-releases": "^2.0.51", + "update-browserslist-db": "^1.2.3" }, "bin": { "browserslist": "cli.js" @@ -6660,9 +6796,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001767", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001767.tgz", - "integrity": "sha512-34+zUAMhSH+r+9eKmYG+k2Rpt8XttfE4yXAjoZvkAPs15xcYQhyBYdalJ65BzivAvGRMViEjy6oKr/S91loekQ==", + "version": "1.0.30001806", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001806.tgz", + "integrity": "sha512-72Cuvd95zbSYPKq6Fhg8eDJRlzgWDf7/mtoZv6Qe/DYNCEBdNxoA3+rZAU2ZhGCpZlns3EssFavaZomckT5Uuw==", "dev": true, "funding": [ { @@ -7303,9 +7439,9 @@ } }, "node_modules/electron-to-chromium": { - "version": "1.5.286", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.286.tgz", - "integrity": "sha512-9tfDXhJ4RKFNerfjdCcZfufu49vg620741MNs26a9+bhLThdB+plgMeou98CAaHu/WATj2iHOOHTp1hWtABj2A==", + "version": "1.5.396", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.396.tgz", + "integrity": "sha512-yHiw2Y3C3H9U6TMbOfoWK/BPreiOPXRfTWPBwQBoZG6/8TB6eOPnsy5oaRYuatR7Fw2SJ4kKforgufeo7fq0EQ==", "dev": true, "license": "ISC" }, @@ -7603,9 +7739,9 @@ } }, "node_modules/eslint": { - "version": "9.39.4", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.39.4.tgz", - "integrity": "sha512-XoMjdBOwe/esVgEvLmNsD3IRHkm7fbKIUGvrleloJXUZgDHig2IPWNniv+GwjyJXzuNqVjlr5+4yVUZjycJwfQ==", + "version": "9.39.5", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.39.5.tgz", + "integrity": "sha512-DgZS62aPLXKlnxILS/AYCoRvHaZeXceIzlXPkkGGzJWSow1aEk0lbTlxUSlyjC8jcaKxAdOnTDz+o1JFSBsyjw==", "dev": true, "license": "MIT", "dependencies": { @@ -7614,8 +7750,8 @@ "@eslint/config-array": "^0.21.2", "@eslint/config-helpers": "^0.4.2", "@eslint/core": "^0.17.0", - "@eslint/eslintrc": "^3.3.5", - "@eslint/js": "9.39.4", + "@eslint/eslintrc": "^3.3.6", + "@eslint/js": "9.39.5", "@eslint/plugin-kit": "^0.4.1", "@humanfs/node": "^0.16.6", "@humanwhocodes/module-importer": "^1.0.1", @@ -7700,9 +7836,9 @@ } }, "node_modules/eslint-plugin-react-hooks": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-7.0.1.tgz", - "integrity": "sha512-O0d0m04evaNzEPoSW+59Mezf8Qt0InfgGIBJnpC0h3NH/WjUAR7BIKUfysC6todmtiZ/A0oUVS8Gce0WhBrHsA==", + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-7.1.1.tgz", + "integrity": "sha512-f2I7Gw6JbvCexzIInuSbZpfdQ44D7iqdWX01FKLvrPgqxoE7oMj8clOfto8U6vYiz4yd5oKu39rRSVOe1zRu0g==", "dev": true, "license": "MIT", "dependencies": { @@ -7716,7 +7852,7 @@ "node": ">=18" }, "peerDependencies": { - "eslint": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0 || ^9.0.0" + "eslint": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0 || ^9.0.0 || ^10.0.0" } }, "node_modules/eslint-plugin-react/node_modules/doctrine": { @@ -8100,9 +8236,9 @@ } }, "node_modules/filelist/node_modules/brace-expansion": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.3.tgz", - "integrity": "sha512-MCV/fYJEbqx68aE58kv2cA/kiky1G8vux3OR6/jbS+jIMe/6fJWa0DTzJU7dqijOWYwHi1t29FlfYI9uytqlpA==", + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.1.3.tgz", + "integrity": "sha512-DRdx5neNsG/QXbniLFWi2YmC/68oeOOmKz6zOjVk6ZS1ZLXgLIKqVEc6hWsmkjBbgii0SwaBTcJ5XKj5gzY/4A==", "dev": true, "license": "MIT", "dependencies": { @@ -8228,16 +8364,17 @@ } }, "node_modules/fraction.js": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-4.2.0.tgz", - "integrity": "sha512-MhLuK+2gUcnZe8ZHlaaINnQLl0xRIGRfcGk2yl8xoQAfHrSsL3rYu6FCmBdkdbhc9EPlwyGHewaRsvwRMJtAlA==", + "version": "5.3.4", + "resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-5.3.4.tgz", + "integrity": "sha512-1X1NTtiJphryn/uLQz3whtY6jK3fTqoE3ohKs0tT+Ujr1W59oopxmoEh7Lu5p6vBaPbgoM0bzveAW4Qi5RyWDQ==", "dev": true, + "license": "MIT", "engines": { "node": "*" }, "funding": { - "type": "patreon", - "url": "https://www.patreon.com/infusion" + "type": "github", + "url": "https://github.com/sponsors/rawify" } }, "node_modules/fs.realpath": { @@ -9524,10 +9661,20 @@ "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==" }, "node_modules/js-yaml": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.1.tgz", - "integrity": "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==", + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.3.0.tgz", + "integrity": "sha512-1td788aAnnZ5qs7V2QIRl1owjtYpbKt749Y3xauqQgwIIGF/xXWz1wMTEBx5O3LK3lXLVuqXPdPxj2BoFHaW9Q==", "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/puzrin" + }, + { + "type": "github", + "url": "https://github.com/sponsors/nodeca" + } + ], "license": "MIT", "dependencies": { "argparse": "^2.0.1" @@ -9674,9 +9821,9 @@ } }, "node_modules/knip": { - "version": "6.16.1", - "resolved": "https://registry.npmjs.org/knip/-/knip-6.16.1.tgz", - "integrity": "sha512-TKMn1rxgH6h9vXR9Y0B+Cq7AdPTr9EI02IwoT65NzqYUkvoDQAaJ/aPybiFpAhZ1px6cNYYwXf86iHkBgzCo9w==", + "version": "6.27.0", + "resolved": "https://registry.npmjs.org/knip/-/knip-6.27.0.tgz", + "integrity": "sha512-CngYEYrD0n20N06FXA8n3u/0Wnnugoa+B9k14OP+iKIgkCHuzvIdsP3nfwjhByoc1WfogpxfiriMboAXFETDUw==", "dev": true, "funding": [ { @@ -9694,13 +9841,13 @@ "formatly": "^0.3.0", "get-tsconfig": "4.14.0", "jiti": "^2.7.0", - "oxc-parser": "^0.133.0", - "oxc-resolver": "^11.20.0", + "oxc-parser": "^0.137.0", + "oxc-resolver": "11.21.3", "picomatch": "^4.0.4", "smol-toml": "^1.6.1", "strip-json-comments": "5.0.3", - "tinyglobby": "^0.2.16", - "unbash": "^3.0.0", + "tinyglobby": "^0.2.17", + "unbash": "^4.0.1", "yaml": "^2.9.0", "zod": "^4.1.11" }, @@ -9808,9 +9955,19 @@ "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==" }, "node_modules/linkify-it": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/linkify-it/-/linkify-it-5.0.0.tgz", - "integrity": "sha512-5aHCbzQRADcdP+ATqnDuhhJ/MRIqDkZX5pyjFHRRysS8vZ5AbqGEoFIb6pYHPZ+L/OC2Lc+xT8uHVVR5CAK/wQ==", + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/linkify-it/-/linkify-it-5.0.2.tgz", + "integrity": "sha512-ONTm2jCMAVZjgQa/Fy1kScXsuOoF5NPTsoFBdE1KVIZ2vAh/r9+Bqo+0jINCBYnavTPQZz38QzFTme79ENoN3Q==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/puzrin" + }, + { + "type": "github", + "url": "https://github.com/sponsors/markdown-it" + } + ], "license": "MIT", "peer": true, "dependencies": { @@ -9896,6 +10053,7 @@ "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", "dev": true, + "license": "ISC", "dependencies": { "yallist": "^3.0.2" } @@ -9928,15 +10086,25 @@ } }, "node_modules/markdown-it": { - "version": "14.1.1", - "resolved": "https://registry.npmjs.org/markdown-it/-/markdown-it-14.1.1.tgz", - "integrity": "sha512-BuU2qnTti9YKgK5N+IeMubp14ZUKUUw7yeJbkjtosvHiP0AZ5c8IAgEMk79D0eC8F23r4Ac/q8cAIFdm2FtyoA==", + "version": "14.3.0", + "resolved": "https://registry.npmjs.org/markdown-it/-/markdown-it-14.3.0.tgz", + "integrity": "sha512-RCEsPjR+sr0x+AuYp601tKTkgFG4YEPLCzHST3cQ/fhlJkqAkz1L2/Qbp1j9qw5SBwQHFBoW8+hoN5xssOF0Tw==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/puzrin" + }, + { + "type": "github", + "url": "https://github.com/sponsors/markdown-it" + } + ], "license": "MIT", "peer": true, "dependencies": { "argparse": "^2.0.1", - "entities": "^4.4.0", - "linkify-it": "^5.0.0", + "entities": "^4.5.0", + "linkify-it": "^5.0.2", "mdurl": "^2.0.0", "punycode.js": "^2.3.1", "uc.micro": "^2.1.0" @@ -10107,9 +10275,9 @@ } }, "node_modules/nanoid": { - "version": "3.3.11", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz", - "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==", + "version": "3.3.16", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.16.tgz", + "integrity": "sha512-bzlKTyNJ7+LdGIIwy8ijFpIqEQIvafahV7eYykJ8Cvh42EdJeODoJ6gUJXpQJvej1BddH8OqTXZNE/KfbWAu8Q==", "funding": [ { "type": "github", @@ -10150,11 +10318,14 @@ } }, "node_modules/node-releases": { - "version": "2.0.27", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.27.tgz", - "integrity": "sha512-nmh3lCkYZ3grZvqcCH+fjmQ7X+H0OeZgP40OierEaAptX4XofMh5kwNbWh7lBduUzCcV/8kZ+NDLCwm2iorIlA==", + "version": "2.0.51", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.51.tgz", + "integrity": "sha512-wRNIrw4DmVLKQlbgOMdkMx27Wrpzes2hh5Jtbi2bjPd+4wJstWIqP5A+lscnqbm0xxmT5Bpg8Lec5ItEBwx6BQ==", "dev": true, - "license": "MIT" + "license": "MIT", + "engines": { + "node": ">=18" + } }, "node_modules/normalize-path": { "version": "3.0.0", @@ -10164,15 +10335,6 @@ "node": ">=0.10.0" } }, - "node_modules/normalize-range": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/normalize-range/-/normalize-range-0.1.2.tgz", - "integrity": "sha512-bdok/XvKII3nUpklnV6P2hxtMNrCboOjAcyBuQnWEhO665FwrSNRxU+AqpsyvO6LgGYPspN+lu5CLtw4jPRKNA==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/object-assign": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", @@ -10384,13 +10546,13 @@ } }, "node_modules/oxc-parser": { - "version": "0.133.0", - "resolved": "https://registry.npmjs.org/oxc-parser/-/oxc-parser-0.133.0.tgz", - "integrity": "sha512-661RSx+ZcjBmjBYid+Fpp/2F5EbtildpeoZh5HdgnGs+jZ03nqQEQW8yGkt4BGyOC3OMPDQQRl8M5kqD2/g6jw==", + "version": "0.137.0", + "resolved": "https://registry.npmjs.org/oxc-parser/-/oxc-parser-0.137.0.tgz", + "integrity": "sha512-yFImD+WLElJpLKy8llG1qe4DCmMsL18peRp8XP1JKfig/gISbJkglnpDtX2aTmAn10kZF7164HbN2H8QPsXxGg==", "dev": true, "license": "MIT", "dependencies": { - "@oxc-project/types": "^0.133.0" + "@oxc-project/types": "^0.137.0" }, "engines": { "node": "^20.19.0 || >=22.12.0" @@ -10399,57 +10561,57 @@ "url": "https://github.com/sponsors/Boshen" }, "optionalDependencies": { - "@oxc-parser/binding-android-arm-eabi": "0.133.0", - "@oxc-parser/binding-android-arm64": "0.133.0", - "@oxc-parser/binding-darwin-arm64": "0.133.0", - "@oxc-parser/binding-darwin-x64": "0.133.0", - "@oxc-parser/binding-freebsd-x64": "0.133.0", - "@oxc-parser/binding-linux-arm-gnueabihf": "0.133.0", - "@oxc-parser/binding-linux-arm-musleabihf": "0.133.0", - "@oxc-parser/binding-linux-arm64-gnu": "0.133.0", - "@oxc-parser/binding-linux-arm64-musl": "0.133.0", - "@oxc-parser/binding-linux-ppc64-gnu": "0.133.0", - "@oxc-parser/binding-linux-riscv64-gnu": "0.133.0", - "@oxc-parser/binding-linux-riscv64-musl": "0.133.0", - "@oxc-parser/binding-linux-s390x-gnu": "0.133.0", - "@oxc-parser/binding-linux-x64-gnu": "0.133.0", - "@oxc-parser/binding-linux-x64-musl": "0.133.0", - "@oxc-parser/binding-openharmony-arm64": "0.133.0", - "@oxc-parser/binding-wasm32-wasi": "0.133.0", - "@oxc-parser/binding-win32-arm64-msvc": "0.133.0", - "@oxc-parser/binding-win32-ia32-msvc": "0.133.0", - "@oxc-parser/binding-win32-x64-msvc": "0.133.0" + "@oxc-parser/binding-android-arm-eabi": "0.137.0", + "@oxc-parser/binding-android-arm64": "0.137.0", + "@oxc-parser/binding-darwin-arm64": "0.137.0", + "@oxc-parser/binding-darwin-x64": "0.137.0", + "@oxc-parser/binding-freebsd-x64": "0.137.0", + "@oxc-parser/binding-linux-arm-gnueabihf": "0.137.0", + "@oxc-parser/binding-linux-arm-musleabihf": "0.137.0", + "@oxc-parser/binding-linux-arm64-gnu": "0.137.0", + "@oxc-parser/binding-linux-arm64-musl": "0.137.0", + "@oxc-parser/binding-linux-ppc64-gnu": "0.137.0", + "@oxc-parser/binding-linux-riscv64-gnu": "0.137.0", + "@oxc-parser/binding-linux-riscv64-musl": "0.137.0", + "@oxc-parser/binding-linux-s390x-gnu": "0.137.0", + "@oxc-parser/binding-linux-x64-gnu": "0.137.0", + "@oxc-parser/binding-linux-x64-musl": "0.137.0", + "@oxc-parser/binding-openharmony-arm64": "0.137.0", + "@oxc-parser/binding-wasm32-wasi": "0.137.0", + "@oxc-parser/binding-win32-arm64-msvc": "0.137.0", + "@oxc-parser/binding-win32-ia32-msvc": "0.137.0", + "@oxc-parser/binding-win32-x64-msvc": "0.137.0" } }, "node_modules/oxc-resolver": { - "version": "11.20.0", - "resolved": "https://registry.npmjs.org/oxc-resolver/-/oxc-resolver-11.20.0.tgz", - "integrity": "sha512-CblytBiV/a/ZXY34dsVU2NxhIOxMXst8CvDCtyBelVITgd7PLrKzbEbA6oKLdPjvDKDzCiW48qzmzZ+mYaqn+g==", + "version": "11.21.3", + "resolved": "https://registry.npmjs.org/oxc-resolver/-/oxc-resolver-11.21.3.tgz", + "integrity": "sha512-2Mx3fKQz7+xgrBONjsxOgCGtMHOn38/HxMzW1I5efwXB5a4lRN0Vp40gYUJFBWJslcrvwoofTrqoTnLbwTd3pA==", "dev": true, "license": "MIT", "funding": { "url": "https://github.com/sponsors/Boshen" }, "optionalDependencies": { - "@oxc-resolver/binding-android-arm-eabi": "11.20.0", - "@oxc-resolver/binding-android-arm64": "11.20.0", - "@oxc-resolver/binding-darwin-arm64": "11.20.0", - "@oxc-resolver/binding-darwin-x64": "11.20.0", - "@oxc-resolver/binding-freebsd-x64": "11.20.0", - "@oxc-resolver/binding-linux-arm-gnueabihf": "11.20.0", - "@oxc-resolver/binding-linux-arm-musleabihf": "11.20.0", - "@oxc-resolver/binding-linux-arm64-gnu": "11.20.0", - "@oxc-resolver/binding-linux-arm64-musl": "11.20.0", - "@oxc-resolver/binding-linux-ppc64-gnu": "11.20.0", - "@oxc-resolver/binding-linux-riscv64-gnu": "11.20.0", - "@oxc-resolver/binding-linux-riscv64-musl": "11.20.0", - "@oxc-resolver/binding-linux-s390x-gnu": "11.20.0", - "@oxc-resolver/binding-linux-x64-gnu": "11.20.0", - "@oxc-resolver/binding-linux-x64-musl": "11.20.0", - "@oxc-resolver/binding-openharmony-arm64": "11.20.0", - "@oxc-resolver/binding-wasm32-wasi": "11.20.0", - "@oxc-resolver/binding-win32-arm64-msvc": "11.20.0", - "@oxc-resolver/binding-win32-x64-msvc": "11.20.0" + "@oxc-resolver/binding-android-arm-eabi": "11.21.3", + "@oxc-resolver/binding-android-arm64": "11.21.3", + "@oxc-resolver/binding-darwin-arm64": "11.21.3", + "@oxc-resolver/binding-darwin-x64": "11.21.3", + "@oxc-resolver/binding-freebsd-x64": "11.21.3", + "@oxc-resolver/binding-linux-arm-gnueabihf": "11.21.3", + "@oxc-resolver/binding-linux-arm-musleabihf": "11.21.3", + "@oxc-resolver/binding-linux-arm64-gnu": "11.21.3", + "@oxc-resolver/binding-linux-arm64-musl": "11.21.3", + "@oxc-resolver/binding-linux-ppc64-gnu": "11.21.3", + "@oxc-resolver/binding-linux-riscv64-gnu": "11.21.3", + "@oxc-resolver/binding-linux-riscv64-musl": "11.21.3", + "@oxc-resolver/binding-linux-s390x-gnu": "11.21.3", + "@oxc-resolver/binding-linux-x64-gnu": "11.21.3", + "@oxc-resolver/binding-linux-x64-musl": "11.21.3", + "@oxc-resolver/binding-openharmony-arm64": "11.21.3", + "@oxc-resolver/binding-wasm32-wasi": "11.21.3", + "@oxc-resolver/binding-win32-arm64-msvc": "11.21.3", + "@oxc-resolver/binding-win32-x64-msvc": "11.21.3" } }, "node_modules/p-limit": { @@ -10687,9 +10849,9 @@ } }, "node_modules/postcss": { - "version": "8.5.12", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.12.tgz", - "integrity": "sha512-W62t/Se6rA0Az3DfCL0AqJwXuKwBeYg6nOaIgzP+xZ7N5BFCI7DYi1qs6ygUYT6rvfi6t9k65UMLJC+PHZpDAA==", + "version": "8.5.23", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.23.tgz", + "integrity": "sha512-g50586zr4bZmwFiTlflMu8E0bDTb5I5gertgwAKmsdUlTQIhZtunzUlD1WSzwcVWPoAVpsrA6vlfCD7oXvRwgg==", "funding": [ { "type": "opencollective", @@ -10706,7 +10868,7 @@ ], "license": "MIT", "dependencies": { - "nanoid": "^3.3.11", + "nanoid": "^3.3.16", "picocolors": "^1.1.1", "source-map-js": "^1.2.1" }, @@ -13544,16 +13706,16 @@ } }, "node_modules/temp/node_modules/brace-expansion": { - "version": "5.0.5", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.5.tgz", - "integrity": "sha512-VZznLgtwhn+Mact9tfiwx64fA9erHH/MCXEUfB/0bX/6Fz6ny5EGTXYltMocqg4xFAQZtnO3DHWWXi8RiuN7cQ==", + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.8.tgz", + "integrity": "sha512-JZyDyq3D4AUifKTPOB7DELf6XsB3WdPuNxCtob1vFXPsSXhdAiHBWJ/tJ8HAc9aH84BK+5JFZLNkJKx3G9kzQg==", "dev": true, "license": "MIT", "dependencies": { "balanced-match": "^4.0.2" }, "engines": { - "node": "18 || 20 || >=22" + "node": "20 || >=22" } }, "node_modules/temp/node_modules/glob": { @@ -13997,10 +14159,11 @@ } }, "node_modules/typescript": { - "version": "5.3.2", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.3.2.tgz", - "integrity": "sha512-6l+RyNy7oAHDfxC4FzSJcz9vnjTKxrLpDG5M2Vu4SHRVNg6xzqZp6LYSR9zjqQTu8DU/f5xwxUdADOkbrIX2gQ==", + "version": "5.9.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", + "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", "dev": true, + "license": "Apache-2.0", "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" @@ -14017,9 +14180,9 @@ "peer": true }, "node_modules/unbash": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/unbash/-/unbash-3.0.0.tgz", - "integrity": "sha512-FeFPZ/WFT0mbRCuydiZzpPFlrYN8ZUpphQKoq4EeElVIYjYyGzPMxQR/simUwCOJIyVhpFk4RbtyO7RuMpMnHA==", + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/unbash/-/unbash-4.0.4.tgz", + "integrity": "sha512-60m9IVGbavD6jholbxt0jVBXZkEB/HsMZq7Tyaghseve2/Sf0zQRAIfWsD34sde+DKP2tBxJS2wP88ZM0D1FhA==", "dev": true, "license": "ISC", "engines": { @@ -14046,9 +14209,9 @@ } }, "node_modules/undici": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/undici/-/undici-7.27.1.tgz", - "integrity": "sha512-UDdpiex+mzigiyrXrGbiUaF4HzTNhKbh2vRNFaTMzcqmLIPrZxaCtwo/1TMSuWoM1Xz3WiTo9KdgI3kRqYzJGg==", + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/undici/-/undici-7.29.0.tgz", + "integrity": "sha512-IDxfleLmmbSskfWSUATiN1nfn2rDuvnMOqb5CWR92iIfojA0Ud+ulOAAEQ57LPr9rWmsreUyf5lwyao+7GNNVw==", "dev": true, "license": "MIT", "engines": { @@ -14312,13 +14475,13 @@ } }, "node_modules/vite": { - "version": "7.3.5", - "resolved": "https://registry.npmjs.org/vite/-/vite-7.3.5.tgz", - "integrity": "sha512-KuOaNhcnGFN2zIPGA7wRmzF+lJA1sea7rHq17aiJ++9lzY1WWG6Jpwqwe1KNbRVPIqHmr8GLYx7jbrQcN/7/ww==", + "version": "7.3.6", + "resolved": "https://registry.npmjs.org/vite/-/vite-7.3.6.tgz", + "integrity": "sha512-4XP60spRGjSZFf1qYH+dJIkK2znL3zQfl9KkOV9MkkRR/3Dls0dxaBsQPTloEc5BLXWPL9vsOxopxyKoMmDueg==", "dev": true, "license": "MIT", "dependencies": { - "esbuild": "^0.27.0", + "esbuild": "^0.27.0 || ^0.28.0", "fdir": "^6.5.0", "picomatch": "^4.0.3", "postcss": "^8.5.6", @@ -14738,19 +14901,19 @@ } }, "node_modules/vitest": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/vitest/-/vitest-4.1.0.tgz", - "integrity": "sha512-YbDrMF9jM2Lqc++2530UourxZHmkKLxrs4+mYhEwqWS97WJ7wOYEkcr+QfRgJ3PW9wz3odRijLZjHEaRLTNbqw==", + "version": "4.1.10", + "resolved": "https://registry.npmjs.org/vitest/-/vitest-4.1.10.tgz", + "integrity": "sha512-R9jUTe5S4Qb0HCd4TNqpC7oGcrMssMRGXLW80ubjWsW9VH5GF8y1Y0SFLY9AbqSk6nt0PnOx4H4WNJYZ13GUPw==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/expect": "4.1.0", - "@vitest/mocker": "4.1.0", - "@vitest/pretty-format": "4.1.0", - "@vitest/runner": "4.1.0", - "@vitest/snapshot": "4.1.0", - "@vitest/spy": "4.1.0", - "@vitest/utils": "4.1.0", + "@vitest/expect": "4.1.10", + "@vitest/mocker": "4.1.10", + "@vitest/pretty-format": "4.1.10", + "@vitest/runner": "4.1.10", + "@vitest/snapshot": "4.1.10", + "@vitest/spy": "4.1.10", + "@vitest/utils": "4.1.10", "es-module-lexer": "^2.0.0", "expect-type": "^1.3.0", "magic-string": "^0.30.21", @@ -14761,8 +14924,8 @@ "tinybench": "^2.9.0", "tinyexec": "^1.0.2", "tinyglobby": "^0.2.15", - "tinyrainbow": "^3.0.3", - "vite": "^6.0.0 || ^7.0.0 || ^8.0.0-0", + "tinyrainbow": "^3.1.0", + "vite": "^6.0.0 || ^7.0.0 || ^8.0.0", "why-is-node-running": "^2.3.0" }, "bin": { @@ -14778,13 +14941,15 @@ "@edge-runtime/vm": "*", "@opentelemetry/api": "^1.9.0", "@types/node": "^20.0.0 || ^22.0.0 || >=24.0.0", - "@vitest/browser-playwright": "4.1.0", - "@vitest/browser-preview": "4.1.0", - "@vitest/browser-webdriverio": "4.1.0", - "@vitest/ui": "4.1.0", + "@vitest/browser-playwright": "4.1.10", + "@vitest/browser-preview": "4.1.10", + "@vitest/browser-webdriverio": "4.1.10", + "@vitest/coverage-istanbul": "4.1.10", + "@vitest/coverage-v8": "4.1.10", + "@vitest/ui": "4.1.10", "happy-dom": "*", "jsdom": "*", - "vite": "^6.0.0 || ^7.0.0 || ^8.0.0-0" + "vite": "^6.0.0 || ^7.0.0 || ^8.0.0" }, "peerDependenciesMeta": { "@edge-runtime/vm": { @@ -14805,6 +14970,12 @@ "@vitest/browser-webdriverio": { "optional": true }, + "@vitest/coverage-istanbul": { + "optional": true + }, + "@vitest/coverage-v8": { + "optional": true + }, "@vitest/ui": { "optional": true }, @@ -15098,9 +15269,9 @@ "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==" }, "node_modules/ws": { - "version": "8.20.0", - "resolved": "https://registry.npmjs.org/ws/-/ws-8.20.0.tgz", - "integrity": "sha512-sAt8BhgNbzCtgGbt2OxmpuryO63ZoDk/sqaB/znQm94T4fCEsy/yV+7CdC1kJhOU9lboAEU7R3kquuycDoibVA==", + "version": "8.21.1", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.21.1.tgz", + "integrity": "sha512-+0NTnW77fFN/DjQi6k/Sq/Yvk4Sgajw7urW8V+asjXnRgDs9gyGkdb7EzgfhA4goXsRIZKE28fzIXBHEzhuiWw==", "dev": true, "license": "MIT", "engines": { @@ -15149,7 +15320,8 @@ "version": "3.1.1", "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", - "dev": true + "dev": true, + "license": "ISC" }, "node_modules/yaml": { "version": "2.9.0", diff --git a/client/package.json b/client/package.json index 504c3df42..39b5e44bc 100644 --- a/client/package.json +++ b/client/package.json @@ -7,9 +7,9 @@ "start": "vite --port 3000", "build": "vite build", "test": "vitest --passWithNoTests", - "test:prepush": "vitest --watchAll=false --passWithNoTests", + "test:prepush": "vitest run --passWithNoTests", "check:prepush": "npm run build && npm run test:prepush", - "lint": "tsc --noEmit && eslint ./src", + "lint": "tsc --noEmit && tsc --noEmit -p tsconfig.test.json && eslint ./src", "storybook": "storybook dev -p 6006", "build-storybook": "storybook build", "knip": "knip" @@ -74,44 +74,44 @@ "web-vitals": "^1.0.1" }, "devDependencies": { - "@eslint/compat": "^2.0.3", - "@eslint/eslintrc": "^3.3.5", + "@eslint/compat": "^2.1.0", + "@eslint/eslintrc": "^3.3.6", "@faker-js/faker": "^8.4.1", "@storybook/react": "^9.1.20", "@storybook/react-vite": "^9.1.20", - "@testing-library/jest-dom": "^5.11.9", + "@testing-library/jest-dom": "^5.17.0", "@testing-library/react": "^11.2.3", "@testing-library/user-event": "^12.6.0", - "@types/google.maps": "^3.48.7", - "@types/latlon-geohash": "^2.0.0", - "@types/lodash": "^4.14.181", + "@types/google.maps": "^3.65.2", + "@types/latlon-geohash": "^2.0.4", + "@types/lodash": "^4.17.24", "@types/react": "^18.0.0", - "@types/react-beautiful-dnd": "^13.1.4", - "@types/react-csv": "^1.1.3", + "@types/react-beautiful-dnd": "^13.1.8", + "@types/react-csv": "^1.1.10", "@types/react-dom": "^18.0.0", "@types/react-syntax-highlighter": "^13.5.2", - "@types/react-table": "^7.7.5", - "@typescript-eslint/eslint-plugin": "^8.57.2", + "@types/react-table": "^7.7.20", + "@typescript-eslint/eslint-plugin": "^8.64.0", "@typescript-eslint/parser": "^8.57.2", - "@vitejs/plugin-react": "^5.1.4", - "autoprefixer": "^10.4.7", - "eslint": "^9.39.4", + "@vitejs/plugin-react": "^5.2.0", + "autoprefixer": "^10.5.4", + "eslint": "^9.39.5", "eslint-plugin-custom-rules": "file:./eslint", "eslint-plugin-react": "^7.30.1", - "eslint-plugin-react-hooks": "^7.0.1", + "eslint-plugin-react-hooks": "^7.1.1", "jsdom": "^29.1.1", - "knip": "^6.16.1", - "postcss": "^8.5.10", + "knip": "^6.27.0", + "postcss": "^8.5.23", "prop-types": "^15.8.1", "source-map-explorer": "^2.5.3", "storybook": "^9.1.20", "tailwindcss": "^3.4.19", - "typescript": "^5.3.2", - "vite": "^7.3.5", + "typescript": "^5.9.3", + "vite": "^7.3.6", "vite-plugin-commonjs": "^0.10.4", "vite-plugin-svgr": "^4.5.0", "vite-tsconfig-paths": "^6.1.1", - "vitest": "^4.1.0" + "vitest": "^4.1.10" }, "overrides": { "tailwindcss": { diff --git a/client/public/e2e/test-video-2s.mp4 b/client/public/e2e/test-video-2s.mp4 new file mode 100644 index 000000000..701f77b7c Binary files /dev/null and b/client/public/e2e/test-video-2s.mp4 differ diff --git a/client/public/e2e/tone-3s.wav b/client/public/e2e/tone-3s.wav new file mode 100644 index 000000000..755a5c30b Binary files /dev/null and b/client/public/e2e/tone-3s.wav differ diff --git a/client/src/components/ItemAction.tsx b/client/src/components/ItemAction.tsx index 5d1da1d60..1d3a2044c 100644 --- a/client/src/components/ItemAction.tsx +++ b/client/src/components/ItemAction.tsx @@ -30,8 +30,7 @@ type EligibleAction = { }; type ParamsModalState = - | { open: false } - | { open: true; mode: 'create' | 'edit'; actionId: string }; + { open: false } | { open: true; mode: 'create' | 'edit'; actionId: string }; export default function ItemAction(props: { itemIdentifier: ItemIdentifier; @@ -187,8 +186,7 @@ export default function ItemAction(props: { // issue. See https://github.com/microsoft/TypeScript/issues/17002 for // more details. const policyId = policyIds satisfies - | string - | readonly string[] as string; + string | readonly string[] as string; setSelectedPolicyIds([policyId]); } }, diff --git a/client/src/components/Sidebar.test.tsx b/client/src/components/Sidebar.test.tsx index 699d2467c..c7b578430 100644 --- a/client/src/components/Sidebar.test.tsx +++ b/client/src/components/Sidebar.test.tsx @@ -82,10 +82,8 @@ function renderSidebar( return { ...result, setSelectedMenuItem, logout }; } -function getSettingsLink() { - return screen - .getAllByRole('link') - .find((el) => el.getAttribute('href') === '/dashboard/settings'); +function getSettingsButton() { + return screen.queryByRole('button', { name: 'Settings' }); } describe('Sidebar', () => { @@ -93,7 +91,7 @@ describe('Sidebar', () => { renderSidebar(); expect(screen.getByText('Overview')).toBeInTheDocument(); expect(screen.getByText('Review Console')).toBeInTheDocument(); - expect(getSettingsLink()).toBeDefined(); + expect(getSettingsButton()).toBeInTheDocument(); }); it('shows settings sub-items when on a settings page', () => { @@ -112,20 +110,21 @@ describe('Sidebar', () => { it('highlights gear icon on settings pages but not elsewhere', () => { const { unmount } = renderSidebar('/dashboard/settings', 'Settings'); - expect(getSettingsLink()!.className).toContain('text-primary'); + expect(getSettingsButton()).toHaveClass('text-primary'); unmount(); renderSidebar('/dashboard/overview', 'Overview'); - expect(getSettingsLink()!.className).not.toContain('text-primary'); + expect(getSettingsButton()).not.toHaveClass('text-primary'); }); - it('sets selectedMenuItem to Settings when gear icon is clicked', () => { - const { setSelectedMenuItem } = renderSidebar( - '/dashboard/overview', - 'Overview', - ); - fireEvent.click(getSettingsLink()!); - expect(setSelectedMenuItem).toHaveBeenCalledWith('Settings'); + it('expands settings sub-items when gear icon is clicked', () => { + renderSidebar('/dashboard/overview', 'Overview'); + const settingsButton = getSettingsButton(); + expect(settingsButton).toHaveAttribute('aria-expanded', 'false'); + + fireEvent.click(settingsButton!); + + expect(settingsButton).toHaveAttribute('aria-expanded', 'true'); }); describe('path-based menu selection', () => { @@ -142,7 +141,7 @@ describe('Sidebar', () => { it('hides gear icon when user lacks ManageOrg permission', () => { renderSidebar('/dashboard/overview', 'Overview', { permissions: [] }); - expect(getSettingsLink()).toBeUndefined(); + expect(getSettingsButton()).not.toBeInTheDocument(); }); it('hides sub-items when user lacks permissions', () => { diff --git a/client/src/components/Sidebar.tsx b/client/src/components/Sidebar.tsx index aab22473e..6f5d4cc2d 100644 --- a/client/src/components/Sidebar.tsx +++ b/client/src/components/Sidebar.tsx @@ -353,6 +353,8 @@ export default function Sidebar(props: SidebarProps) {
setIsSettingsMenuExpanded((prev) => !prev)} onKeyDown={(e) => { diff --git a/client/src/coop-ui/Calendar.test.tsx b/client/src/coop-ui/Calendar.test.tsx index 32772c595..b4e4bc1b4 100644 --- a/client/src/coop-ui/Calendar.test.tsx +++ b/client/src/coop-ui/Calendar.test.tsx @@ -15,6 +15,7 @@ describe('Calendar Component', () => { renderCalendar({ mode: 'single', selected: selectedDate, + defaultMonth: selectedDate, }); const selectedDay = screen.getByText('15'); @@ -26,6 +27,7 @@ describe('Calendar Component', () => { renderCalendar({ mode: 'multiple', selected: selectedDates, + defaultMonth: selectedDates[0], }); selectedDates.forEach((date) => { diff --git a/client/src/coop-ui/Typography.tsx b/client/src/coop-ui/Typography.tsx index 6aaf08e40..044f51275 100644 --- a/client/src/coop-ui/Typography.tsx +++ b/client/src/coop-ui/Typography.tsx @@ -3,16 +3,7 @@ import { cva, VariantProps } from 'class-variance-authority'; import React from 'react'; type TextSize = - | 'XXS' - | 'XS' - | 'SM' - | 'base' - | 'LG' - | 'XL' - | '2XL' - | '3XL' - | '4XL' - | '5XL'; + 'XXS' | 'XS' | 'SM' | 'base' | 'LG' | 'XL' | '2XL' | '3XL' | '4XL' | '5XL'; type TextWeight = 'regular' | 'medium' | 'semibold' | 'bold'; diff --git a/client/src/graphql/generated.ts b/client/src/graphql/generated.ts index 9211df454..3153553a1 100644 --- a/client/src/graphql/generated.ts +++ b/client/src/graphql/generated.ts @@ -231,8 +231,7 @@ export type GQLAddFavoriteRuleSuccessResponse = { }; export type GQLAddManualReviewJobCommentResponse = - | GQLAddManualReviewJobCommentSuccessResponse - | GQLNotFoundError; + GQLAddManualReviewJobCommentSuccessResponse | GQLNotFoundError; export type GQLAddManualReviewJobCommentSuccessResponse = { readonly __typename: 'AddManualReviewJobCommentSuccessResponse'; @@ -416,8 +415,7 @@ export type GQLChangePasswordInput = { }; export type GQLChangePasswordResponse = - | GQLChangePasswordError - | GQLChangePasswordSuccessResponse; + GQLChangePasswordError | GQLChangePasswordSuccessResponse; export type GQLChangePasswordSuccessResponse = { readonly __typename: 'ChangePasswordSuccessResponse'; @@ -526,8 +524,7 @@ export type GQLConditionSetWithResult = { }; export type GQLConditionWithResult = - | GQLConditionSetWithResult - | GQLLeafConditionWithResult; + GQLConditionSetWithResult | GQLLeafConditionWithResult; export type GQLContainer = { readonly __typename: 'Container'; @@ -763,8 +760,7 @@ export type GQLCreateContentRuleInput = { }; export type GQLCreateContentRuleResponse = - | GQLMutateContentRuleSuccessResponse - | GQLRuleNameExistsError; + GQLMutateContentRuleSuccessResponse | GQLRuleNameExistsError; export type GQLCreateHashBankInput = { readonly description?: InputMaybe; @@ -814,8 +810,7 @@ export type GQLCreateReportingRuleInput = { }; export type GQLCreateReportingRuleResponse = - | GQLMutateReportingRuleSuccessResponse - | GQLReportingRuleNameExistsError; + GQLMutateReportingRuleSuccessResponse | GQLReportingRuleNameExistsError; export type GQLCreateRoutingRuleInput = { readonly conditionSet: GQLConditionSetInput; @@ -873,8 +868,7 @@ export type GQLCreateUserRuleInput = { }; export type GQLCreateUserRuleResponse = - | GQLMutateUserRuleSuccessResponse - | GQLRuleNameExistsError; + GQLMutateUserRuleSuccessResponse | GQLRuleNameExistsError; export type GQLCustomAction = GQLActionBase & { readonly __typename: 'CustomAction'; @@ -992,8 +986,7 @@ export const GQLDecisionsCountGroupBy = { export type GQLDecisionsCountGroupBy = (typeof GQLDecisionsCountGroupBy)[keyof typeof GQLDecisionsCountGroupBy]; export type GQLDeleteAllJobsFromQueueResponse = - | GQLDeleteAllJobsFromQueueSuccessResponse - | GQLDeleteAllJobsUnauthorizedError; + GQLDeleteAllJobsFromQueueSuccessResponse | GQLDeleteAllJobsUnauthorizedError; export type GQLDeleteAllJobsFromQueueSuccessResponse = { readonly __typename: 'DeleteAllJobsFromQueueSuccessResponse'; @@ -1011,8 +1004,7 @@ export type GQLDeleteAllJobsUnauthorizedError = GQLError & { }; export type GQLDeleteItemTypeResponse = - | GQLCannotDeleteDefaultUserError - | GQLDeleteItemTypeSuccessResponse; + GQLCannotDeleteDefaultUserError | GQLDeleteItemTypeSuccessResponse; export type GQLDeleteItemTypeSuccessResponse = { readonly __typename: 'DeleteItemTypeSuccessResponse'; @@ -1326,8 +1318,7 @@ export type GQLGetDecisionCountsTableInput = { }; export type GQLGetFullReportingRuleResultForItemResponse = - | GQLNotFoundError - | GQLReportingRuleExecutionResult; + GQLNotFoundError | GQLReportingRuleExecutionResult; export type GQLGetFullResultForItemInput = { readonly date?: InputMaybe; @@ -1337,8 +1328,7 @@ export type GQLGetFullResultForItemInput = { }; export type GQLGetFullResultForItemResponse = - | GQLNotFoundError - | GQLRuleExecutionResult; + GQLNotFoundError | GQLRuleExecutionResult; export type GQLGetJobCreationCountInput = { readonly filterBy: GQLJobCreationFilterByInput; @@ -1630,9 +1620,7 @@ export type GQLItemSubmissions = { }; export type GQLItemType = - | GQLContentItemType - | GQLThreadItemType - | GQLUserItemType; + GQLContentItemType | GQLThreadItemType | GQLUserItemType; export type GQLItemTypeBase = { readonly baseFields: ReadonlyArray; @@ -2145,8 +2133,7 @@ export const GQLManualReviewChartMetric = { export type GQLManualReviewChartMetric = (typeof GQLManualReviewChartMetric)[keyof typeof GQLManualReviewChartMetric]; export type GQLManualReviewChartSettings = - | GQLGetDecisionCountSettings - | GQLGetJobCreationCountSettings; + GQLGetDecisionCountSettings | GQLGetJobCreationCountSettings; export type GQLManualReviewChartSettingsInput = { readonly decisionCountSettings?: InputMaybe; @@ -2374,6 +2361,7 @@ export type GQLModeratorSafetySettingsInput = { readonly moderatorSafetyBlurLevel: Scalars['Int']['input']; readonly moderatorSafetyGrayscale: Scalars['Boolean']['input']; readonly moderatorSafetyMuteVideo: Scalars['Boolean']['input']; + readonly moderatorSafetySepia: Scalars['Boolean']['input']; }; export const GQLMrtClearReportsDisposition = { @@ -2408,8 +2396,7 @@ export const GQLMutateActionError = { export type GQLMutateActionError = (typeof GQLMutateActionError)[keyof typeof GQLMutateActionError]; export type GQLMutateActionResponse = - | GQLActionNameExistsError - | GQLMutateActionSuccessResponse; + GQLActionNameExistsError | GQLMutateActionSuccessResponse; export type GQLMutateActionSuccessResponse = { readonly __typename: 'MutateActionSuccessResponse'; @@ -2423,8 +2410,7 @@ export type GQLMutateBankResponse = { }; export type GQLMutateContentItemTypeResponse = - | GQLItemTypeNameAlreadyExistsError - | GQLMutateContentTypeSuccessResponse; + GQLItemTypeNameAlreadyExistsError | GQLMutateContentTypeSuccessResponse; export type GQLMutateContentRuleSuccessResponse = { readonly __typename: 'MutateContentRuleSuccessResponse'; @@ -2437,8 +2423,7 @@ export type GQLMutateContentTypeSuccessResponse = { }; export type GQLMutateHashBankResponse = - | GQLMatchingBankNameExistsError - | GQLMutateHashBankSuccessResponse; + GQLMatchingBankNameExistsError | GQLMutateHashBankSuccessResponse; export type GQLMutateHashBankSuccessResponse = { readonly __typename: 'MutateHashBankSuccessResponse'; @@ -2447,8 +2432,7 @@ export type GQLMutateHashBankSuccessResponse = { }; export type GQLMutateLocationBankResponse = - | GQLLocationBankNameExistsError - | GQLMutateLocationBankSuccessResponse; + GQLLocationBankNameExistsError | GQLMutateLocationBankSuccessResponse; export type GQLMutateLocationBankSuccessResponse = { readonly __typename: 'MutateLocationBankSuccessResponse'; @@ -2476,8 +2460,7 @@ export type GQLMutateRoutingRulesOrderSuccessResponse = { }; export type GQLMutateThreadItemTypeResponse = - | GQLItemTypeNameAlreadyExistsError - | GQLMutateThreadTypeSuccessResponse; + GQLItemTypeNameAlreadyExistsError | GQLMutateThreadTypeSuccessResponse; export type GQLMutateThreadTypeSuccessResponse = { readonly __typename: 'MutateThreadTypeSuccessResponse'; @@ -2485,8 +2468,7 @@ export type GQLMutateThreadTypeSuccessResponse = { }; export type GQLMutateUserItemTypeResponse = - | GQLItemTypeNameAlreadyExistsError - | GQLMutateUserTypeSuccessResponse; + GQLItemTypeNameAlreadyExistsError | GQLMutateUserTypeSuccessResponse; export type GQLMutateUserRuleSuccessResponse = { readonly __typename: 'MutateUserRuleSuccessResponse'; @@ -3893,8 +3875,7 @@ export type GQLRemoveAccessibleQueuesToUserInput = { }; export type GQLRemoveAccessibleQueuesToUserResponse = - | GQLMutateAccessibleQueuesForUserSuccessResponse - | GQLNotFoundError; + GQLMutateAccessibleQueuesForUserSuccessResponse | GQLNotFoundError; export type GQLRemoveFavoriteMrtQueueSuccessResponse = { readonly __typename: 'RemoveFavoriteMRTQueueSuccessResponse'; @@ -4087,8 +4068,7 @@ export type GQLRotateApiKeyInput = { }; export type GQLRotateApiKeyResponse = - | GQLRotateApiKeyError - | GQLRotateApiKeySuccessResponse; + GQLRotateApiKeyError | GQLRotateApiKeySuccessResponse; export type GQLRotateApiKeySuccessResponse = { readonly __typename: 'RotateApiKeySuccessResponse'; @@ -4107,8 +4087,7 @@ export type GQLRotateWebhookSigningKeyError = GQLError & { }; export type GQLRotateWebhookSigningKeyResponse = - | GQLRotateWebhookSigningKeyError - | GQLRotateWebhookSigningKeySuccessResponse; + GQLRotateWebhookSigningKeyError | GQLRotateWebhookSigningKeySuccessResponse; export type GQLRotateWebhookSigningKeySuccessResponse = { readonly __typename: 'RotateWebhookSigningKeySuccessResponse'; @@ -4391,8 +4370,7 @@ export type GQLSignUpInput = { }; export type GQLSignUpResponse = - | GQLSignUpSuccessResponse - | GQLSignUpUserExistsError; + GQLSignUpSuccessResponse | GQLSignUpUserExistsError; export type GQLSignUpSuccessResponse = { readonly __typename: 'SignUpSuccessResponse'; @@ -4470,8 +4448,7 @@ export const GQLSignalInputType = { export type GQLSignalInputType = (typeof GQLSignalInputType)[keyof typeof GQLSignalInputType]; export type GQLSignalOutputType = - | GQLEnumSignalOutputType - | GQLScalarSignalOutputType; + GQLEnumSignalOutputType | GQLScalarSignalOutputType; export type GQLSignalPricingStructure = { readonly __typename: 'SignalPricingStructure'; @@ -4519,6 +4496,12 @@ export const GQLSignalType = { OpenAiHateTextModel: 'OPEN_AI_HATE_TEXT_MODEL', OpenAiHateThreateningTextModel: 'OPEN_AI_HATE_THREATENING_TEXT_MODEL', OpenAiSelfHarmImageModel: 'OPEN_AI_SELF_HARM_IMAGE_MODEL', + OpenAiSelfHarmInstructionsImageModel: + 'OPEN_AI_SELF_HARM_INSTRUCTIONS_IMAGE_MODEL', + OpenAiSelfHarmInstructionsTextModel: + 'OPEN_AI_SELF_HARM_INSTRUCTIONS_TEXT_MODEL', + OpenAiSelfHarmIntentImageModel: 'OPEN_AI_SELF_HARM_INTENT_IMAGE_MODEL', + OpenAiSelfHarmIntentTextModel: 'OPEN_AI_SELF_HARM_INTENT_TEXT_MODEL', OpenAiSelfHarmTextModel: 'OPEN_AI_SELF_HARM_TEXT_MODEL', OpenAiSexualImageModel: 'OPEN_AI_SEXUAL_IMAGE_MODEL', OpenAiSexualMinorsTextModel: 'OPEN_AI_SEXUAL_MINORS_TEXT_MODEL', @@ -5093,6 +5076,7 @@ export type GQLUserInterfacePreferences = { readonly moderatorSafetyBlurLevel: Scalars['Int']['output']; readonly moderatorSafetyGrayscale: Scalars['Boolean']['output']; readonly moderatorSafetyMuteVideo: Scalars['Boolean']['output']; + readonly moderatorSafetySepia: Scalars['Boolean']['output']; readonly mrtChartConfigurations: ReadonlyArray; }; @@ -7514,8 +7498,7 @@ export type GQLGetItemsByIpAddressQuery = { export type GQLGetAuthorInfoQueryVariables = Exact<{ userIdentifiers: - | ReadonlyArray - | GQLItemIdentifierInput; + ReadonlyArray | GQLItemIdentifierInput; }>; export type GQLGetAuthorInfoQuery = { @@ -12313,6 +12296,7 @@ export type GQLManualReviewSafetySettingsQuery = { readonly moderatorSafetyMuteVideo: boolean; readonly moderatorSafetyGrayscale: boolean; readonly moderatorSafetyBlurLevel: number; + readonly moderatorSafetySepia: boolean; }; } | null; }; @@ -15409,15 +15393,6 @@ export type GQLLogSkipMutation = { readonly logSkip: boolean; }; -export type GQLReleaseJobLockMutationVariables = Exact<{ - input: GQLReleaseJobLockInput; -}>; - -export type GQLReleaseJobLockMutation = { - readonly __typename: 'Mutation'; - readonly releaseJobLock: boolean; -}; - export type GQLJobFieldsFragment = { readonly __typename: 'ManualReviewJob'; readonly id: string; @@ -16746,105 +16721,6 @@ export type GQLJobFieldsFragment = { }; }; -export type GQLGetRelatedItemsQueryVariables = Exact<{ - itemIdentifiers: - | ReadonlyArray - | GQLItemIdentifierInput; -}>; - -export type GQLGetRelatedItemsQuery = { - readonly __typename: 'Query'; - readonly latestItemSubmissions: ReadonlyArray< - | { - readonly __typename: 'ContentItem'; - readonly id: string; - readonly submissionId: string; - readonly submissionTime?: Date | string | null; - readonly data: JsonObject; - readonly type: { - readonly __typename: 'ContentItemType'; - readonly id: string; - readonly name: string; - readonly baseFields: ReadonlyArray<{ - readonly __typename: 'BaseField'; - readonly name: string; - readonly type: GQLFieldType; - readonly required: boolean; - readonly container?: { - readonly __typename: 'Container'; - readonly containerType: GQLContainerType; - readonly keyScalarType?: GQLScalarType | null; - readonly valueScalarType: GQLScalarType; - } | null; - }>; - readonly schemaFieldRoles: { - readonly __typename: 'ContentSchemaFieldRoles'; - readonly displayName?: string | null; - }; - }; - } - | { - readonly __typename: 'ThreadItem'; - readonly id: string; - readonly submissionId: string; - readonly submissionTime?: Date | string | null; - readonly data: JsonObject; - readonly type: { - readonly __typename: 'ThreadItemType'; - readonly id: string; - readonly name: string; - readonly baseFields: ReadonlyArray<{ - readonly __typename: 'BaseField'; - readonly name: string; - readonly type: GQLFieldType; - readonly required: boolean; - readonly container?: { - readonly __typename: 'Container'; - readonly containerType: GQLContainerType; - readonly keyScalarType?: GQLScalarType | null; - readonly valueScalarType: GQLScalarType; - } | null; - }>; - readonly schemaFieldRoles: { - readonly __typename: 'ThreadSchemaFieldRoles'; - readonly displayName?: string | null; - }; - }; - } - | { - readonly __typename: 'UserItem'; - readonly id: string; - readonly submissionId: string; - readonly submissionTime?: Date | string | null; - readonly data: JsonObject; - readonly type: { - readonly __typename: 'UserItemType'; - readonly id: string; - readonly name: string; - readonly baseFields: ReadonlyArray<{ - readonly __typename: 'BaseField'; - readonly name: string; - readonly type: GQLFieldType; - readonly required: boolean; - readonly container?: { - readonly __typename: 'Container'; - readonly containerType: GQLContainerType; - readonly keyScalarType?: GQLScalarType | null; - readonly valueScalarType: GQLScalarType; - } | null; - }>; - readonly schemaFieldRoles: { - readonly __typename: 'UserSchemaFieldRoles'; - readonly displayName?: string | null; - readonly createdAt?: string | null; - readonly profileIcon?: string | null; - readonly backgroundImage?: string | null; - }; - }; - } - >; -}; - export type GQLManualReviewJobCommentFieldsFragment = { readonly __typename: 'ManualReviewJobComment'; readonly id: string; @@ -16958,6 +16834,104 @@ export type GQLGetThreadHistoryQuery = { }>; }; +export type GQLGetRelatedItemsQueryVariables = Exact<{ + itemIdentifiers: + ReadonlyArray | GQLItemIdentifierInput; +}>; + +export type GQLGetRelatedItemsQuery = { + readonly __typename: 'Query'; + readonly latestItemSubmissions: ReadonlyArray< + | { + readonly __typename: 'ContentItem'; + readonly id: string; + readonly submissionId: string; + readonly submissionTime?: Date | string | null; + readonly data: JsonObject; + readonly type: { + readonly __typename: 'ContentItemType'; + readonly id: string; + readonly name: string; + readonly baseFields: ReadonlyArray<{ + readonly __typename: 'BaseField'; + readonly name: string; + readonly type: GQLFieldType; + readonly required: boolean; + readonly container?: { + readonly __typename: 'Container'; + readonly containerType: GQLContainerType; + readonly keyScalarType?: GQLScalarType | null; + readonly valueScalarType: GQLScalarType; + } | null; + }>; + readonly schemaFieldRoles: { + readonly __typename: 'ContentSchemaFieldRoles'; + readonly displayName?: string | null; + }; + }; + } + | { + readonly __typename: 'ThreadItem'; + readonly id: string; + readonly submissionId: string; + readonly submissionTime?: Date | string | null; + readonly data: JsonObject; + readonly type: { + readonly __typename: 'ThreadItemType'; + readonly id: string; + readonly name: string; + readonly baseFields: ReadonlyArray<{ + readonly __typename: 'BaseField'; + readonly name: string; + readonly type: GQLFieldType; + readonly required: boolean; + readonly container?: { + readonly __typename: 'Container'; + readonly containerType: GQLContainerType; + readonly keyScalarType?: GQLScalarType | null; + readonly valueScalarType: GQLScalarType; + } | null; + }>; + readonly schemaFieldRoles: { + readonly __typename: 'ThreadSchemaFieldRoles'; + readonly displayName?: string | null; + }; + }; + } + | { + readonly __typename: 'UserItem'; + readonly id: string; + readonly submissionId: string; + readonly submissionTime?: Date | string | null; + readonly data: JsonObject; + readonly type: { + readonly __typename: 'UserItemType'; + readonly id: string; + readonly name: string; + readonly baseFields: ReadonlyArray<{ + readonly __typename: 'BaseField'; + readonly name: string; + readonly type: GQLFieldType; + readonly required: boolean; + readonly container?: { + readonly __typename: 'Container'; + readonly containerType: GQLContainerType; + readonly keyScalarType?: GQLScalarType | null; + readonly valueScalarType: GQLScalarType; + } | null; + }>; + readonly schemaFieldRoles: { + readonly __typename: 'UserSchemaFieldRoles'; + readonly displayName?: string | null; + readonly createdAt?: string | null; + readonly profileIcon?: string | null; + readonly backgroundImage?: string | null; + }; + }; + } + >; +}; + export type GQLItemTypeHiddenFieldsQueryVariables = Exact<{ [key: string]: never; }>; @@ -17005,8 +16979,7 @@ export type GQLAllManualReviewQueuesQuery = { export type GQLGetLatestUserSubmittedItemsWithThreadsQueryVariables = Exact<{ userId: GQLItemIdentifierInput; reportedMessages: - | ReadonlyArray - | GQLItemIdentifierInput; + ReadonlyArray | GQLItemIdentifierInput; }>; export type GQLGetLatestUserSubmittedItemsWithThreadsQuery = { @@ -17533,8 +17506,7 @@ export type GQLGetMoreInfoForItemsQuery = { export type GQLGetUserItemsQueryVariables = Exact<{ itemIdentifiers: - | ReadonlyArray - | GQLItemIdentifierInput; + ReadonlyArray | GQLItemIdentifierInput; }>; export type GQLGetUserItemsQuery = { @@ -24826,6 +24798,7 @@ export type GQLPersonalSafetySettingsQuery = { readonly moderatorSafetyMuteVideo: boolean; readonly moderatorSafetyGrayscale: boolean; readonly moderatorSafetyBlurLevel: number; + readonly moderatorSafetySepia: boolean; }; } | null; }; @@ -25335,6 +25308,7 @@ export type GQLOrgDefaultSafetySettingsQuery = { readonly moderatorSafetyMuteVideo: boolean; readonly moderatorSafetyGrayscale: boolean; readonly moderatorSafetyBlurLevel: number; + readonly moderatorSafetySepia: boolean; }; } | null; }; @@ -28358,8 +28332,7 @@ export function useGQLActionQuery( GQLActionQueryVariables > & ( - | { variables: GQLActionQueryVariables; skip?: boolean } - | { skip: boolean } + { variables: GQLActionQueryVariables; skip?: boolean } | { skip: boolean } ), ) { const options = { ...defaultOptions, ...baseOptions }; @@ -34449,6 +34422,7 @@ export const GQLManualReviewSafetySettingsDocument = gql` moderatorSafetyMuteVideo moderatorSafetyGrayscale moderatorSafetyBlurLevel + moderatorSafetySepia } } } @@ -35024,228 +34998,6 @@ export type GQLLogSkipMutationOptions = Apollo.BaseMutationOptions< GQLLogSkipMutation, GQLLogSkipMutationVariables >; -export const GQLReleaseJobLockDocument = gql` - mutation ReleaseJobLock($input: ReleaseJobLockInput!) { - releaseJobLock(input: $input) - } -`; -export type GQLReleaseJobLockMutationFn = Apollo.MutationFunction< - GQLReleaseJobLockMutation, - GQLReleaseJobLockMutationVariables ->; - -/** - * __useGQLReleaseJobLockMutation__ - * - * To run a mutation, you first call `useGQLReleaseJobLockMutation` within a React component and pass it any options that fit your needs. - * When your component renders, `useGQLReleaseJobLockMutation` returns a tuple that includes: - * - A mutate function that you can call at any time to execute the mutation - * - An object with fields that represent the current status of the mutation's execution - * - * @param baseOptions options that will be passed into the mutation, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options-2; - * - * @example - * const [gqlReleaseJobLockMutation, { data, loading, error }] = useGQLReleaseJobLockMutation({ - * variables: { - * input: // value for 'input' - * }, - * }); - */ -export function useGQLReleaseJobLockMutation( - baseOptions?: Apollo.MutationHookOptions< - GQLReleaseJobLockMutation, - GQLReleaseJobLockMutationVariables - >, -) { - const options = { ...defaultOptions, ...baseOptions }; - return Apollo.useMutation< - GQLReleaseJobLockMutation, - GQLReleaseJobLockMutationVariables - >(GQLReleaseJobLockDocument, options); -} -export type GQLReleaseJobLockMutationHookResult = ReturnType< - typeof useGQLReleaseJobLockMutation ->; -export type GQLReleaseJobLockMutationResult = - Apollo.MutationResult; -export type GQLReleaseJobLockMutationOptions = Apollo.BaseMutationOptions< - GQLReleaseJobLockMutation, - GQLReleaseJobLockMutationVariables ->; -export const GQLGetRelatedItemsDocument = gql` - query getRelatedItems($itemIdentifiers: [ItemIdentifierInput!]!) { - latestItemSubmissions(itemIdentifiers: $itemIdentifiers) { - ... on UserItem { - id - submissionId - submissionTime - data - type { - id - name - baseFields { - name - type - required - container { - containerType - keyScalarType - valueScalarType - } - } - schemaFieldRoles { - displayName - createdAt - profileIcon - backgroundImage - } - } - } - ... on ContentItem { - id - submissionId - submissionTime - data - type { - id - name - baseFields { - name - type - required - container { - containerType - keyScalarType - valueScalarType - } - } - schemaFieldRoles { - displayName - } - } - } - ... on ThreadItem { - id - submissionId - submissionTime - data - type { - id - name - baseFields { - name - type - required - container { - containerType - keyScalarType - valueScalarType - } - } - schemaFieldRoles { - displayName - } - } - } - } - } -`; - -/** - * __useGQLGetRelatedItemsQuery__ - * - * To run a query within a React component, call `useGQLGetRelatedItemsQuery` and pass it any options that fit your needs. - * When your component renders, `useGQLGetRelatedItemsQuery` returns an object from Apollo Client that contains loading, error, and data properties - * you can use to render your UI. - * - * @param baseOptions options that will be passed into the query, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options; - * - * @example - * const { data, loading, error } = useGQLGetRelatedItemsQuery({ - * variables: { - * itemIdentifiers: // value for 'itemIdentifiers' - * }, - * }); - */ -export function useGQLGetRelatedItemsQuery( - baseOptions: Apollo.QueryHookOptions< - GQLGetRelatedItemsQuery, - GQLGetRelatedItemsQueryVariables - > & - ( - | { variables: GQLGetRelatedItemsQueryVariables; skip?: boolean } - | { skip: boolean } - ), -) { - const options = { ...defaultOptions, ...baseOptions }; - return Apollo.useQuery< - GQLGetRelatedItemsQuery, - GQLGetRelatedItemsQueryVariables - >(GQLGetRelatedItemsDocument, options); -} -export function useGQLGetRelatedItemsLazyQuery( - baseOptions?: Apollo.LazyQueryHookOptions< - GQLGetRelatedItemsQuery, - GQLGetRelatedItemsQueryVariables - >, -) { - const options = { ...defaultOptions, ...baseOptions }; - return Apollo.useLazyQuery< - GQLGetRelatedItemsQuery, - GQLGetRelatedItemsQueryVariables - >(GQLGetRelatedItemsDocument, options); -} -// @ts-ignore -export function useGQLGetRelatedItemsSuspenseQuery( - baseOptions?: Apollo.SuspenseQueryHookOptions< - GQLGetRelatedItemsQuery, - GQLGetRelatedItemsQueryVariables - >, -): Apollo.UseSuspenseQueryResult< - GQLGetRelatedItemsQuery, - GQLGetRelatedItemsQueryVariables ->; -export function useGQLGetRelatedItemsSuspenseQuery( - baseOptions?: - | Apollo.SkipToken - | Apollo.SuspenseQueryHookOptions< - GQLGetRelatedItemsQuery, - GQLGetRelatedItemsQueryVariables - >, -): Apollo.UseSuspenseQueryResult< - GQLGetRelatedItemsQuery | undefined, - GQLGetRelatedItemsQueryVariables ->; -export function useGQLGetRelatedItemsSuspenseQuery( - baseOptions?: - | Apollo.SkipToken - | Apollo.SuspenseQueryHookOptions< - GQLGetRelatedItemsQuery, - GQLGetRelatedItemsQueryVariables - >, -) { - const options = - baseOptions === Apollo.skipToken - ? baseOptions - : { ...defaultOptions, ...baseOptions }; - return Apollo.useSuspenseQuery< - GQLGetRelatedItemsQuery, - GQLGetRelatedItemsQueryVariables - >(GQLGetRelatedItemsDocument, options); -} -export type GQLGetRelatedItemsQueryHookResult = ReturnType< - typeof useGQLGetRelatedItemsQuery ->; -export type GQLGetRelatedItemsLazyQueryHookResult = ReturnType< - typeof useGQLGetRelatedItemsLazyQuery ->; -export type GQLGetRelatedItemsSuspenseQueryHookResult = ReturnType< - typeof useGQLGetRelatedItemsSuspenseQuery ->; -export type GQLGetRelatedItemsQueryResult = Apollo.QueryResult< - GQLGetRelatedItemsQuery, - GQLGetRelatedItemsQueryVariables ->; export const GQLGetCommentsForJobDocument = gql` query GetCommentsForJob($jobId: ID!) { getCommentsForJob(jobId: $jobId) { @@ -35594,6 +35346,180 @@ export type GQLGetThreadHistoryQueryResult = Apollo.QueryResult< GQLGetThreadHistoryQuery, GQLGetThreadHistoryQueryVariables >; +export const GQLGetRelatedItemsDocument = gql` + query getRelatedItems($itemIdentifiers: [ItemIdentifierInput!]!) { + latestItemSubmissions(itemIdentifiers: $itemIdentifiers) { + ... on UserItem { + id + submissionId + submissionTime + data + type { + id + name + baseFields { + name + type + required + container { + containerType + keyScalarType + valueScalarType + } + } + schemaFieldRoles { + displayName + createdAt + profileIcon + backgroundImage + } + } + } + ... on ContentItem { + id + submissionId + submissionTime + data + type { + id + name + baseFields { + name + type + required + container { + containerType + keyScalarType + valueScalarType + } + } + schemaFieldRoles { + displayName + } + } + } + ... on ThreadItem { + id + submissionId + submissionTime + data + type { + id + name + baseFields { + name + type + required + container { + containerType + keyScalarType + valueScalarType + } + } + schemaFieldRoles { + displayName + } + } + } + } + } +`; + +/** + * __useGQLGetRelatedItemsQuery__ + * + * To run a query within a React component, call `useGQLGetRelatedItemsQuery` and pass it any options that fit your needs. + * When your component renders, `useGQLGetRelatedItemsQuery` returns an object from Apollo Client that contains loading, error, and data properties + * you can use to render your UI. + * + * @param baseOptions options that will be passed into the query, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options; + * + * @example + * const { data, loading, error } = useGQLGetRelatedItemsQuery({ + * variables: { + * itemIdentifiers: // value for 'itemIdentifiers' + * }, + * }); + */ +export function useGQLGetRelatedItemsQuery( + baseOptions: Apollo.QueryHookOptions< + GQLGetRelatedItemsQuery, + GQLGetRelatedItemsQueryVariables + > & + ( + | { variables: GQLGetRelatedItemsQueryVariables; skip?: boolean } + | { skip: boolean } + ), +) { + const options = { ...defaultOptions, ...baseOptions }; + return Apollo.useQuery< + GQLGetRelatedItemsQuery, + GQLGetRelatedItemsQueryVariables + >(GQLGetRelatedItemsDocument, options); +} +export function useGQLGetRelatedItemsLazyQuery( + baseOptions?: Apollo.LazyQueryHookOptions< + GQLGetRelatedItemsQuery, + GQLGetRelatedItemsQueryVariables + >, +) { + const options = { ...defaultOptions, ...baseOptions }; + return Apollo.useLazyQuery< + GQLGetRelatedItemsQuery, + GQLGetRelatedItemsQueryVariables + >(GQLGetRelatedItemsDocument, options); +} +// @ts-ignore +export function useGQLGetRelatedItemsSuspenseQuery( + baseOptions?: Apollo.SuspenseQueryHookOptions< + GQLGetRelatedItemsQuery, + GQLGetRelatedItemsQueryVariables + >, +): Apollo.UseSuspenseQueryResult< + GQLGetRelatedItemsQuery, + GQLGetRelatedItemsQueryVariables +>; +export function useGQLGetRelatedItemsSuspenseQuery( + baseOptions?: + | Apollo.SkipToken + | Apollo.SuspenseQueryHookOptions< + GQLGetRelatedItemsQuery, + GQLGetRelatedItemsQueryVariables + >, +): Apollo.UseSuspenseQueryResult< + GQLGetRelatedItemsQuery | undefined, + GQLGetRelatedItemsQueryVariables +>; +export function useGQLGetRelatedItemsSuspenseQuery( + baseOptions?: + | Apollo.SkipToken + | Apollo.SuspenseQueryHookOptions< + GQLGetRelatedItemsQuery, + GQLGetRelatedItemsQueryVariables + >, +) { + const options = + baseOptions === Apollo.skipToken + ? baseOptions + : { ...defaultOptions, ...baseOptions }; + return Apollo.useSuspenseQuery< + GQLGetRelatedItemsQuery, + GQLGetRelatedItemsQueryVariables + >(GQLGetRelatedItemsDocument, options); +} +export type GQLGetRelatedItemsQueryHookResult = ReturnType< + typeof useGQLGetRelatedItemsQuery +>; +export type GQLGetRelatedItemsLazyQueryHookResult = ReturnType< + typeof useGQLGetRelatedItemsLazyQuery +>; +export type GQLGetRelatedItemsSuspenseQueryHookResult = ReturnType< + typeof useGQLGetRelatedItemsSuspenseQuery +>; +export type GQLGetRelatedItemsQueryResult = Apollo.QueryResult< + GQLGetRelatedItemsQuery, + GQLGetRelatedItemsQueryVariables +>; export const GQLItemTypeHiddenFieldsDocument = gql` query ItemTypeHiddenFields { myOrg { @@ -43093,6 +43019,7 @@ export const GQLPersonalSafetySettingsDocument = gql` moderatorSafetyMuteVideo moderatorSafetyGrayscale moderatorSafetyBlurLevel + moderatorSafetySepia } } } @@ -45440,6 +45367,7 @@ export const GQLOrgDefaultSafetySettingsDocument = gql` moderatorSafetyMuteVideo moderatorSafetyGrayscale moderatorSafetyBlurLevel + moderatorSafetySepia } } } @@ -45646,9 +45574,9 @@ export const namedOperations = { GetDecidedJob: 'GetDecidedJob', ManualReviewSafetySettings: 'ManualReviewSafetySettings', ManualReviewJobInfo: 'ManualReviewJobInfo', - getRelatedItems: 'getRelatedItems', GetCommentsForJob: 'GetCommentsForJob', getThreadHistory: 'getThreadHistory', + getRelatedItems: 'getRelatedItems', ItemTypeHiddenFields: 'ItemTypeHiddenFields', AllManualReviewQueues: 'AllManualReviewQueues', getLatestUserSubmittedItemsWithThreads: @@ -45764,7 +45692,6 @@ export const namedOperations = { DequeueManualReviewJob: 'DequeueManualReviewJob', SubmitManualReviewDecision: 'SubmitManualReviewDecision', LogSkip: 'LogSkip', - ReleaseJobLock: 'ReleaseJobLock', AddJobComment: 'AddJobComment', DeleteJobComment: 'DeleteJobComment', DeleteRoutingRule: 'DeleteRoutingRule', diff --git a/client/src/models/safetySettings.test.ts b/client/src/models/safetySettings.test.ts new file mode 100644 index 000000000..ff049303c --- /dev/null +++ b/client/src/models/safetySettings.test.ts @@ -0,0 +1,77 @@ +import { + colorSchemeClassName, + colorSchemeFromPreferences, + preferencesFromColorScheme, +} from './safetySettings'; + +describe('safetySettings color scheme', () => { + it('maps boolean preferences to a color scheme', () => { + expect( + colorSchemeFromPreferences({ + moderatorSafetyGrayscale: true, + moderatorSafetySepia: false, + }), + ).toBe('GRAYSCALE'); + expect( + colorSchemeFromPreferences({ + moderatorSafetyGrayscale: false, + moderatorSafetySepia: true, + }), + ).toBe('SEPIA'); + expect( + colorSchemeFromPreferences({ + moderatorSafetyGrayscale: false, + moderatorSafetySepia: false, + }), + ).toBe('NONE'); + }); + + it('prefers grayscale if both stored flags are set', () => { + expect( + colorSchemeFromPreferences({ + moderatorSafetyGrayscale: true, + moderatorSafetySepia: true, + }), + ).toBe('GRAYSCALE'); + }); + + it('maps a color scheme back to mutually exclusive booleans', () => { + expect(preferencesFromColorScheme('GRAYSCALE')).toEqual({ + moderatorSafetyGrayscale: true, + moderatorSafetySepia: false, + }); + expect(preferencesFromColorScheme('SEPIA')).toEqual({ + moderatorSafetyGrayscale: false, + moderatorSafetySepia: true, + }); + expect(preferencesFromColorScheme('NONE')).toEqual({ + moderatorSafetyGrayscale: false, + moderatorSafetySepia: false, + }); + }); + + it('round-trips every scheme', () => { + for (const scheme of ['NONE', 'GRAYSCALE', 'SEPIA'] as const) { + expect( + colorSchemeFromPreferences(preferencesFromColorScheme(scheme)), + ).toBe(scheme); + } + }); + + it('maps a color scheme to its Tailwind class', () => { + expect(colorSchemeClassName('GRAYSCALE')).toBe('grayscale'); + expect(colorSchemeClassName('SEPIA')).toBe('sepia'); + expect(colorSchemeClassName('NONE')).toBe(''); + }); + + it('never yields both filter classes even if both flags are set', () => { + expect( + colorSchemeClassName( + colorSchemeFromPreferences({ + moderatorSafetyGrayscale: true, + moderatorSafetySepia: true, + }), + ), + ).toBe('grayscale'); + }); +}); diff --git a/client/src/models/safetySettings.ts b/client/src/models/safetySettings.ts new file mode 100644 index 000000000..71d72669c --- /dev/null +++ b/client/src/models/safetySettings.ts @@ -0,0 +1,62 @@ +export const MODERATOR_SAFETY_COLOR_SCHEMES = [ + 'NONE', + 'GRAYSCALE', + 'SEPIA', +] as const; + +export type ModeratorSafetyColorScheme = + (typeof MODERATOR_SAFETY_COLOR_SCHEMES)[number]; + +export const MODERATOR_SAFETY_COLOR_SCHEME_LABELS: Record< + ModeratorSafetyColorScheme, + string +> = { + NONE: 'None', + GRAYSCALE: 'Grayscale', + SEPIA: 'Sepia', +}; + +// The API stores the color scheme as two independent booleans +// (moderatorSafetyGrayscale / moderatorSafetySepia) so the schema stays +// backwards-compatible; the UI models them as one mutually exclusive scheme. +// Grayscale wins if both flags are somehow set — the UI only ever writes one. +export function colorSchemeFromPreferences(preferences: { + moderatorSafetyGrayscale: boolean; + moderatorSafetySepia: boolean; +}): ModeratorSafetyColorScheme { + if (preferences.moderatorSafetyGrayscale) { + return 'GRAYSCALE'; + } + if (preferences.moderatorSafetySepia) { + return 'SEPIA'; + } + return 'NONE'; +} + +// Tailwind filter class for a resolved color scheme. Deriving classes from the +// resolved scheme (rather than the raw booleans) keeps the "grayscale wins" +// invariant even if both flags are somehow set. +export function colorSchemeClassName( + colorScheme: ModeratorSafetyColorScheme, +): string { + switch (colorScheme) { + case 'GRAYSCALE': + return 'grayscale'; + case 'SEPIA': + return 'sepia'; + case 'NONE': + return ''; + } +} + +export function preferencesFromColorScheme( + colorScheme: ModeratorSafetyColorScheme, +): { + moderatorSafetyGrayscale: boolean; + moderatorSafetySepia: boolean; +} { + return { + moderatorSafetyGrayscale: colorScheme === 'GRAYSCALE', + moderatorSafetySepia: colorScheme === 'SEPIA', + }; +} diff --git a/client/src/models/signal.ts b/client/src/models/signal.ts index 24f06f661..5c74378a6 100644 --- a/client/src/models/signal.ts +++ b/client/src/models/signal.ts @@ -29,6 +29,10 @@ export function integrationForSignalType(type: string) { case 'OPEN_AI_HATE_TEXT_MODEL': case 'OPEN_AI_HATE_THREATENING_TEXT_MODEL': case 'OPEN_AI_SELF_HARM_IMAGE_MODEL': + case 'OPEN_AI_SELF_HARM_INSTRUCTIONS_IMAGE_MODEL': + case 'OPEN_AI_SELF_HARM_INSTRUCTIONS_TEXT_MODEL': + case 'OPEN_AI_SELF_HARM_INTENT_IMAGE_MODEL': + case 'OPEN_AI_SELF_HARM_INTENT_TEXT_MODEL': case 'OPEN_AI_SELF_HARM_TEXT_MODEL': case 'OPEN_AI_SEXUAL_IMAGE_MODEL': case 'OPEN_AI_SEXUAL_MINORS_TEXT_MODEL': diff --git a/client/src/utils/collections.ts b/client/src/utils/collections.ts index 4bf43da3d..3071b6cca 100644 --- a/client/src/utils/collections.ts +++ b/client/src/utils/collections.ts @@ -22,6 +22,8 @@ export function filterNullOrUndefined( return array.filter((it) => it !== null && it !== undefined) as T[]; } -export function arrayFromArrayOrSingleItem(array: readonly T[] | T): T[] { - return Array.isArray(array) ? [...array] : [array]; +export function arrayFromArrayOrSingleItem( + array: readonly string[] | string, +): string[] { + return typeof array === 'string' ? [array] : [...array]; } diff --git a/client/src/utils/time.test.ts b/client/src/utils/time.test.ts new file mode 100644 index 000000000..dc58b2339 --- /dev/null +++ b/client/src/utils/time.test.ts @@ -0,0 +1,106 @@ +import { + parseDatetimeToMonthDayYearDateStringInCurrentTimeZone, + parseDatetimeToReadableStringInCurrentTimeZone, + parseDatetimeToReadableStringInUTC, + safeFormat, + safeFormatDistanceToNow, +} from './time'; + +describe('Time utils tests', () => { + describe('safeFormat', () => { + it('formats a valid date', () => { + expect(safeFormat('2026-07-15T13:06:00Z', 'yyyy-MM-dd')).toBe( + '2026-07-15', + ); + }); + + it('formats the epoch instead of treating it as empty', () => { + expect(safeFormat(new Date(0), 'yyyy')).not.toBe('Unknown'); + }); + + it('returns the fallback for an unparseable string without throwing', () => { + expect(safeFormat('not a date', 'yyyy-MM-dd')).toBe('Unknown'); + }); + + it('returns the fallback for null and undefined', () => { + expect(safeFormat(null, 'yyyy-MM-dd')).toBe('Unknown'); + expect(safeFormat(undefined, 'yyyy-MM-dd')).toBe('Unknown'); + }); + + it('honors a custom fallback', () => { + expect(safeFormat('not a date', 'yyyy-MM-dd', '-')).toBe('-'); + }); + }); + + describe('safeFormatDistanceToNow', () => { + it('returns a relative string for a valid date', () => { + expect(safeFormatDistanceToNow(new Date())).toContain('ago'); + }); + + it('returns the fallback for an unparseable string without throwing', () => { + expect(safeFormatDistanceToNow('not a date')).toBe('Unknown'); + }); + + it('returns the fallback for null', () => { + expect(safeFormatDistanceToNow(null)).toBe('Unknown'); + }); + }); + + describe('parseDatetimeToReadableStringInUTC', () => { + it('formats a valid date', () => { + expect( + parseDatetimeToReadableStringInUTC('2026-07-15T13:06:00Z'), + ).toMatch(/07\/15\/26/); + }); + + it('returns fallback for an unparseable string', () => { + expect(parseDatetimeToReadableStringInUTC('not a date')).toBe('Unknown'); + }); + + it('returns fallback for null', () => { + expect(parseDatetimeToReadableStringInUTC(null)).toBe('Unknown'); + }); + }); + + describe('parseDatetimeToReadableStringInCurrentTimeZone', () => { + it('formats a valid date', () => { + expect( + parseDatetimeToReadableStringInCurrentTimeZone('2026-07-15T13:06:00Z'), + ).toMatch(/07\/15\/26/); + }); + + it('returns fallback for an unparseable string', () => { + expect(parseDatetimeToReadableStringInCurrentTimeZone('not a date')).toBe( + 'Unknown', + ); + }); + + it('returns fallback for null', () => { + expect(parseDatetimeToReadableStringInCurrentTimeZone(null)).toBe( + 'Unknown', + ); + }); + }); + + describe('parseDatetimeToMonthDayYearDateStringInCurrentTimeZone', () => { + it('formats a valid date', () => { + expect( + parseDatetimeToMonthDayYearDateStringInCurrentTimeZone( + '2026-07-15T13:06:00Z', + ), + ).toMatch(/Jul 15, 2026/); + }); + + it('returns fallback for an unparseable string', () => { + expect( + parseDatetimeToMonthDayYearDateStringInCurrentTimeZone('not a date'), + ).toBe('Unknown'); + }); + + it('returns fallback for null', () => { + expect(parseDatetimeToMonthDayYearDateStringInCurrentTimeZone(null)).toBe( + 'Unknown', + ); + }); + }); +}); diff --git a/client/src/utils/time.ts b/client/src/utils/time.ts index 8be151613..74f55ed73 100644 --- a/client/src/utils/time.ts +++ b/client/src/utils/time.ts @@ -1,5 +1,12 @@ import { DateString } from '@roostorg/coop-types'; -import { addDays, addHours, format, isBefore } from 'date-fns'; +import { + addDays, + addHours, + format, + formatDistanceToNow, + isBefore, + isValid, +} from 'date-fns'; export enum LookbackLength { CUSTOM = 'Custom', @@ -32,10 +39,41 @@ function toDate(date: string | DateString | Date): Date { return date instanceof Date ? date : new Date(date); } +type MaybeDate = string | DateString | Date | null | undefined; + +/** + * date-fns `format` throws on an invalid date; this returns `fallback` instead. + */ +export function safeFormat( + date: MaybeDate, + formatStr: string, + fallback = 'Unknown', +): string { + if (date == null) return fallback; + const d = toDate(date); + return isValid(d) ? format(d, formatStr) : fallback; +} + +/** + * date-fns `formatDistanceToNow` throws on an invalid date; this returns + * `fallback` instead. + */ +export function safeFormatDistanceToNow( + date: MaybeDate, + fallback = 'Unknown', +): string { + if (date == null) return fallback; + const d = toDate(date); + return isValid(d) ? formatDistanceToNow(d, { addSuffix: true }) : fallback; +} + export function parseDatetimeToReadableStringInUTC( - date: string | DateString | Date, + date: MaybeDate, + fallback = 'Unknown', ): string { + if (date == null) return fallback; const d = toDate(date); + if (!isValid(d)) return fallback; return new Intl.DateTimeFormat('en-US', { timeZone: 'UTC', month: '2-digit', @@ -51,15 +89,21 @@ export function parseDatetimeToReadableStringInUTC( } export function parseDatetimeToReadableStringInCurrentTimeZone( - date: string | DateString | Date, + date: MaybeDate, + fallback = 'Unknown', ): string { - return format(toDate(date), 'MM/dd/yy hh:mm:ss a'); + if (date == null) return fallback; + const d = toDate(date); + return isValid(d) ? format(d, 'MM/dd/yy hh:mm:ss a') : fallback; } export function parseDatetimeToMonthDayYearDateStringInCurrentTimeZone( - date: string | DateString | Date, + date: MaybeDate, + fallback = 'Unknown', ): string { - return format(toDate(date), 'MMM d, yyyy'); + if (date == null) return fallback; + const d = toDate(date); + return isValid(d) ? format(d, 'MMM d, yyyy') : fallback; } export function startOfHourUTC(date: Date): Date { diff --git a/client/src/webpages/dashboard/banks/hash/HashBankForm.tsx b/client/src/webpages/dashboard/banks/hash/HashBankForm.tsx index faa639e67..b61f95256 100644 --- a/client/src/webpages/dashboard/banks/hash/HashBankForm.tsx +++ b/client/src/webpages/dashboard/banks/hash/HashBankForm.tsx @@ -23,9 +23,7 @@ import { } from '../../../../graphql/generated'; type SchemaField = GQLExchangeApiSchemaQuery['exchangeApiSchema'] extends - | infer S - | null - | undefined + infer S | null | undefined ? S extends { config_schema: { fields: ReadonlyArray } } ? F : never diff --git a/client/src/webpages/dashboard/bulk_actioning/BulkActioningDashboard.tsx b/client/src/webpages/dashboard/bulk_actioning/BulkActioningDashboard.tsx index 4d6dcca3f..d2b32017d 100644 --- a/client/src/webpages/dashboard/bulk_actioning/BulkActioningDashboard.tsx +++ b/client/src/webpages/dashboard/bulk_actioning/BulkActioningDashboard.tsx @@ -314,8 +314,7 @@ export default function BulkActioningDashboard() { // issue. See https://github.com/microsoft/TypeScript/issues/17002 for // more details. const policyId = policyIds satisfies - | string - | readonly string[] as string; + string | readonly string[] as string; setSelectedPolicyIds([policyId]); } }} diff --git a/client/src/webpages/dashboard/components/CoopButton.tsx b/client/src/webpages/dashboard/components/CoopButton.tsx index ec6c34558..d46f60447 100644 --- a/client/src/webpages/dashboard/components/CoopButton.tsx +++ b/client/src/webpages/dashboard/components/CoopButton.tsx @@ -6,11 +6,7 @@ import { Link } from 'react-router-dom'; export type CoopButtonSize = 'small' | 'middle' | 'large'; export type CoopButtonType = - | 'primary' - | 'secondary' - | 'danger' - | 'green' - | 'link'; + 'primary' | 'secondary' | 'danger' | 'green' | 'link'; export type CoopButtonFontWeight = 'normal' | 'semibold'; export type CoopButtonIconStyle = 'fill' | 'stroke'; diff --git a/client/src/webpages/dashboard/components/RoundedTag.tsx b/client/src/webpages/dashboard/components/RoundedTag.tsx index 9b2cbe223..81c90db54 100644 --- a/client/src/webpages/dashboard/components/RoundedTag.tsx +++ b/client/src/webpages/dashboard/components/RoundedTag.tsx @@ -4,9 +4,7 @@ export default function RoundedTag( props: { title: string; } & ( - | { status: GQLRuleStatus } - | { environment: GQLRuleEnvironment } - | object + { status: GQLRuleStatus } | { environment: GQLRuleEnvironment } | object ), ) { const { title } = props; diff --git a/client/src/webpages/dashboard/investigation/ItemInvestigation.tsx b/client/src/webpages/dashboard/investigation/ItemInvestigation.tsx index 7526c912c..c0ae2937c 100644 --- a/client/src/webpages/dashboard/investigation/ItemInvestigation.tsx +++ b/client/src/webpages/dashboard/investigation/ItemInvestigation.tsx @@ -11,6 +11,7 @@ import ItemAction from '@/components/ItemAction'; import { GQLItemHistoryResult, GQLItemType, + GQLSchemaFieldRoles, GQLThreadItem, GQLUserItem, useGQLGetItemsWithIdLazyQuery, @@ -401,14 +402,8 @@ export default function ItemInvestigation(props: { // shadowing it. const derivedIpAddress = (() => { try { - return getFieldValueForRole( - { - // eslint-disable-next-line custom-rules/no-casting-in-getFieldValueForRole - type: item.type as Parameters< - typeof getFieldValueForRole - >[0]['type'], - data: item.data, - }, + return getFieldValueForRole( + { type: item.type, data: item.data }, 'ipAddress', ); } catch { diff --git a/client/src/webpages/dashboard/item_types/ItemTypePreview.tsx b/client/src/webpages/dashboard/item_types/ItemTypePreview.tsx index 8621e3d15..49a742f7c 100644 --- a/client/src/webpages/dashboard/item_types/ItemTypePreview.tsx +++ b/client/src/webpages/dashboard/item_types/ItemTypePreview.tsx @@ -37,7 +37,7 @@ export default function ItemTypePreview(props: { case 'CONTENT': return } />; case 'THREAD': - return } />; + return ; case 'USER': return } />; } diff --git a/client/src/webpages/dashboard/item_types/itemTypeCodeSampleUtils.ts b/client/src/webpages/dashboard/item_types/itemTypeCodeSampleUtils.ts index 283f410b9..e7f8153c2 100644 --- a/client/src/webpages/dashboard/item_types/itemTypeCodeSampleUtils.ts +++ b/client/src/webpages/dashboard/item_types/itemTypeCodeSampleUtils.ts @@ -114,12 +114,7 @@ export function generateItemData( } type JsonValue = - | string - | number - | boolean - | null - | JsonValue[] - | { [k: string]: JsonValue }; + string | number | boolean | null | JsonValue[] | { [k: string]: JsonValue }; export function translateJSONObjectToPHP(json: JsonValue): string { function translateValue(value: JsonValue, indentLevel: number = 0): string { diff --git a/client/src/webpages/dashboard/mrt/ManualReviewQueueJobsPreview.tsx b/client/src/webpages/dashboard/mrt/ManualReviewQueueJobsPreview.tsx index 5ed2567c8..5437f7cd8 100644 --- a/client/src/webpages/dashboard/mrt/ManualReviewQueueJobsPreview.tsx +++ b/client/src/webpages/dashboard/mrt/ManualReviewQueueJobsPreview.tsx @@ -1,5 +1,5 @@ +import { safeFormat } from '@/utils/time'; import { gql } from '@apollo/client'; -import { format } from 'date-fns'; import { useMemo } from 'react'; import { useParams } from 'react-router-dom'; import { Row } from 'react-table'; @@ -207,7 +207,7 @@ export default function ManualReviewQueueJobsPreview() { ), numReports:
{values.numReports}
, createdAt: ( -
{format(new Date(values.createdAt), 'MM/dd/yy hh:mm a')}
+
{safeFormat(values.createdAt, 'MM/dd/yy hh:mm a')}
), jobId: values.jobId, values, diff --git a/client/src/webpages/dashboard/mrt/ManualReviewQueuesDashboard.tsx b/client/src/webpages/dashboard/mrt/ManualReviewQueuesDashboard.tsx index 7e8f74081..b486a65f0 100644 --- a/client/src/webpages/dashboard/mrt/ManualReviewQueuesDashboard.tsx +++ b/client/src/webpages/dashboard/mrt/ManualReviewQueuesDashboard.tsx @@ -6,7 +6,14 @@ import { gql } from '@apollo/client'; import Button from 'antd/lib/button'; import Checkbox from 'antd/lib/checkbox'; import Input from 'antd/lib/input'; -import { useCallback, useEffect, useMemo, useRef, useState } from 'react'; +import { + Fragment, + useCallback, + useEffect, + useMemo, + useRef, + useState, +} from 'react'; import { Helmet } from 'react-helmet-async'; import { Link, useNavigate } from 'react-router-dom'; @@ -208,8 +215,29 @@ export default function ManualReviewQueuesDashboard() { fetchPolicy: 'no-cache', pollInterval: 5000, }); + const [deleteError, setDeleteError] = useState<{ + message: string; + ruleNames: string[]; + } | null>(null); const [deleteReviewQueue] = useGQLDeleteManualReviewQueueMutation({ - onError: () => {}, + onError: (error) => { + const gqlError = error.graphQLErrors[0]; + const message = gqlError?.message ?? 'Failed to delete queue.'; + const rawDetail = gqlError?.extensions?.['detail']; + let ruleNames: string[] = []; + if (typeof rawDetail === 'string') { + try { + const parsed: unknown = JSON.parse(rawDetail); + if ( + Array.isArray(parsed) && + parsed.every((n): n is string => typeof n === 'string') + ) { + ruleNames = parsed; + } + } catch {} + } + setDeleteError({ message, ruleNames }); + }, onCompleted: async () => refetch(), }); const [addFavoriteMRTQueue] = useGQLAddFavoriteMrtQueueMutation({ @@ -443,6 +471,41 @@ export default function ManualReviewQueuesDashboard() { ); + const deleteErrorModal = ( + setDeleteError(null), + type: 'primary', + }, + ]} + onClose={() => setDeleteError(null)} + > + {deleteError?.ruleNames && deleteError.ruleNames.length > 0 ? ( +
+

+ This queue cannot be deleted because it is used by the following + routing rules: +

+

+ {deleteError.ruleNames.map((name, i) => ( + + {i > 0 && ', '} + {name} + + ))} +

+

Update or delete those rules first.

+
+ ) : ( +

{deleteError?.message}

+ )} +
+ ); + const onDeleteReviewQueue = (id: string) => { deleteReviewQueue({ variables: { id }, @@ -929,6 +992,7 @@ export default function ManualReviewQueuesDashboard() { /> } {deleteModal} + {deleteErrorModal} {deleteAllJobsModal}
); diff --git a/client/src/webpages/dashboard/mrt/ManualReviewSafetySettings.tsx b/client/src/webpages/dashboard/mrt/ManualReviewSafetySettings.tsx index 4044e57b5..b1649c083 100644 --- a/client/src/webpages/dashboard/mrt/ManualReviewSafetySettings.tsx +++ b/client/src/webpages/dashboard/mrt/ManualReviewSafetySettings.tsx @@ -1,4 +1,11 @@ import { Label } from '@/coop-ui/Label'; +import { + Select, + SelectContent, + SelectItem, + SelectTrigger, + SelectValue, +} from '@/coop-ui/Select'; import { Slider } from '@/coop-ui/Slider'; import { Switch } from '@/coop-ui/Switch'; import { gql } from '@apollo/client'; @@ -15,6 +22,14 @@ import { useGQLSetModeratorSafetySettingsMutation, } from '../../../graphql/generated'; import GoldenRetrieverPuppies from '../../../images/GoldenRetrieverPuppies.png'; +import { + colorSchemeClassName, + colorSchemeFromPreferences, + MODERATOR_SAFETY_COLOR_SCHEME_LABELS, + MODERATOR_SAFETY_COLOR_SCHEMES, + preferencesFromColorScheme, + type ModeratorSafetyColorScheme, +} from '../../../models/safetySettings'; import { BLUR_LEVELS, BlurStrength, @@ -27,6 +42,7 @@ gql` moderatorSafetyMuteVideo moderatorSafetyGrayscale moderatorSafetyBlurLevel + moderatorSafetySepia } } } @@ -47,10 +63,12 @@ export default function ManualReviewSafetySettings() { moderatorSafetyBlurLevel: BlurStrength; moderatorSafetyGrayscale: boolean; moderatorSafetyMuteVideo: boolean; + moderatorSafetySepia: boolean; }>({ moderatorSafetyBlurLevel: 2, moderatorSafetyGrayscale: true, moderatorSafetyMuteVideo: true, + moderatorSafetySepia: false, }); const [notificationApi, notificationContextHolder] = notification.useNotification(); @@ -76,11 +94,13 @@ export default function ManualReviewSafetySettings() { moderatorSafetyMuteVideo, moderatorSafetyGrayscale, moderatorSafetyBlurLevel, + moderatorSafetySepia, } = data.me.interfacePreferences; setSettings({ moderatorSafetyMuteVideo, moderatorSafetyGrayscale, moderatorSafetyBlurLevel: moderatorSafetyBlurLevel as BlurStrength, + moderatorSafetySepia, }); }, [data?.me?.interfacePreferences]); @@ -123,27 +143,35 @@ export default function ManualReviewSafetySettings() { step={1} /> -
-
- - setSettings({ - ...settings, - moderatorSafetyGrayscale: value, - }) - } - checked={settings.moderatorSafetyGrayscale} - /> - -
+
+ +
setSettings({ ...settings, @@ -161,7 +189,7 @@ export default function ManualReviewSafetySettings() { settings.moderatorSafetyBlurLevel != null ? BLUR_LEVELS[settings.moderatorSafetyBlurLevel] : 'blur-sm' - } ${settings.moderatorSafetyGrayscale ? 'grayscale' : ''}`} + } ${colorSchemeClassName(colorSchemeFromPreferences(settings))}`} alt="puppies" src={GoldenRetrieverPuppies} /> diff --git a/client/src/webpages/dashboard/mrt/manual_review_job/IframeContentDisplayComponent.tsx b/client/src/webpages/dashboard/mrt/manual_review_job/IframeContentDisplayComponent.tsx index 954849b5c..02a72e96a 100644 --- a/client/src/webpages/dashboard/mrt/manual_review_job/IframeContentDisplayComponent.tsx +++ b/client/src/webpages/dashboard/mrt/manual_review_job/IframeContentDisplayComponent.tsx @@ -25,25 +25,35 @@ export default function IframeContentDisplayComponent(props: { blur: boolean; grayscale: boolean; shouldTranslate: boolean; + sepia: boolean; }>({ blur: true, grayscale: false, shouldTranslate: false, + sepia: false, }); - const { blur, grayscale, shouldTranslate } = state; + const { blur, grayscale, shouldTranslate, sepia } = state; const { loading, data } = useGQLPersonalSafetySettingsQuery(); - const { moderatorSafetyBlurLevel = 2, moderatorSafetyGrayscale = true } = - data?.me?.interfacePreferences ?? {}; + const { + moderatorSafetyBlurLevel = 2, + moderatorSafetyGrayscale = true, + moderatorSafetySepia = false, + } = data?.me?.interfacePreferences ?? {}; useEffect(() => { setState({ blur: moderatorSafetyBlurLevel !== 0, grayscale: moderatorSafetyGrayscale, shouldTranslate: false, + sepia: moderatorSafetySepia, }); - }, [moderatorSafetyBlurLevel, moderatorSafetyGrayscale]); + }, [ + moderatorSafetyBlurLevel, + moderatorSafetyGrayscale, + moderatorSafetySepia, + ]); useEffect(() => { // Translation status messages come from the content proxy. With no proxy @@ -77,6 +87,7 @@ export default function IframeContentDisplayComponent(props: { blur: blur ? moderatorSafetyBlurLevel : 0, grayscale, shouldTranslate, + sepia, }, proxyUrl, ); @@ -93,6 +104,7 @@ export default function IframeContentDisplayComponent(props: { blur: blur ? moderatorSafetyBlurLevel : 0, grayscale, shouldTranslate, + sepia, }, proxyUrl, ); @@ -116,6 +128,7 @@ export default function IframeContentDisplayComponent(props: { shouldTranslate, contentProxyUrl, isIframeLoading, + sepia, ], ); diff --git a/client/src/webpages/dashboard/mrt/manual_review_job/ManualReviewJobContentBlurableImage.tsx b/client/src/webpages/dashboard/mrt/manual_review_job/ManualReviewJobContentBlurableImage.tsx index 973a9db15..32f58cfe1 100644 --- a/client/src/webpages/dashboard/mrt/manual_review_job/ManualReviewJobContentBlurableImage.tsx +++ b/client/src/webpages/dashboard/mrt/manual_review_job/ManualReviewJobContentBlurableImage.tsx @@ -14,6 +14,7 @@ export default function ManualReviewJobContentBlurableImage(props: { blurStrength?: BlurStrength; grayscale?: boolean; disableZoom?: boolean; + sepia?: boolean; }; onError?: () => void; }) { @@ -25,6 +26,7 @@ export default function ManualReviewJobContentBlurableImage(props: { blurStrength = 0, grayscale = false, disableZoom = false, + sepia = false, } = options ?? {}; const [clicked, setClicked] = useState(false); @@ -49,7 +51,7 @@ export default function ManualReviewJobContentBlurableImage(props: { setClicked(true)} diff --git a/client/src/webpages/dashboard/mrt/manual_review_job/ManualReviewJobContentBlurableVideo.tsx b/client/src/webpages/dashboard/mrt/manual_review_job/ManualReviewJobContentBlurableVideo.tsx index 941076025..e874a5976 100644 --- a/client/src/webpages/dashboard/mrt/manual_review_job/ManualReviewJobContentBlurableVideo.tsx +++ b/client/src/webpages/dashboard/mrt/manual_review_job/ManualReviewJobContentBlurableVideo.tsx @@ -1,6 +1,6 @@ import { PlayCircleFilled } from '@ant-design/icons'; import { useEffect, useRef, useState } from 'react'; -import ReactPlayer from 'react-player/lazy'; +import ReactPlayer from 'react-player'; import CoopModal from '../../components/CoopModal'; diff --git a/client/src/webpages/dashboard/mrt/manual_review_job/ManualReviewJobReview.tsx b/client/src/webpages/dashboard/mrt/manual_review_job/ManualReviewJobReview.tsx index 7f528b209..7f2ca4e5b 100644 --- a/client/src/webpages/dashboard/mrt/manual_review_job/ManualReviewJobReview.tsx +++ b/client/src/webpages/dashboard/mrt/manual_review_job/ManualReviewJobReview.tsx @@ -39,7 +39,6 @@ import { useGQLDequeueManualReviewJobMutation, useGQLLogSkipMutation, useGQLManualReviewJobInfoQuery, - useGQLReleaseJobLockMutation, useGQLSubmitManualReviewDecisionMutation, type GQLActionParameter, type GQLThreadManualReviewJobPayload, @@ -221,10 +220,6 @@ gql` mutation LogSkip($input: LogSkipInput!) { logSkip(input: $input) } - - mutation ReleaseJobLock($input: ReleaseJobLockInput!) { - releaseJobLock(input: $input) - } `; enum BuiltInActionType { @@ -342,17 +337,18 @@ function ManualReviewJobReviewImpl(props: { const actionStore = useContext(ManualReviewActionStore); - const setSelectedRelatedActions = ( - actions: ManualReviewJobEnqueuedActionData[], - ) => { - actionStore?.setActions( - actions.map((it) => ({ - itemId: it.target.identifier.itemId, - action: it.action, - })), - ); - selectedRelatedActionsSetter(actions); - }; + const setSelectedRelatedActions = useCallback( + (actions: ManualReviewJobEnqueuedActionData[]) => { + actionStore?.setActions( + actions.map((it) => ({ + itemId: it.target.identifier.itemId, + action: it.action, + })), + ); + selectedRelatedActionsSetter(actions); + }, + [actionStore], + ); const { queueId, jobId, lockToken } = useParams<{ queueId?: string; @@ -364,12 +360,12 @@ function ManualReviewJobReviewImpl(props: { const mrtParentComponentRef = useRef(null); const reportedUserRef = useRef(null); - const resetState = () => { + const resetState = useCallback(() => { setSelectedPrimaryActions([]); setSelectedPrimaryPolicies([]); setSelectedRelatedActions([]); setDecisionReason(undefined); - }; + }, [setSelectedRelatedActions]); const { data, @@ -389,9 +385,12 @@ function ManualReviewJobReviewImpl(props: { onCompleted: (data) => { // Here, we update the URL to include the queue ID, job ID, and lock // token. That way, users are able to send around the URL to others. - // In case we can't find the required job, we can just fail silently. const { dequeueManualReviewJob } = data; if (dequeueManualReviewJob == null) { + // No reviewable job: the queue is drained, or everything left is + // skipped by this reviewer. Send them back to the queue list instead + // of leaving them on a perpetual loading spinner. + navigate('/dashboard/manual_review/queues', { replace: true }); return; } @@ -739,10 +738,6 @@ function ManualReviewJobReviewImpl(props: { fetchPolicy: 'no-cache', }); - const [releaseJobLock] = useGQLReleaseJobLockMutation({ - fetchPolicy: 'no-cache', - }); - const advanceToNextJobAfterInvalidation = useCallback(async () => { setIsAdvancingToNextJob(true); try { @@ -754,8 +749,7 @@ function ManualReviewJobReviewImpl(props: { } finally { setIsAdvancingToNextJob(false); } - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [getNextJob, navigate]); + }, [getNextJob, navigate, resetState]); // Runs after the invalidate mutation resolves. Refreshes the job view, // and if invalidation deleted the current job, advances to the next one. @@ -783,25 +777,50 @@ function ManualReviewJobReviewImpl(props: { }, [jobId, queueId, refetchJobInfo, advanceToNextJobAfterInvalidation]); const skipToNextJob = async () => { - // First, release the lock on the current job and log the skip + // This is wired straight to a button's `onClick`, so nothing downstream + // catches a rejection: any error escaping here is an unhandled promise + // rejection and the reviewer gets no feedback at all. Both the skip and + // the follow-up dequeue therefore handle rejection explicitly. + const showSkipFailed = () => + setModalInfo({ + visible: true, + modalBody: 'Failed to skip this job. Please try again.', + footer: [{ title: 'Ok', type: 'primary', onClick: hideModal }], + }); + + // Skipping is one server-side operation: it logs the skip, hides the job + // from this reviewer for the skip window, and releases the lock so the + // job returns to the shared pool for everyone else. if (queueId && job?.id && lockToken) { - await Promise.all([ - logSkip(), - releaseJobLock({ - variables: { - input: { - queueId, - jobId: job.id, - lockToken, - }, - }, - }), - ]); + let skipped: boolean; + try { + const result = await logSkip(); + skipped = result.data?.logSkip === true; + } catch { + // Network/GraphQL failure. Same reviewer-facing outcome as a falsy + // response: nothing was released or hidden. + skipped = false; + } + if (!skipped) { + // Stay on the current job so the reviewer can retry (or decide) + // instead of advancing past it. + showSkipFailed(); + return; + } } // Reset state and try to get the next job resetState(); - const result = await getNextJob(); + let result; + try { + result = await getNextJob(); + } catch { + // The skip already succeeded, so the job is gone from this reviewer's + // view; only the advance failed. Surface it rather than leaving the + // reviewer on a job they no longer hold. + showSkipFailed(); + return; + } // If there's no next job, redirect to the queues page if (result.data?.dequeueManualReviewJob == null) { @@ -809,7 +828,11 @@ function ManualReviewJobReviewImpl(props: { } }; - if (loading || jobDataLoading || (!closedJob && !lockToken)) { + // `loading && !data`: the job-info query reloads in place (e.g. when its + // jobIds variable changes while advancing, or on refetch after an + // invalidation). Once we have data, keep the current view up during those + // reloads instead of flashing the full-screen spinner. + if ((loading && !data) || jobDataLoading || (!closedJob && !lockToken)) { return (
@@ -1402,8 +1425,7 @@ function ManualReviewJobReviewImpl(props: { // issue. See https://github.com/microsoft/TypeScript/issues/17002 for // more details. const policyId = policyIds satisfies - | string - | readonly string[] as string; + string | readonly string[] as string; setSelectedPrimaryPolicies(policiesFromIds([policyId])); setSelectedPrimaryActions( selectedPrimaryActions.map((action) => ({ @@ -1458,8 +1480,7 @@ function ManualReviewJobReviewImpl(props: { { // Expand the table; collapsed by default. screen.getByRole('button', { name: /show/i }).click(); const buttons = screen.getAllByRole('button', { - name: /invalidate reports/i, + name: /invalidate all reports/i, }); expect(buttons).toHaveLength(2); }); @@ -99,7 +99,7 @@ describe('MergedReportsComponent invalidation actions', () => { renderMerged(false); screen.getByRole('button', { name: /show/i }).click(); expect( - screen.queryByRole('button', { name: /invalidate reports/i }), + screen.queryByRole('button', { name: /invalidate all reports/i }), ).not.toBeInTheDocument(); }); }); diff --git a/client/src/webpages/dashboard/mrt/manual_review_job/ReportInfoComponent.tsx b/client/src/webpages/dashboard/mrt/manual_review_job/ReportInfoComponent.tsx index 8dd2187cc..8de6fabc3 100644 --- a/client/src/webpages/dashboard/mrt/manual_review_job/ReportInfoComponent.tsx +++ b/client/src/webpages/dashboard/mrt/manual_review_job/ReportInfoComponent.tsx @@ -13,7 +13,7 @@ import { userHasPermissions } from '@/routing/permissions'; import { filterNullOrUndefined } from '@/utils/collections'; import { getFieldValueForRole } from '@/utils/itemUtils'; import { selectPreferredUserItem } from '@/utils/manualReviewTool'; -import { format } from 'date-fns'; +import { safeFormat } from '@/utils/time'; import { ExternalLink } from 'lucide-react'; import { useCallback } from 'react'; import { Link } from 'react-router-dom'; @@ -153,7 +153,7 @@ export default function ReportInfoComponent(props: { {isAppeal ? 'Appeal ' : 'Report '}Received - {format(new Date(createdAt as string), 'MM/dd/yy hh:mm a')} + {safeFormat(createdAt, 'MM/dd/yy hh:mm a')} diff --git a/client/src/webpages/dashboard/mrt/manual_review_job/v2/ContentRelatedItemComponent.tsx b/client/src/webpages/dashboard/mrt/manual_review_job/v2/ContentRelatedItemComponent.tsx index a767cd08e..9dd237aec 100644 --- a/client/src/webpages/dashboard/mrt/manual_review_job/v2/ContentRelatedItemComponent.tsx +++ b/client/src/webpages/dashboard/mrt/manual_review_job/v2/ContentRelatedItemComponent.tsx @@ -1,103 +1,18 @@ -import { useGQLGetRelatedItemsQuery } from '@/graphql/generated'; +import { GQLContentItem } from '@/graphql/generated'; import { ItemTypeFieldFieldData } from '@/webpages/dashboard/item_types/itemTypeUtils'; -import { gql } from '@apollo/client'; -import { ItemIdentifier } from '@roostorg/coop-types'; import FieldsComponent from './ManualReviewJobFieldsComponent'; -gql` - query getRelatedItems($itemIdentifiers: [ItemIdentifierInput!]!) { - latestItemSubmissions(itemIdentifiers: $itemIdentifiers) { - ... on UserItem { - id - submissionId - submissionTime - data - type { - id - name - baseFields { - name - type - required - container { - containerType - keyScalarType - valueScalarType - } - } - schemaFieldRoles { - displayName - createdAt - profileIcon - backgroundImage - } - } - } - ... on ContentItem { - id - submissionId - submissionTime - data - type { - id - name - baseFields { - name - type - required - container { - containerType - keyScalarType - valueScalarType - } - } - schemaFieldRoles { - displayName - } - } - } - ... on ThreadItem { - id - submissionId - submissionTime - data - type { - id - name - baseFields { - name - type - required - container { - containerType - keyScalarType - valueScalarType - } - } - schemaFieldRoles { - displayName - } - } - } - } - } -`; +type LoadedContentItem = Pick & { + type: Pick; +}; + export default function ContentRelatedItemComponent(props: { - relatedItem: ItemIdentifier; + item: LoadedContentItem; unblurAllMedia: boolean; title: string; }) { - const { relatedItem, unblurAllMedia } = props; - const { data, error } = useGQLGetRelatedItemsQuery({ - variables: { - itemIdentifiers: [relatedItem], - }, - }); - if (!data || error) { - return null; - } - const item = data.latestItemSubmissions[0]; + const { item, unblurAllMedia } = props; const fieldData = item.type.baseFields.map( ( diff --git a/client/src/webpages/dashboard/mrt/manual_review_job/v2/ManualReviewJobCommentSection.tsx b/client/src/webpages/dashboard/mrt/manual_review_job/v2/ManualReviewJobCommentSection.tsx index 1a5fc4773..47417ab93 100644 --- a/client/src/webpages/dashboard/mrt/manual_review_job/v2/ManualReviewJobCommentSection.tsx +++ b/client/src/webpages/dashboard/mrt/manual_review_job/v2/ManualReviewJobCommentSection.tsx @@ -1,3 +1,4 @@ +import { safeFormatDistanceToNow } from '@/utils/time'; import { CommentOutlined, DeleteOutlined, @@ -7,7 +8,6 @@ import { } from '@ant-design/icons'; import { gql } from '@apollo/client'; import { Button, Input } from 'antd'; -import { formatDistanceToNow } from 'date-fns'; import { useEffect, useRef, useState } from 'react'; import ComponentLoading from '../../../../../components/common/ComponentLoading'; @@ -99,9 +99,7 @@ function ManualReviewJobComment(props: { isBeingDeleted ? 'text-gray-300' : 'text-gray-500' }`} > - {formatDistanceToNow(new Date(comment.createdAt as string), { - addSuffix: true, - })} + {safeFormatDistanceToNow(comment.createdAt)}
+
{/* Split the data into two columns: non-media fields and media fields*/}
diff --git a/client/src/webpages/dashboard/mrt/manual_review_job/v2/ManualReviewJobFieldsComponent.tsx b/client/src/webpages/dashboard/mrt/manual_review_job/v2/ManualReviewJobFieldsComponent.tsx index 3cf58a53f..8ac9c4db5 100644 --- a/client/src/webpages/dashboard/mrt/manual_review_job/v2/ManualReviewJobFieldsComponent.tsx +++ b/client/src/webpages/dashboard/mrt/manual_review_job/v2/ManualReviewJobFieldsComponent.tsx @@ -14,6 +14,7 @@ import ReactAudioPlayer from 'react-audio-player'; import { Link } from 'react-router-dom'; import ComponentLoading from '../../../../../components/common/ComponentLoading'; +import CollapsibleText from '@/webpages/dashboard/mrt/manual_review_job/v2/components/CollapsibleText'; import { GQLContentItem, @@ -90,6 +91,85 @@ type FieldsComponentOptions = { transparentBackground?: boolean; }; +gql` + query getRelatedItems($itemIdentifiers: [ItemIdentifierInput!]!) { + latestItemSubmissions(itemIdentifiers: $itemIdentifiers) { + ... on UserItem { + id + submissionId + submissionTime + data + type { + id + name + baseFields { + name + type + required + container { + containerType + keyScalarType + valueScalarType + } + } + schemaFieldRoles { + displayName + createdAt + profileIcon + backgroundImage + } + } + } + ... on ContentItem { + id + submissionId + submissionTime + data + type { + id + name + baseFields { + name + type + required + container { + containerType + keyScalarType + valueScalarType + } + } + schemaFieldRoles { + displayName + } + } + } + ... on ThreadItem { + id + submissionId + submissionTime + data + type { + id + name + baseFields { + name + type + required + container { + containerType + keyScalarType + valueScalarType + } + } + schemaFieldRoles { + displayName + } + } + } + } + } +`; + gql` query ItemTypeHiddenFields { myOrg { @@ -190,12 +270,23 @@ function TableRowComponent(props: {
); } + case 'STRING': { + return ( +
+ {label ? ( +
+ {label} +
+ ) : null} + +
+ ); + } case 'BOOLEAN': case 'GEOHASH': case 'ID': case 'NUMBER': case 'POLICY_ID': - case 'STRING': case 'EMAIL_ADDRESS': { // EMAIL_ADDRESS renders as plain text for now; a follow-up could make // it a mailto/pivot link the way IP_ADDRESS pivots on the IP. @@ -278,6 +369,7 @@ function TableRowComponent(props: { ? (safetySettings.moderatorSafetyBlurLevel as BlurStrength) : (2 as const), grayscale: safetySettings?.moderatorSafetyGrayscale ?? false, + sepia: safetySettings?.moderatorSafetySepia ?? false, }} /> {label ?
{label}
: null} @@ -352,6 +444,7 @@ function TableRowComponent(props: { ? (safetySettings.moderatorSafetyBlurLevel as BlurStrength) : (2 as const), grayscale: safetySettings?.moderatorSafetyGrayscale ?? false, + sepia: safetySettings?.moderatorSafetySepia ?? false, }} /> {label ?
{label}
: null} @@ -526,7 +619,7 @@ function FieldComponent(props: { case 'EMAIL_ADDRESS': case 'DATETIME': return ( -
+
{!hideLabels ? (
@@ -644,7 +737,7 @@ function ContainerComponent(props: { type: data.container!.valueScalarType, }; return ( -
+
{/*Talk to ethan about how to avoid casting here*/} ) : null}
diff --git a/client/src/webpages/dashboard/mrt/manual_review_job/v2/components/CollapsibleText.test.tsx b/client/src/webpages/dashboard/mrt/manual_review_job/v2/components/CollapsibleText.test.tsx new file mode 100644 index 000000000..94a54d24c --- /dev/null +++ b/client/src/webpages/dashboard/mrt/manual_review_job/v2/components/CollapsibleText.test.tsx @@ -0,0 +1,114 @@ +import { fireEvent, render, screen } from '@testing-library/react'; +import React from 'react'; + +import '@testing-library/jest-dom/extend-expect'; + +import CollapsibleText from '@/webpages/dashboard/mrt/manual_review_job/v2/components/CollapsibleText'; + +describe('CollapsibleText', () => { + it('renders short text in full without a Read more button', () => { + render(); + expect(screen.getByText('hello world')).toBeInTheDocument(); + expect(screen.queryByRole('button')).not.toBeInTheDocument(); + }); + + it('collapses text exceeding maxGraphemes and shows Read more', () => { + const longText = 'a'.repeat(2001); + render(); + // The full text is in the DOM (CSS line-clamp hides overflow visually, not in the DOM). + expect(screen.getByText(longText)).toBeInTheDocument(); + expect( + screen.getByRole('button', { name: /read more/i }), + ).toBeInTheDocument(); + }); + + it('expands to full text and toggles to Read less on click', () => { + const longText = 'a'.repeat(2001); + render(); + const moreButton = screen.getByRole('button', { name: /read more/i }); + fireEvent.click(moreButton); + expect( + screen.getByRole('button', { name: /read less/i }), + ).toBeInTheDocument(); + // Full text is now rendered (not clamped). + expect(screen.getByText(longText)).toBeInTheDocument(); + }); + + it('collapses again on Read less click', () => { + const longText = 'a'.repeat(2001); + render(); + fireEvent.click(screen.getByRole('button', { name: /read more/i })); + fireEvent.click(screen.getByRole('button', { name: /read less/i })); + expect( + screen.getByRole('button', { name: /read more/i }), + ).toBeInTheDocument(); + }); + + it('counts graphemes, not UTF-16 code units (emoji with skin tone)', () => { + // 👨🏿 is a single grapheme but 4 UTF-16 code units. A naive `.length` + // check would collapse at 501 such graphemes (length 2004 > 2000), but + // a correct grapheme count (501) stays under the threshold → no collapse. + const grapheme = '👨🏿'; + const underThresholdByGrapheme = grapheme.repeat(501); + expect(underThresholdByGrapheme.length).toBeGreaterThan(2000); + render(); + expect(screen.queryByRole('button')).not.toBeInTheDocument(); + + // 2001 graphemes (8016 UTF-16 code units) does exceed the grapheme + // threshold → collapses. + const overThresholdByGrapheme = grapheme.repeat(2001); + render(); + expect( + screen.getByRole('button', { name: /read more/i }), + ).toBeInTheDocument(); + }); + + it('respects a custom maxGraphemes threshold', () => { + // 11 chars, under default maxGraphemes (2000) but over custom maxGraphemes (10). + render(); + expect( + screen.getByRole('button', { name: /read more/i }), + ).toBeInTheDocument(); + }); + + it('respects a custom maxLines threshold for wrapping text', () => { + const wrappingText = 'wrap '.repeat(20).trim(); + const { container, rerender } = render( +
+ +
, + ); + const clampedDiv = container.querySelector( + 'div[style*="-webkit-box"]', + ) as HTMLElement; + expect(clampedDiv.style.webkitLineClamp).toBe('2'); + + rerender( +
+ +
, + ); + expect(clampedDiv.style.webkitLineClamp).toBe('3'); + }); + + it('does not collapse text at exactly maxGraphemes', () => { + render(); + expect(screen.queryByRole('button')).not.toBeInTheDocument(); + }); + + it('resets to collapsed when the text prop changes', () => { + // Simulates navigating between review jobs that reuse the same field name + // (React reuses the CollapsibleText instance). Expanding one long value, + //then rendering a different long value, must restore the collapsed state. + const { rerender } = render(); + fireEvent.click(screen.getByRole('button', { name: /read more/i })); + expect( + screen.getByRole('button', { name: /read less/i }), + ).toBeInTheDocument(); + + rerender(); + expect( + screen.getByRole('button', { name: /read more/i }), + ).toBeInTheDocument(); + }); +}); diff --git a/client/src/webpages/dashboard/mrt/manual_review_job/v2/components/CollapsibleText.tsx b/client/src/webpages/dashboard/mrt/manual_review_job/v2/components/CollapsibleText.tsx new file mode 100644 index 000000000..404652877 --- /dev/null +++ b/client/src/webpages/dashboard/mrt/manual_review_job/v2/components/CollapsibleText.tsx @@ -0,0 +1,89 @@ +import { memo, useEffect, useMemo, useState } from 'react'; + +type CollapsibleTextProps = { + text: string; + maxLines?: number; + maxGraphemes?: number; +}; + +/** + * Count graphemes using Intl.Segmenter (handles all scripts correctly), + * stopping early once the count is known to exceed `threshold`. This avoids + * segmenting an entire pathological paste (e.g. a 1MB string) when we only + * need to know whether it crosses the collapse threshold. + */ +function exceedsGraphemeThreshold(text: string, threshold: number): boolean { + const segmenter = new Intl.Segmenter(undefined, { granularity: 'grapheme' }); + let count = 0; + for (const _ of segmenter.segment(text)) { + count += 1; + if (count > threshold) { + return true; + } + } + return false; +} + +/** + * Renders text with wrapping. When the text exceeds `maxGraphemes`, it is + * collapsed to `maxLines` visible lines with a "Read more" / "Read less" + * toggle. Short text renders in full with no toggle. + * + * Memoized: props are primitives (string, number), so React.memo's shallow + * comparison prevents re-renders (and re-segmentation) when a parent re-renders + * but the text hasn't changed — important for thread histories with many copies + * of the same message. + */ +function CollapsibleTextImpl({ + text, + maxLines = 12, + maxGraphemes = 2000, +}: CollapsibleTextProps) { + const isCollapsible = useMemo( + () => exceedsGraphemeThreshold(text, maxGraphemes), + [text, maxGraphemes], + ); + const [expanded, setExpanded] = useState(false); + + // When the text prop changes (e.g. navigating between review jobs that reuse + // the same field name → React reuses this component instance), reset to the + // collapsed state so newly loaded long content doesn't inherit the previous + // job's expanded view. + useEffect(() => { + setExpanded(false); + }, [text]); + + if (!isCollapsible) { + return ( +
{text}
+ ); + } + + return ( +
+
+ {text} +
+ +
+ ); +} + +const CollapsibleText = memo(CollapsibleTextImpl); +export default CollapsibleText; diff --git a/client/src/webpages/dashboard/mrt/manual_review_job/v2/ncmec/NCMECMediaViewer.tsx b/client/src/webpages/dashboard/mrt/manual_review_job/v2/ncmec/NCMECMediaViewer.tsx index 5ea0d96df..c24b7b252 100644 --- a/client/src/webpages/dashboard/mrt/manual_review_job/v2/ncmec/NCMECMediaViewer.tsx +++ b/client/src/webpages/dashboard/mrt/manual_review_job/v2/ncmec/NCMECMediaViewer.tsx @@ -1,6 +1,19 @@ import { Label } from '@/coop-ui/Label'; +import { + Select, + SelectContent, + SelectItem, + SelectTrigger, + SelectValue, +} from '@/coop-ui/Select'; import { Slider } from '@/coop-ui/Slider'; -import { Switch } from '@/coop-ui/Switch'; +import { + colorSchemeFromPreferences, + MODERATOR_SAFETY_COLOR_SCHEME_LABELS, + MODERATOR_SAFETY_COLOR_SCHEMES, + preferencesFromColorScheme, + type ModeratorSafetyColorScheme, +} from '@/models/safetySettings'; import { SearchOutlined } from '@ant-design/icons'; import { useEffect, useState } from 'react'; @@ -84,10 +97,12 @@ export default function NCMECMediaViewer(props: { moderatorSafetyBlurLevel: BlurStrength; moderatorSafetyGrayscale: boolean; moderatorSafetyMuteVideo: boolean; + moderatorSafetySepia: boolean; }>({ moderatorSafetyBlurLevel: 2, moderatorSafetyGrayscale: true, moderatorSafetyMuteVideo: true, + moderatorSafetySepia: false, }); const { loading, error, data } = useGQLPersonalSafetySettingsQuery(); @@ -170,7 +185,7 @@ export default function NCMECMediaViewer(props: { shouldBlur ? BLUR_LEVELS[safetySettings.moderatorSafetyBlurLevel] : 0 - } ${safetySettings.moderatorSafetyGrayscale ? 'grayscale' : ''}`} + } ${safetySettings.moderatorSafetyGrayscale ? 'grayscale' : ''} ${safetySettings.moderatorSafetySepia ? 'sepia' : ''}`} alt="" src={mediaId.urlInfo.url} onError={(img) => { @@ -190,7 +205,7 @@ export default function NCMECMediaViewer(props: { isInInspectedView ? 'w-auto' : 'object-scale-down grow max-w-64 max-h-48' - } ${safetySettings.moderatorSafetyGrayscale ? 'grayscale' : ''}`} + } ${safetySettings.moderatorSafetyGrayscale ? 'grayscale' : ''} ${safetySettings.moderatorSafetySepia ? 'sepia' : ''}`} url={mediaId.urlInfo.url} options={{ shouldBlur: @@ -254,18 +269,29 @@ export default function NCMECMediaViewer(props: { />
- + +
)} diff --git a/client/src/webpages/dashboard/mrt/manual_review_job/v2/ncmec/NCMECReviewUser.tsx b/client/src/webpages/dashboard/mrt/manual_review_job/v2/ncmec/NCMECReviewUser.tsx index 7300d3183..154c903f6 100644 --- a/client/src/webpages/dashboard/mrt/manual_review_job/v2/ncmec/NCMECReviewUser.tsx +++ b/client/src/webpages/dashboard/mrt/manual_review_job/v2/ncmec/NCMECReviewUser.tsx @@ -3,6 +3,7 @@ import { BulbOutlined, ExclamationCircleOutlined } from '@ant-design/icons'; import { gql } from '@apollo/client'; import { ItemIdentifier, MediaKind, TaggedScalar } from '@roostorg/coop-types'; import { Button } from 'antd'; +import clsx from 'clsx'; import pick from 'lodash/pick'; import uniqBy from 'lodash/uniqBy'; import uniqWith from 'lodash/uniqWith'; @@ -446,9 +447,10 @@ export default function NCMECReviewUser( } const { - moderatorSafetyBlurLevel, - moderatorSafetyGrayscale, - moderatorSafetyMuteVideo, + moderatorSafetyBlurLevel = 2, + moderatorSafetyGrayscale = true, + moderatorSafetyMuteVideo = true, + moderatorSafetySepia = false, } = data?.me?.interfacePreferences ?? {}; // Compares two pieces of media to determine whether they're the same, based @@ -634,23 +636,29 @@ export default function NCMECReviewUser(
{!loading && moderatorSafetyBlurLevel != null && - moderatorSafetyGrayscale != null ? ( + moderatorSafetyGrayscale != null && + moderatorSafetySepia != null ? ( media.urlInfo.mediaType === 'IMAGE' ? ( ) : ( - it.contentItem.id === mediaInDetailView.itemId && - it.urlInfo.url === mediaInDetailView.urlInfo.url, - )! - } + fullNcmecContentItem={allMediaItemsWithUrls.find( + (it) => + it.contentItem.id === mediaInDetailView.itemId && + it.urlInfo.url === mediaInDetailView.urlInfo.url, + )!} state={selectedMedia.find((it) => areMediaEqual(it, mediaInDetailView), )} diff --git a/client/src/webpages/dashboard/mrt/manual_review_job/v2/user/ManualReviewJobLatestSubmissionsWithThreadComponent.tsx b/client/src/webpages/dashboard/mrt/manual_review_job/v2/user/ManualReviewJobLatestSubmissionsWithThreadComponent.tsx index e16f93906..e22313611 100644 --- a/client/src/webpages/dashboard/mrt/manual_review_job/v2/user/ManualReviewJobLatestSubmissionsWithThreadComponent.tsx +++ b/client/src/webpages/dashboard/mrt/manual_review_job/v2/user/ManualReviewJobLatestSubmissionsWithThreadComponent.tsx @@ -255,9 +255,12 @@ export default function ManualReviewJobLatestSubmissionsWithThreadComponent(prop } return ( - | undefined; + Array | undefined; metric: ManualReviewDashboardInsightsChartMetric; title?: string; isCustomTitle?: boolean; diff --git a/client/src/webpages/dashboard/rules/rule_form/ReportingRuleForm.tsx b/client/src/webpages/dashboard/rules/rule_form/ReportingRuleForm.tsx index 9e0cd6322..858e0709e 100644 --- a/client/src/webpages/dashboard/rules/rule_form/ReportingRuleForm.tsx +++ b/client/src/webpages/dashboard/rules/rule_form/ReportingRuleForm.tsx @@ -476,8 +476,8 @@ export default function RuleForm() { dispatch({ type: ReportingRuleFormReducerActionType.UpdateItemTypes, payload: { - selectedItemTypes: selectedTypeIDs.map( - (id) => allItemTypes.find((itemType) => itemType.id === id)!, + selectedItemTypes: selectedTypeIDs.map((id) => + allItemTypes.find((itemType) => itemType.id === id)!, ), allActions, allSignals: allSignals satisfies readonly GQLSignal[], diff --git a/client/src/webpages/dashboard/rules/rule_form/RuleForm.tsx b/client/src/webpages/dashboard/rules/rule_form/RuleForm.tsx index c9ad7f6c5..d752b3465 100644 --- a/client/src/webpages/dashboard/rules/rule_form/RuleForm.tsx +++ b/client/src/webpages/dashboard/rules/rule_form/RuleForm.tsx @@ -685,7 +685,7 @@ export default function RuleForm() { const out: Record> = {}; for (const action of rule?.actions ?? []) { if ('configuredParameters' in action && action.configuredParameters) { - out[action.id] = action.configuredParameters as Record; + out[action.id] = action.configuredParameters; } } return out; @@ -823,9 +823,7 @@ export default function RuleForm() { const showUpdateRuleCaughtErrorModal = ( isUpdate: boolean, errorName: - | 'NotFoundError' - | 'RuleNameExistsError' - | 'RuleHasRunningBacktestsError', + 'NotFoundError' | 'RuleNameExistsError' | 'RuleHasRunningBacktestsError', ) => { dispatch({ type: RuleFormReducerActionType.ShowModal, @@ -1113,8 +1111,8 @@ export default function RuleForm() { dispatch({ type: RuleFormReducerActionType.UpdateItemTypes, payload: { - selectedItemTypes: selectedTypeIDs.map( - (id: string) => allItemTypes.find((itemType) => itemType.id === id)!, + selectedItemTypes: selectedTypeIDs.map((id: string) => + allItemTypes.find((itemType) => itemType.id === id)!, ), allActions, allSignals: allSignals satisfies readonly GQLSignal[], diff --git a/client/src/webpages/dashboard/rules/rule_form/RuleFormReducers.test.ts b/client/src/webpages/dashboard/rules/rule_form/RuleFormReducers.test.ts index e38331d80..a9f9b7520 100644 --- a/client/src/webpages/dashboard/rules/rule_form/RuleFormReducers.test.ts +++ b/client/src/webpages/dashboard/rules/rule_form/RuleFormReducers.test.ts @@ -7,7 +7,7 @@ import { GQLSignalPricingStructureType, GQLSignalType, } from '../../../../graphql/generated'; -import { RuleFormConditionSet, RuleFormLeafCondition } from '../types'; +import { RuleFormLeafCondition } from '../types'; import { initialState, RuleFormState } from './RuleForm'; import { RuleFormReducerActionType, updateInput } from './RuleFormReducers'; import { @@ -86,7 +86,7 @@ describe('updateInput signal eligibility', () => { conditions: [ { input: oldInput, signal: custom1, eligibleSignals: [custom1] }, ], - } as RuleFormConditionSet, + }, }; const result = updateInput(state, { @@ -117,7 +117,7 @@ describe('updateInput signal eligibility', () => { conditions: [ { input: oldInput, signal: custom2, eligibleSignals: [custom2] }, ], - } as RuleFormConditionSet, + }, }; const result = updateInput(state, { diff --git a/client/src/webpages/dashboard/rules/types.ts b/client/src/webpages/dashboard/rules/types.ts index bc230904c..5e0562866 100644 --- a/client/src/webpages/dashboard/rules/types.ts +++ b/client/src/webpages/dashboard/rules/types.ts @@ -59,8 +59,7 @@ export function getMatchingValuesType(matchingValues: GQLMatchingValues) { } export type ConditionWithResult = - | LeafConditionWithResult - | ConditionSetWithResult; + LeafConditionWithResult | ConditionSetWithResult; export type ConditionSetWithResult = { conditions: [ConditionWithResult, ...ConditionWithResult[]]; diff --git a/client/src/webpages/settings/AccountSettings.tsx b/client/src/webpages/settings/AccountSettings.tsx index 11434db7e..71f16d892 100644 --- a/client/src/webpages/settings/AccountSettings.tsx +++ b/client/src/webpages/settings/AccountSettings.tsx @@ -10,6 +10,13 @@ import { } from '@/coop-ui/Dialog'; import { Input } from '@/coop-ui/Input'; import { Label } from '@/coop-ui/Label'; +import { + Select, + SelectContent, + SelectItem, + SelectTrigger, + SelectValue, +} from '@/coop-ui/Select'; import { Slider } from '@/coop-ui/Slider'; import { Switch } from '@/coop-ui/Switch'; import { toast } from '@/coop-ui/Toast'; @@ -31,6 +38,14 @@ import { useGQLUpdateAccountInfoMutation, } from '../../graphql/generated'; import GoldenRetrieverPuppies from '../../images/GoldenRetrieverPuppies.png'; +import { + colorSchemeClassName, + colorSchemeFromPreferences, + MODERATOR_SAFETY_COLOR_SCHEME_LABELS, + MODERATOR_SAFETY_COLOR_SCHEMES, + preferencesFromColorScheme, + type ModeratorSafetyColorScheme, +} from '../../models/safetySettings'; import { BLUR_LEVELS, type BlurStrength, @@ -60,6 +75,7 @@ gql` moderatorSafetyMuteVideo moderatorSafetyGrayscale moderatorSafetyBlurLevel + moderatorSafetySepia } } } @@ -141,6 +157,7 @@ type SafetySettings = { moderatorSafetyBlurLevel: BlurStrength; moderatorSafetyGrayscale: boolean; moderatorSafetyMuteVideo: boolean; + moderatorSafetySepia: boolean; }; export default function AccountSettings() { @@ -149,6 +166,7 @@ export default function AccountSettings() { moderatorSafetyBlurLevel: 2, moderatorSafetyGrayscale: true, moderatorSafetyMuteVideo: true, + moderatorSafetySepia: false, }); const [dialogConfig, setDialogConfig] = useState({ @@ -228,12 +246,14 @@ export default function AccountSettings() { moderatorSafetyMuteVideo, moderatorSafetyGrayscale, moderatorSafetyBlurLevel, + moderatorSafetySepia, } = safetySettingsData.me.interfacePreferences; setSafetySettings({ moderatorSafetyMuteVideo, moderatorSafetyGrayscale, moderatorSafetyBlurLevel: moderatorSafetyBlurLevel as BlurStrength, + moderatorSafetySepia, }); }, [safetySettingsData?.me?.interfacePreferences]); @@ -298,11 +318,20 @@ export default function AccountSettings() { })); }, []); - const setGrayscalePreference = useCallback( - (moderatorSafetyGrayscale: boolean): void => + const setMuteVideoPreference = useCallback( + (moderatorSafetyMuteVideo: boolean): void => + setSafetySettings((prevSettings) => ({ + ...prevSettings, + moderatorSafetyMuteVideo, + })), + [], + ); + + const setColorSchemePreference = useCallback( + (colorScheme: ModeratorSafetyColorScheme): void => setSafetySettings((prevSettings) => ({ ...prevSettings, - moderatorSafetyGrayscale, + ...preferencesFromColorScheme(colorScheme), })), [], ); @@ -366,15 +395,6 @@ export default function AccountSettings() { }); }, [currentPassword, newPassword, confirmNewPassword, changePassword]); - const setMuteVideoPreference = useCallback( - (moderatorSafetyMuteVideo: boolean): void => - setSafetySettings((prevSettings) => ({ - ...prevSettings, - moderatorSafetyMuteVideo, - })), - [], - ); - const moderatorSafetyBlurValue = useMemo( () => [safetySettings.moderatorSafetyBlurLevel], [safetySettings.moderatorSafetyBlurLevel], @@ -589,11 +609,24 @@ export default function AccountSettings() { />
- - + +
@@ -608,7 +641,7 @@ export default function AccountSettings() { puppies diff --git a/client/src/webpages/settings/SettingsPage.test.tsx b/client/src/webpages/settings/SettingsPage.test.tsx index e21ac4628..b0796e053 100644 --- a/client/src/webpages/settings/SettingsPage.test.tsx +++ b/client/src/webpages/settings/SettingsPage.test.tsx @@ -128,6 +128,7 @@ const wellnessSettingsMock: MockedResponse = { moderatorSafetyBlurLevel: 2, moderatorSafetyGrayscale: true, moderatorSafetyMuteVideo: true, + moderatorSafetySepia: false, }, }, }, @@ -562,12 +563,14 @@ describe('SettingsPage', () => { screen.getByText('Default Wellness Settings'), ).toBeInTheDocument(); expect(screen.getByText('Blur Media')).toBeInTheDocument(); - expect(screen.getByText('Greyscale')).toBeInTheDocument(); + expect(screen.getByText('Color Scheme')).toBeInTheDocument(); expect(screen.getByText('Mute videos')).toBeInTheDocument(); }); + // Grayscale=true / sepia=false in the mock maps to the GRAYSCALE scheme. + expect(screen.getByRole('combobox')).toHaveTextContent('Grayscale'); const switches = screen.getAllByRole('switch'); + expect(switches).toHaveLength(1); expect(switches[0]).toHaveAttribute('aria-checked', 'true'); - expect(switches[1]).toHaveAttribute('aria-checked', 'true'); }); it('calls save mutation with updated settings', async () => { @@ -584,8 +587,9 @@ describe('SettingsPage', () => { variables: { orgDefaultSafetySettings: { moderatorSafetyBlurLevel: 2, - moderatorSafetyGrayscale: false, - moderatorSafetyMuteVideo: true, + moderatorSafetyGrayscale: true, + moderatorSafetyMuteVideo: false, + moderatorSafetySepia: false, }, }, }, @@ -595,7 +599,7 @@ describe('SettingsPage', () => { 'wellness', ); await waitFor(() => { - expect(screen.getByText('Greyscale')).toBeInTheDocument(); + expect(screen.getByText('Mute videos')).toBeInTheDocument(); }); userEvent.click(screen.getAllByRole('switch')[0]); diff --git a/client/src/webpages/settings/SettingsPage.tsx b/client/src/webpages/settings/SettingsPage.tsx index 4a2b956d4..318ee6c03 100644 --- a/client/src/webpages/settings/SettingsPage.tsx +++ b/client/src/webpages/settings/SettingsPage.tsx @@ -79,12 +79,7 @@ gql` `; type Tab = - | 'organization' - | 'sso' - | 'appeals' - | 'review-console' - | 'wellness' - | 'other'; + 'organization' | 'sso' | 'appeals' | 'review-console' | 'wellness' | 'other'; const TABS: { value: Tab; label: string; icon: React.ReactNode }[] = [ { diff --git a/client/src/webpages/settings/tabs/WellnessTab.tsx b/client/src/webpages/settings/tabs/WellnessTab.tsx index fe4c6504c..dc738f5f5 100644 --- a/client/src/webpages/settings/tabs/WellnessTab.tsx +++ b/client/src/webpages/settings/tabs/WellnessTab.tsx @@ -1,13 +1,29 @@ import { Button } from '@/coop-ui/Button'; +import { + Select, + SelectContent, + SelectItem, + SelectTrigger, + SelectValue, +} from '@/coop-ui/Select'; import { Slider } from '@/coop-ui/Slider'; import { Switch } from '@/coop-ui/Switch'; import { toast } from '@/coop-ui/Toast'; import { Heading, Text } from '@/coop-ui/Typography'; import { + namedOperations, useGQLOrgDefaultSafetySettingsQuery, useGQLSetOrgDefaultSafetySettingsMutation, } from '@/graphql/generated'; import GoldenRetrieverPuppies from '@/images/GoldenRetrieverPuppies.png'; +import { + colorSchemeClassName, + colorSchemeFromPreferences, + MODERATOR_SAFETY_COLOR_SCHEME_LABELS, + MODERATOR_SAFETY_COLOR_SCHEMES, + preferencesFromColorScheme, + type ModeratorSafetyColorScheme, +} from '@/models/safetySettings'; import { gql } from '@apollo/client'; import { useEffect, useState } from 'react'; @@ -28,6 +44,7 @@ gql` moderatorSafetyMuteVideo moderatorSafetyGrayscale moderatorSafetyBlurLevel + moderatorSafetySepia } } } @@ -47,6 +64,7 @@ type SafetySettings = { moderatorSafetyBlurLevel: BlurStrength; moderatorSafetyGrayscale: boolean; moderatorSafetyMuteVideo: boolean; + moderatorSafetySepia: boolean; }; export default function WellnessTab() { @@ -54,6 +72,7 @@ export default function WellnessTab() { moderatorSafetyBlurLevel: 2, moderatorSafetyGrayscale: true, moderatorSafetyMuteVideo: true, + moderatorSafetySepia: false, }); const { loading, error, data } = useGQLOrgDefaultSafetySettingsQuery({ @@ -64,6 +83,9 @@ export default function WellnessTab() { const [saveSafetySettings, { loading: isSaving }] = useGQLSetOrgDefaultSafetySettingsMutation({ + // The mutation returns no data, so refetch to update the cached + // baseline that hasChanges compares against. + refetchQueries: [namedOperations.Query.OrgDefaultSafetySettings], onCompleted: () => { toast.success('Default wellness settings saved!'); }, @@ -80,11 +102,13 @@ export default function WellnessTab() { moderatorSafetyMuteVideo, moderatorSafetyGrayscale, moderatorSafetyBlurLevel, + moderatorSafetySepia, } = defaultInterfacePreferences; setSafetySettings({ moderatorSafetyMuteVideo, moderatorSafetyGrayscale, moderatorSafetyBlurLevel: moderatorSafetyBlurLevel as BlurStrength, + moderatorSafetySepia, }); }, [defaultInterfacePreferences]); @@ -99,7 +123,8 @@ export default function WellnessTab() { safetySettings.moderatorSafetyGrayscale !== serverPrefs.moderatorSafetyGrayscale || safetySettings.moderatorSafetyMuteVideo !== - serverPrefs.moderatorSafetyMuteVideo; + serverPrefs.moderatorSafetyMuteVideo || + safetySettings.moderatorSafetySepia !== serverPrefs.moderatorSafetySepia; return (
@@ -139,17 +164,30 @@ export default function WellnessTab() {
- Greyscale + Color Scheme - +
@@ -169,7 +207,7 @@ export default function WellnessTab() { puppies diff --git a/client/tsconfig.json b/client/tsconfig.json index 823c15c10..fb0490dbb 100644 --- a/client/tsconfig.json +++ b/client/tsconfig.json @@ -21,5 +21,12 @@ "@/*": ["./src/*"] } }, - "include": ["src"] + "include": ["src"], + "exclude": [ + "src/**/*.test.ts", + "src/**/*.test.tsx", + "src/**/*.spec.ts", + "src/**/*.spec.tsx", + "src/setupTests.ts" + ] } diff --git a/client/tsconfig.test.json b/client/tsconfig.test.json new file mode 100644 index 000000000..73456acc9 --- /dev/null +++ b/client/tsconfig.test.json @@ -0,0 +1,19 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "types": [ + "vite/client", + "vite-plugin-svgr/client", + "vitest/globals", + "node" + ] + }, + "include": [ + "src/**/*.test.ts", + "src/**/*.test.tsx", + "src/**/*.spec.ts", + "src/**/*.spec.tsx", + "src/setupTests.ts" + ], + "exclude": ["node_modules"] +} diff --git a/client/vite.config.ts b/client/vite.config.ts index 2e29ca89e..ba47330cc 100644 --- a/client/vite.config.ts +++ b/client/vite.config.ts @@ -34,5 +34,8 @@ export default defineConfig({ globals: true, environment: 'jsdom', setupFiles: './src/setupTests.ts', + typecheck: { + tsconfig: './tsconfig.test.json', + }, }, }); diff --git a/db/Dockerfile b/db/Dockerfile index 15ffa9d50..d43d7ddc8 100644 --- a/db/Dockerfile +++ b/db/Dockerfile @@ -1,4 +1,4 @@ -FROM node:24.14.1-bullseye-slim AS builder +FROM node:24.18.0-bullseye-slim AS builder WORKDIR /app @@ -13,7 +13,7 @@ RUN npm run build RUN npm prune --omit=dev -FROM node:24.14.1-bullseye-slim AS base +FROM node:24.18.0-bullseye-slim AS base WORKDIR /app diff --git a/db/README.md b/db/README.md index 48b0ca162..665fc46e2 100644 --- a/db/README.md +++ b/db/README.md @@ -6,7 +6,7 @@ A Node.js CLI for running database migrations and seeds. ``` npm install -node --loader ts-node/esm --require dotenv/config index.ts +node --env-file-if-exists=.env --loader ts-node/esm index.ts ``` This displays available commands and arguments. diff --git a/db/knip.json b/db/knip.json index 55d171cf3..134f5d394 100644 --- a/db/knip.json +++ b/db/knip.json @@ -2,5 +2,5 @@ "$schema": "./node_modules/knip/schema.json", "entry": ["src/index.ts"], "project": ["src/**/*.ts"], - "ignoreDependencies": ["ts-node", "dotenv"] + "ignoreDependencies": ["ts-node"] } diff --git a/db/package-lock.json b/db/package-lock.json index 9b72268c9..1ce98f7e4 100644 --- a/db/package-lock.json +++ b/db/package-lock.json @@ -9,40 +9,30 @@ "version": "1.0.0", "license": "ISC", "dependencies": { - "@clickhouse/client": "^1.18.3", - "@roostorg/db-migrator": "^1.1.0", - "cassandra-driver": "^4.8.0", - "kysely": "^0.28.17", - "pg": "^8.7.1", + "@clickhouse/client": "^1.23.1", + "@roostorg/db-migrator": "^1.1.1", + "cassandra-driver": "^4.9.0", + "kysely": "^0.29.4", + "pg": "^8.22.0", "sequelize": "^6.37.8", "ts-node": "^10.9.2", - "umzug": "^3.0.0" + "umzug": "^3.8.3" }, "devDependencies": { "@types/pg": "^8.10.2", - "dotenv": "^17.4.2", - "knip": "^6.16.1", + "knip": "^6.26.0", "typescript": "^6.0.3" } }, "node_modules/@clickhouse/client": { - "version": "1.18.3", - "resolved": "https://registry.npmjs.org/@clickhouse/client/-/client-1.18.3.tgz", - "integrity": "sha512-340ngdYktL8PLUBK2QKSwe0o02tYfZSz1mSn1uXCEU8TxHvwh9pnQxElf9YHumDGj5gX/IdgxPsJTGMs82Hgug==", + "version": "1.23.1", + "resolved": "https://registry.npmjs.org/@clickhouse/client/-/client-1.23.1.tgz", + "integrity": "sha512-vs3/Zc1dHvT171btW5nMoPsPCJ6QVJ5pp7obxzO5sjqwFx/jjz9wwCAqcFOdc2DhprugDBaVn+4dVY8hG3A9nw==", "license": "Apache-2.0", - "dependencies": { - "@clickhouse/client-common": "1.18.3" - }, "engines": { - "node": ">=16" + "node": ">=20" } }, - "node_modules/@clickhouse/client-common": { - "version": "1.18.3", - "resolved": "https://registry.npmjs.org/@clickhouse/client-common/-/client-common-1.18.3.tgz", - "integrity": "sha512-3axzO3zvrsGT5PzDenxgWscltYCNRDbhaHWUgdsmcM9OnW/VnZn9EarOcZogr9P82Z0mQh+Jd2x+p2K4TFD2fA==", - "license": "Apache-2.0" - }, "node_modules/@cspotcode/source-map-support": { "version": "0.8.1", "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz", @@ -56,21 +46,21 @@ } }, "node_modules/@emnapi/core": { - "version": "1.10.0", - "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.10.0.tgz", - "integrity": "sha512-yq6OkJ4p82CAfPl0u9mQebQHKPJkY7WrIuk205cTYnYe+k2Z8YBh11FrbRG/H6ihirqcacOgl2BIO8oyMQLeXw==", + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.11.1.tgz", + "integrity": "sha512-RSvbQmHzdKzNsLYa/wHrbc3KN4sYLKAdPZxqiM2HATqv/SBk2/ENSHpvXGaLOMcsAyz0poEGqkmmKYG3OWiJEQ==", "dev": true, "license": "MIT", "optional": true, "dependencies": { - "@emnapi/wasi-threads": "1.2.1", + "@emnapi/wasi-threads": "1.2.2", "tslib": "^2.4.0" } }, "node_modules/@emnapi/runtime": { - "version": "1.10.0", - "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.10.0.tgz", - "integrity": "sha512-ewvYlk86xUoGI0zQRNq/mC+16R1QeDlKQy21Ki3oSYXNgLb45GV1P6A0M+/s6nyCuNDqe5VpaY84BzXGwVbwFA==", + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.11.1.tgz", + "integrity": "sha512-vgj7R3y3Wgx24IQaGPA/R6YFXLHVMOZ0uVEyIQPaWs+rd1AzfEMXlAC22FYwO1XkKR6NPsq7mUandH8oIRdZFw==", "dev": true, "license": "MIT", "optional": true, @@ -79,9 +69,9 @@ } }, "node_modules/@emnapi/wasi-threads": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.2.1.tgz", - "integrity": "sha512-uTII7OYF+/Mes/MrcIOYp5yOtSMLBWSIoLPpcgwipoiKbli6k322tcoFsxoIIxPDqW01SQGAgko4EzZi2BNv2w==", + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.2.2.tgz", + "integrity": "sha512-c95qOXkHdydNKhscBTebqEC1CVAZpyqOfVfBzQ1qgzyl3gfeldUjIggDbIZgDKsHLgnsM+igH7TJ/eAasaVuMA==", "dev": true, "license": "MIT", "optional": true, @@ -115,14 +105,14 @@ } }, "node_modules/@napi-rs/wasm-runtime": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-1.1.4.tgz", - "integrity": "sha512-3NQNNgA1YSlJb/kMH1ildASP9HW7/7kYnRI2szWJaofaS1hWmbGI4H+d3+22aGzXXN9IJ+n+GiFVcGipJP18ow==", + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-1.1.6.tgz", + "integrity": "sha512-ZLv/JdUfkvOy9eCnnBaGfiO+XimbjebAeO+MRQqD/B+FR1tnRN0tpKSJHRbE8sFfS6aqsXZ67TQjfwfsxULVbg==", "dev": true, "license": "MIT", "optional": true, "dependencies": { - "@tybys/wasm-util": "^0.10.1" + "@tybys/wasm-util": "^0.10.3" }, "funding": { "type": "github", @@ -133,45 +123,10 @@ "@emnapi/runtime": "^1.7.1" } }, - "node_modules/@nodelib/fs.scandir": { - "version": "2.1.5", - "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", - "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", - "license": "MIT", - "dependencies": { - "@nodelib/fs.stat": "2.0.5", - "run-parallel": "^1.1.9" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/@nodelib/fs.stat": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", - "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", - "license": "MIT", - "engines": { - "node": ">= 8" - } - }, - "node_modules/@nodelib/fs.walk": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", - "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", - "license": "MIT", - "dependencies": { - "@nodelib/fs.scandir": "2.1.5", - "fastq": "^1.6.0" - }, - "engines": { - "node": ">= 8" - } - }, "node_modules/@oxc-parser/binding-android-arm-eabi": { - "version": "0.133.0", - "resolved": "https://registry.npmjs.org/@oxc-parser/binding-android-arm-eabi/-/binding-android-arm-eabi-0.133.0.tgz", - "integrity": "sha512-l/44caGse+VpnY9gx0yvvc5QnnG3yG1FO3KZgYvNL1GZrfK86zIwAOgGEVlxDyRymzrU/KHiblPFpevKOmJmUA==", + "version": "0.137.0", + "resolved": "https://registry.npmjs.org/@oxc-parser/binding-android-arm-eabi/-/binding-android-arm-eabi-0.137.0.tgz", + "integrity": "sha512-KDs+0VPdEmasOkpuJHW9V5WCF+cvYdMQv2Jd+aJXt+cxIx12NToRQRbXaRwUEDsZw+/jMk81Ve8ZFbjUkJTOwA==", "cpu": [ "arm" ], @@ -186,9 +141,9 @@ } }, "node_modules/@oxc-parser/binding-android-arm64": { - "version": "0.133.0", - "resolved": "https://registry.npmjs.org/@oxc-parser/binding-android-arm64/-/binding-android-arm64-0.133.0.tgz", - "integrity": "sha512-KUHmPMziLBp4u+zbrLdB7iWS7KshuZe+RAp7ELnY9SI9nNXBZ+dp8fiBqWOxhXqn+FQg3a4UcQhwmsJOKV8Jjg==", + "version": "0.137.0", + "resolved": "https://registry.npmjs.org/@oxc-parser/binding-android-arm64/-/binding-android-arm64-0.137.0.tgz", + "integrity": "sha512-WhALNzfy3x/RfC6bsqX+csavuUY0yHHE7XfgPE5M542uhoBZUUoGTPG+nkMbGoG4+gcfss5s7urMyn5QBHu0sw==", "cpu": [ "arm64" ], @@ -203,9 +158,9 @@ } }, "node_modules/@oxc-parser/binding-darwin-arm64": { - "version": "0.133.0", - "resolved": "https://registry.npmjs.org/@oxc-parser/binding-darwin-arm64/-/binding-darwin-arm64-0.133.0.tgz", - "integrity": "sha512-q8dWmnU/8ea2tga9w2f1PinQ5rcMPDUGkF64T189b65YMjUomET4oy5oRldOr4AwOQkneOG/Zttnz1Dvrc62wg==", + "version": "0.137.0", + "resolved": "https://registry.npmjs.org/@oxc-parser/binding-darwin-arm64/-/binding-darwin-arm64-0.137.0.tgz", + "integrity": "sha512-bFPr5hgmNMOMoyPTGtdsK4Ug21RovIPojRMgDDhSp1LtCnc/DkLwGONKjgRjszg677RlGnkYSviQ8hHaUPOVYA==", "cpu": [ "arm64" ], @@ -220,9 +175,9 @@ } }, "node_modules/@oxc-parser/binding-darwin-x64": { - "version": "0.133.0", - "resolved": "https://registry.npmjs.org/@oxc-parser/binding-darwin-x64/-/binding-darwin-x64-0.133.0.tgz", - "integrity": "sha512-cOKeIELIB2bJnCKwqx4Rdj+1Lss/U6uCbLxRySZrhyOOQa1flKhwZFjEHRHxk8fU1NKmhK5OnTdPQ4CpjuFuVw==", + "version": "0.137.0", + "resolved": "https://registry.npmjs.org/@oxc-parser/binding-darwin-x64/-/binding-darwin-x64-0.137.0.tgz", + "integrity": "sha512-CL5dMm1asqXIDZHg14FLxj3Mc36w8PI7xCWh1uA4is6z8g2XrIILoTcQYOxDbwzuk34RDPX5IAGUxZr6LA9KAg==", "cpu": [ "x64" ], @@ -237,9 +192,9 @@ } }, "node_modules/@oxc-parser/binding-freebsd-x64": { - "version": "0.133.0", - "resolved": "https://registry.npmjs.org/@oxc-parser/binding-freebsd-x64/-/binding-freebsd-x64-0.133.0.tgz", - "integrity": "sha512-OpaSv4pW3KgFrMYQxTaS0aOE4T1DQF3qZE/4B6uqqv1KgPWWd4UQhJALi8PJPX1RRV5K7ThKXRfF7qGg2+3l1A==", + "version": "0.137.0", + "resolved": "https://registry.npmjs.org/@oxc-parser/binding-freebsd-x64/-/binding-freebsd-x64-0.137.0.tgz", + "integrity": "sha512-79h8rYGnSlKPGWo7mHr2ixO6ea7aW8B0CT965SZ8SLbNnCOH5aOYBTeVXUY6eMvEaiLyWr8Skuiugr5pDYgLGw==", "cpu": [ "x64" ], @@ -254,9 +209,9 @@ } }, "node_modules/@oxc-parser/binding-linux-arm-gnueabihf": { - "version": "0.133.0", - "resolved": "https://registry.npmjs.org/@oxc-parser/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-0.133.0.tgz", - "integrity": "sha512-JGK1wlGrGwxBIlVSF7KWTX1/ru6BEtf28fRROztDRkLfiW+Kxa4onnriezMIiogfn9hVw2KzYcKiLjkLR2ns8A==", + "version": "0.137.0", + "resolved": "https://registry.npmjs.org/@oxc-parser/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-0.137.0.tgz", + "integrity": "sha512-ASgmlSimhGyr0lksgVIo6hibz1obnDq4qJbiMX/AzltfgPnanRrzG1Q+23g8ljOHOjv6dsznkUuCYL3gg0sY1Q==", "cpu": [ "arm" ], @@ -271,9 +226,9 @@ } }, "node_modules/@oxc-parser/binding-linux-arm-musleabihf": { - "version": "0.133.0", - "resolved": "https://registry.npmjs.org/@oxc-parser/binding-linux-arm-musleabihf/-/binding-linux-arm-musleabihf-0.133.0.tgz", - "integrity": "sha512-yuZO533Ftonxn/iyoqQzURzLQHMspvsIyfiCSNi1t/ER4eIQaR0SsmUOUm5b/lmSig7IWIUa5/BrbEkAPwcilQ==", + "version": "0.137.0", + "resolved": "https://registry.npmjs.org/@oxc-parser/binding-linux-arm-musleabihf/-/binding-linux-arm-musleabihf-0.137.0.tgz", + "integrity": "sha512-AU2J9aa22Sx32wRGnDjybOU9TQXXQUud5sdUi+ZB0XxwM8aToWLweV+yA0wlQm0yIUVqljquqoHCYEq9II8gJQ==", "cpu": [ "arm" ], @@ -288,13 +243,16 @@ } }, "node_modules/@oxc-parser/binding-linux-arm64-gnu": { - "version": "0.133.0", - "resolved": "https://registry.npmjs.org/@oxc-parser/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-0.133.0.tgz", - "integrity": "sha512-hvpbqT5pN2rR+3+xtWeizwfR/aZ0vGceg6TqYMl+ToxMpk9/tmnX7kSvQnfEUkoua8mhogzvIKsAkn0wxgblBA==", + "version": "0.137.0", + "resolved": "https://registry.npmjs.org/@oxc-parser/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-0.137.0.tgz", + "integrity": "sha512-GdEtiG89yMr7XkUGxifgodXEEm2f+xW2f9CpDjlgAnBOwhTmrpQMvhOGobLVKUyzf/qHBXW16smk5zbF3nZU6w==", "cpu": [ "arm64" ], "dev": true, + "libc": [ + "glibc" + ], "license": "MIT", "optional": true, "os": [ @@ -305,13 +263,16 @@ } }, "node_modules/@oxc-parser/binding-linux-arm64-musl": { - "version": "0.133.0", - "resolved": "https://registry.npmjs.org/@oxc-parser/binding-linux-arm64-musl/-/binding-linux-arm64-musl-0.133.0.tgz", - "integrity": "sha512-wJQGamIosQBoJHW9+S5XxrtKRo3eyJxsnS1XCPrqN0LHi8uw1pTqqTfn3t/NVuvbBg7Pumn4ez9Eidgcn0xbEg==", + "version": "0.137.0", + "resolved": "https://registry.npmjs.org/@oxc-parser/binding-linux-arm64-musl/-/binding-linux-arm64-musl-0.137.0.tgz", + "integrity": "sha512-EGJ+Bs8iXx8KBH8DQ5BLoEm5lnHaYjlh4/8j8vFhrr/6z4tqONy5BZDzLpKmmNWlN6Hlc5r8YOuBVHqZ9vRFEQ==", "cpu": [ "arm64" ], "dev": true, + "libc": [ + "musl" + ], "license": "MIT", "optional": true, "os": [ @@ -322,13 +283,16 @@ } }, "node_modules/@oxc-parser/binding-linux-ppc64-gnu": { - "version": "0.133.0", - "resolved": "https://registry.npmjs.org/@oxc-parser/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-0.133.0.tgz", - "integrity": "sha512-Koaz32/O5+abIfrNGdyndgRvdOZ9jEf5/z3Ep9h3h2QWpdDiUQpVwgH0OcMXCs+l9aXxPLtkupqyVig9W6FDKw==", + "version": "0.137.0", + "resolved": "https://registry.npmjs.org/@oxc-parser/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-0.137.0.tgz", + "integrity": "sha512-vzFUQENy/fnbSe5DZWovq6tIBc1uhuMztanSW6rz1e9WdQE4gHwYuD7ZII6JnrJifd1R3RSoqiZbgRFlVL2tYQ==", "cpu": [ "ppc64" ], "dev": true, + "libc": [ + "glibc" + ], "license": "MIT", "optional": true, "os": [ @@ -339,13 +303,16 @@ } }, "node_modules/@oxc-parser/binding-linux-riscv64-gnu": { - "version": "0.133.0", - "resolved": "https://registry.npmjs.org/@oxc-parser/binding-linux-riscv64-gnu/-/binding-linux-riscv64-gnu-0.133.0.tgz", - "integrity": "sha512-R4vOjWzxhnNWHnVLeiB6jNuIifdy9vcMXZGPc7StXcxBovI+U2zg1QhZ9o8OjV80oGivs1lX5NfPLzk4IPqlRA==", + "version": "0.137.0", + "resolved": "https://registry.npmjs.org/@oxc-parser/binding-linux-riscv64-gnu/-/binding-linux-riscv64-gnu-0.137.0.tgz", + "integrity": "sha512-SfVI14HBQs9gtLcUD5hTt5hsNbdrqSUNg9S8muN+LhVQ5nf1WwH3hAoK6B9NKgdYgWAQSXFXGiiBedQ4r/BKuw==", "cpu": [ "riscv64" ], "dev": true, + "libc": [ + "glibc" + ], "license": "MIT", "optional": true, "os": [ @@ -356,13 +323,16 @@ } }, "node_modules/@oxc-parser/binding-linux-riscv64-musl": { - "version": "0.133.0", - "resolved": "https://registry.npmjs.org/@oxc-parser/binding-linux-riscv64-musl/-/binding-linux-riscv64-musl-0.133.0.tgz", - "integrity": "sha512-iwgBNUTHiMdxARLYuM0SBlnYeb19iw1Ea5M+4ERZupCsBMLArti6FyZ6UfFjJxIiTDr2oW2DGQFxlQVQ/dW9rA==", + "version": "0.137.0", + "resolved": "https://registry.npmjs.org/@oxc-parser/binding-linux-riscv64-musl/-/binding-linux-riscv64-musl-0.137.0.tgz", + "integrity": "sha512-e7Ppy4FCIFNQxT/ikSeIWFoQ0l+N9vgtRBtLcyZXeolTzApyVoPqEXsYPrcdM/9i0Bwk8knvYd37vaEMxHyi6g==", "cpu": [ "riscv64" ], "dev": true, + "libc": [ + "musl" + ], "license": "MIT", "optional": true, "os": [ @@ -373,13 +343,16 @@ } }, "node_modules/@oxc-parser/binding-linux-s390x-gnu": { - "version": "0.133.0", - "resolved": "https://registry.npmjs.org/@oxc-parser/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-0.133.0.tgz", - "integrity": "sha512-ZwZNo8FZmB/gVfboQl+wXilBigGl+6nQQs+nITOeAP/HcAOjiHl6XZJL9F/KXNEspODQcbjAiyjUbeCJd9a0fA==", + "version": "0.137.0", + "resolved": "https://registry.npmjs.org/@oxc-parser/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-0.137.0.tgz", + "integrity": "sha512-Bho5qFwdhqsIFR7gipYEUlqvi3SRrY8sugxXig380MIaakBB1PyU9+7dBiBVScfImTNWhijUxdBwqrprGdq5WA==", "cpu": [ "s390x" ], "dev": true, + "libc": [ + "glibc" + ], "license": "MIT", "optional": true, "os": [ @@ -390,13 +363,16 @@ } }, "node_modules/@oxc-parser/binding-linux-x64-gnu": { - "version": "0.133.0", - "resolved": "https://registry.npmjs.org/@oxc-parser/binding-linux-x64-gnu/-/binding-linux-x64-gnu-0.133.0.tgz", - "integrity": "sha512-govCvWx1dBlED3uu4qXctxpRcouu9I8Kn+DBktGCl760JtlGJzc9l/OmPJKlYWSbrRqKkMZehNeZ/4Wfma7uSA==", + "version": "0.137.0", + "resolved": "https://registry.npmjs.org/@oxc-parser/binding-linux-x64-gnu/-/binding-linux-x64-gnu-0.137.0.tgz", + "integrity": "sha512-36mGWtg7PyFzjJwGDkH6/F4o2nIDEoKXLPr/X/lwqklkomQwJJt1I5GJVmGhovUEmgPK5WAeAZMqlFCehwiy9Q==", "cpu": [ "x64" ], "dev": true, + "libc": [ + "glibc" + ], "license": "MIT", "optional": true, "os": [ @@ -407,13 +383,16 @@ } }, "node_modules/@oxc-parser/binding-linux-x64-musl": { - "version": "0.133.0", - "resolved": "https://registry.npmjs.org/@oxc-parser/binding-linux-x64-musl/-/binding-linux-x64-musl-0.133.0.tgz", - "integrity": "sha512-ssTlpXD5Mq9uCssDJPzlRWqBt4Y7Zzd9i+XZhWmK/9Y6KUIuAxVYTYiI8lxcGWi0+3/Cz4A8q9UrD4NK9Y2j7g==", + "version": "0.137.0", + "resolved": "https://registry.npmjs.org/@oxc-parser/binding-linux-x64-musl/-/binding-linux-x64-musl-0.137.0.tgz", + "integrity": "sha512-/Jqx6+N7A44n2BdvUr7pXhVr2vFjs6WGH3unZRczwrfiH0H1zY0QwKQMG/dtRiTlKGDKGukznPT8lx84/oEsZg==", "cpu": [ "x64" ], "dev": true, + "libc": [ + "musl" + ], "license": "MIT", "optional": true, "os": [ @@ -424,9 +403,9 @@ } }, "node_modules/@oxc-parser/binding-openharmony-arm64": { - "version": "0.133.0", - "resolved": "https://registry.npmjs.org/@oxc-parser/binding-openharmony-arm64/-/binding-openharmony-arm64-0.133.0.tgz", - "integrity": "sha512-51aByfXhPtLEdWG4a2Ihdw6cPWV1ei1AarALpFdDP8MLWDLE2NuUMgbo3DERR2Kt8fT/ok1GUvBiLxVGke9uUQ==", + "version": "0.137.0", + "resolved": "https://registry.npmjs.org/@oxc-parser/binding-openharmony-arm64/-/binding-openharmony-arm64-0.137.0.tgz", + "integrity": "sha512-9Uj0qHNNl+OgT1UTGwF7ixIXU6T1u2SbMidmgPy/h1h/fl2gRS6YpAxxY1gwHofcWjoTwkoMFd8xs5Vuj6GOFA==", "cpu": [ "arm64" ], @@ -441,9 +420,9 @@ } }, "node_modules/@oxc-parser/binding-wasm32-wasi": { - "version": "0.133.0", - "resolved": "https://registry.npmjs.org/@oxc-parser/binding-wasm32-wasi/-/binding-wasm32-wasi-0.133.0.tgz", - "integrity": "sha512-2e16tkKp+wDO2GTAmXfxbBcCmGEaFPIJEIRBBmVKNVXSc8/fJsSIaBGyFTPHM9ST5GNWgJcYIt94rDTks+PLwA==", + "version": "0.137.0", + "resolved": "https://registry.npmjs.org/@oxc-parser/binding-wasm32-wasi/-/binding-wasm32-wasi-0.137.0.tgz", + "integrity": "sha512-gW2vfkytNGgMVADiuzdvOfw0mWG9za20F/1fCJsif5aBMAvWJTSbpIXbIe0XkOe0VENk+PadpQ7cZgUy2sUJcA==", "cpu": [ "wasm32" ], @@ -451,18 +430,18 @@ "license": "MIT", "optional": true, "dependencies": { - "@emnapi/core": "1.10.0", - "@emnapi/runtime": "1.10.0", - "@napi-rs/wasm-runtime": "^1.1.4" + "@emnapi/core": "1.11.1", + "@emnapi/runtime": "1.11.1", + "@napi-rs/wasm-runtime": "^1.1.5" }, "engines": { "node": "^20.19.0 || >=22.12.0" } }, "node_modules/@oxc-parser/binding-win32-arm64-msvc": { - "version": "0.133.0", - "resolved": "https://registry.npmjs.org/@oxc-parser/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-0.133.0.tgz", - "integrity": "sha512-KPTNDKbxH1cglrqTyVeXHb4Pk4oksz8EcE1/v8zqU7N4UXbiHfA/IwtXZ2U77fnRAWBbgVkl/lZbL7o3hRdejg==", + "version": "0.137.0", + "resolved": "https://registry.npmjs.org/@oxc-parser/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-0.137.0.tgz", + "integrity": "sha512-x+pFANF0yL5uK/6T7lu6SlR5qid6sp//eZXKLq5iNsIE+EQg6EaS8/wsW7E91nXXjpnPhSoMOHXShSVhGRdn8w==", "cpu": [ "arm64" ], @@ -477,9 +456,9 @@ } }, "node_modules/@oxc-parser/binding-win32-ia32-msvc": { - "version": "0.133.0", - "resolved": "https://registry.npmjs.org/@oxc-parser/binding-win32-ia32-msvc/-/binding-win32-ia32-msvc-0.133.0.tgz", - "integrity": "sha512-Una1bNYv9zCavQrfnDR9wuZVB3itLjCEH4Oz7i6CwAJN/Xq9b+zbbcxmvdkKvvJt4Ngc/MBmIYlbLo3zS4TQ0A==", + "version": "0.137.0", + "resolved": "https://registry.npmjs.org/@oxc-parser/binding-win32-ia32-msvc/-/binding-win32-ia32-msvc-0.137.0.tgz", + "integrity": "sha512-sQUqym80PFi6McRsIqfJrSu2JrSClEZIXXD+/FjAFoULEKzOPsldIdFBG96xdX8aVMzCNQ9792FPx3MfkEIrFA==", "cpu": [ "ia32" ], @@ -494,9 +473,9 @@ } }, "node_modules/@oxc-parser/binding-win32-x64-msvc": { - "version": "0.133.0", - "resolved": "https://registry.npmjs.org/@oxc-parser/binding-win32-x64-msvc/-/binding-win32-x64-msvc-0.133.0.tgz", - "integrity": "sha512-kjBhCiOGSYTwDJQuuZa7a94JbP8htWu7J0X1KwH74kV2K5eYf6eyJRYmkpCDvr0XEL8tMxYI4WU1VekblFCLgg==", + "version": "0.137.0", + "resolved": "https://registry.npmjs.org/@oxc-parser/binding-win32-x64-msvc/-/binding-win32-x64-msvc-0.137.0.tgz", + "integrity": "sha512-2AsevxlvNN4WKxpEn3RtqD5zbqMaXF+T7JXblsP4gVuY+vC9dXS4ED/PwfRCliFqoeisYS3Iro4DHzxr0TEvVA==", "cpu": [ "x64" ], @@ -511,9 +490,9 @@ } }, "node_modules/@oxc-project/types": { - "version": "0.133.0", - "resolved": "https://registry.npmjs.org/@oxc-project/types/-/types-0.133.0.tgz", - "integrity": "sha512-KzkdCd6Uxqnf6l3HOw1xfatAlUURA0g14cvBYFyJ5SaNOQbOUvBr9PKArcPcrNIeRsBdgcUzOGrhKveVpvOIGA==", + "version": "0.137.0", + "resolved": "https://registry.npmjs.org/@oxc-project/types/-/types-0.137.0.tgz", + "integrity": "sha512-WT+Gb24i8hmvo85AIv2oEYouEXkRlKAlT9WaCa3TfLgNCN+GhrJOGZuIlMouAh38Qe4QOx26eUOVsq70qXrywA==", "dev": true, "license": "MIT", "funding": { @@ -521,9 +500,9 @@ } }, "node_modules/@oxc-resolver/binding-android-arm-eabi": { - "version": "11.20.0", - "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-android-arm-eabi/-/binding-android-arm-eabi-11.20.0.tgz", - "integrity": "sha512-IjfWOXRgJFNdORDl+Uf1aibNgZY2guOD3zmOhx1BGVb/MIiqlFTdmjpQNplSN58lhWehnX4UNqC3QwpUo8pjJg==", + "version": "11.21.3", + "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-android-arm-eabi/-/binding-android-arm-eabi-11.21.3.tgz", + "integrity": "sha512-eNU11A2WNizh04v3uyaJCootrHIaS0B9aHYXvAvVnPNk4xYSjMUjHnhQ6dewPN2MRYDskV85d1N0Aw0WNWhcyg==", "cpu": [ "arm" ], @@ -535,9 +514,9 @@ ] }, "node_modules/@oxc-resolver/binding-android-arm64": { - "version": "11.20.0", - "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-android-arm64/-/binding-android-arm64-11.20.0.tgz", - "integrity": "sha512-QqslZAuFQG8Q9xm7JuIn8JUbvywhSBMVhuQHtYW+auirZJloS41oxUUaBXk7uUhZJgp44c5zQLeVvmFaDQB+2Q==", + "version": "11.21.3", + "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-android-arm64/-/binding-android-arm64-11.21.3.tgz", + "integrity": "sha512-8Q+ZjTLvn2dIcWsrmhdrEihm7q+ag/k+mkry7Z+t0QbbHaVxXQfvH9AewyVMh/WrpEKhQ3DDgx9fYbqeCpeOEw==", "cpu": [ "arm64" ], @@ -549,9 +528,9 @@ ] }, "node_modules/@oxc-resolver/binding-darwin-arm64": { - "version": "11.20.0", - "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-darwin-arm64/-/binding-darwin-arm64-11.20.0.tgz", - "integrity": "sha512-MUcavykj2ewlR+kc5arpg4tC2RvzJkUxWtNv74pf7lcNk00GpIpN43vXMj+j6r4eMmfZhlb8hueKoIb8e9kAGQ==", + "version": "11.21.3", + "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-darwin-arm64/-/binding-darwin-arm64-11.21.3.tgz", + "integrity": "sha512-wkh0qKZGHXVUDxFw3oA1TXnU2BDYY/r775oJflGeIr8uDPPoN2pk8gijQIzYRT6hoql/lg3+Tx/SaTn9e2/aGg==", "cpu": [ "arm64" ], @@ -563,9 +542,9 @@ ] }, "node_modules/@oxc-resolver/binding-darwin-x64": { - "version": "11.20.0", - "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-darwin-x64/-/binding-darwin-x64-11.20.0.tgz", - "integrity": "sha512-BGB16nRUK5Etiv//ihPyzj8Lj1px0mhh4YIfe0FDf045ywknfSm0GEbiRESpr6Q4K82AvnyaRIhhluHByvS4bg==", + "version": "11.21.3", + "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-darwin-x64/-/binding-darwin-x64-11.21.3.tgz", + "integrity": "sha512-HbNc23FAQYbuyDV2vBWMez4u4mrsm5RAkniGZAWqr6lYZ3N4beeqIb776jzwRl8qL2zRhHVXpUj97X0QgogVzg==", "cpu": [ "x64" ], @@ -577,9 +556,9 @@ ] }, "node_modules/@oxc-resolver/binding-freebsd-x64": { - "version": "11.20.0", - "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-freebsd-x64/-/binding-freebsd-x64-11.20.0.tgz", - "integrity": "sha512-JZgtePaqj3qmD5XFHJaSLWzHRxQu0LaPkdoM1KJXYADvAaa83ijXHclV3ej3CueeW0wxfIAbGCZVP45J0CA7uQ==", + "version": "11.21.3", + "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-freebsd-x64/-/binding-freebsd-x64-11.21.3.tgz", + "integrity": "sha512-K6xNsTUPEUdfrn0+kbMq5nOUB5w1C5pavPQngt4TM2FpN91lP0PBe2srSpamb4d69O7h86oAi/qWX/kZNRSjkw==", "cpu": [ "x64" ], @@ -591,9 +570,9 @@ ] }, "node_modules/@oxc-resolver/binding-linux-arm-gnueabihf": { - "version": "11.20.0", - "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-11.20.0.tgz", - "integrity": "sha512-hOQ/p3ry3v3SchUBXicrrnszaI/UmYzM4wtS4RGfwgVUX7a+HbyQSzJ5aOzu+o6XZkFkS3ZXN4PZAzhOb77OSg==", + "version": "11.21.3", + "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-11.21.3.tgz", + "integrity": "sha512-VcFmOpcpWX1zoEy8M58tR2M9YxM+Z9RuQhqAx5q0CTmrruaP7Gveejg75hzd/5sg5nk9G3aLALEa3hE2FsmmTQ==", "cpu": [ "arm" ], @@ -605,9 +584,9 @@ ] }, "node_modules/@oxc-resolver/binding-linux-arm-musleabihf": { - "version": "11.20.0", - "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-linux-arm-musleabihf/-/binding-linux-arm-musleabihf-11.20.0.tgz", - "integrity": "sha512-2ArPksaw0AqeuGBfoS715VF+JvJQAhD2niWgjE5hVO+L+nAfikVQopvngCMX9x4BD8itWoQ3dnikrQyl5Ho5Jg==", + "version": "11.21.3", + "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-linux-arm-musleabihf/-/binding-linux-arm-musleabihf-11.21.3.tgz", + "integrity": "sha512-quVoxFLBy43hWaQbbDtQNRwAX5vX76mv7n64icAtQcJ3eNgVeblqmkupF/hAneNthdqSlnd1sTjb3aQSaDPaCQ==", "cpu": [ "arm" ], @@ -619,13 +598,16 @@ ] }, "node_modules/@oxc-resolver/binding-linux-arm64-gnu": { - "version": "11.20.0", - "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-11.20.0.tgz", - "integrity": "sha512-0bJnmYFp62JdZ4nVMDUZ/C58BCZOCcqgKtnUlp7L9Ojf/czIN+3j72YlLPeWLkzlr6SlYvIQA4SGV/HyO0d+qg==", + "version": "11.21.3", + "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-11.21.3.tgz", + "integrity": "sha512-X0AqNZgcD07Q4V3RDK18/vYOj/HQT/FnmEFGYS2jTWqY7JO13ryE3TEs3eAIgUJhBnNkpEaiXqz3VK8M7qQhWQ==", "cpu": [ "arm64" ], "dev": true, + "libc": [ + "glibc" + ], "license": "MIT", "optional": true, "os": [ @@ -633,13 +615,16 @@ ] }, "node_modules/@oxc-resolver/binding-linux-arm64-musl": { - "version": "11.20.0", - "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-linux-arm64-musl/-/binding-linux-arm64-musl-11.20.0.tgz", - "integrity": "sha512-wKHHzPKZo7Ufhv/Bt6yxT7FOgnIgW4gwXcJUipkShGp68W3wGVqvr1Sr0fY65lN0Oy6y41+g2kIDvkgZaMMUkw==", + "version": "11.21.3", + "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-linux-arm64-musl/-/binding-linux-arm64-musl-11.21.3.tgz", + "integrity": "sha512-YkaQnaKYdbuaXvRt5Qd0GpbihzVnyfR6z1SpYfIUC6RTu4NF7lDKPjVkYb+jRI2gedVO2rVpN35Y6akG6ud4Lw==", "cpu": [ "arm64" ], "dev": true, + "libc": [ + "musl" + ], "license": "MIT", "optional": true, "os": [ @@ -647,13 +632,16 @@ ] }, "node_modules/@oxc-resolver/binding-linux-ppc64-gnu": { - "version": "11.20.0", - "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-11.20.0.tgz", - "integrity": "sha512-RN8goF7Ie0B79L4i4G6OeBocTgSC56vJbQ65VJje+oXnldVpLnOU7j/AQ/dP94TcCS+Yh6WG8u3Qt4ETteXFNQ==", + "version": "11.21.3", + "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-11.21.3.tgz", + "integrity": "sha512-gB9HwhrPiFqUzDeEq+y/CgAijz1YdI6BnXz5GaH2Pa9cWdutchlkGFAiAuGb/PjVQpiK6NFKzFuztxrweoit7A==", "cpu": [ "ppc64" ], "dev": true, + "libc": [ + "glibc" + ], "license": "MIT", "optional": true, "os": [ @@ -661,13 +649,16 @@ ] }, "node_modules/@oxc-resolver/binding-linux-riscv64-gnu": { - "version": "11.20.0", - "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-linux-riscv64-gnu/-/binding-linux-riscv64-gnu-11.20.0.tgz", - "integrity": "sha512-5l1yU6/xQEqLZRzxqmMxJfWPslpwCmBsdDGaBvABPehxquCXDC7dd7oraNdKSJUMDXSM7VvVj8H2D2FTjU7oWw==", + "version": "11.21.3", + "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-linux-riscv64-gnu/-/binding-linux-riscv64-gnu-11.21.3.tgz", + "integrity": "sha512-zjDWBlYk8QGv0H8dsPUWqkfjYIIjG2TvspGkzXL0eImbgxtZorA/klKeHyolevoT3Kvbi+1iMr9Lhrh7jf54Og==", "cpu": [ "riscv64" ], "dev": true, + "libc": [ + "glibc" + ], "license": "MIT", "optional": true, "os": [ @@ -675,13 +666,16 @@ ] }, "node_modules/@oxc-resolver/binding-linux-riscv64-musl": { - "version": "11.20.0", - "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-linux-riscv64-musl/-/binding-linux-riscv64-musl-11.20.0.tgz", - "integrity": "sha512-xHEvkbgz6UC+A3JOyDQy76LkUaxsNSfIr3/GV8slwZsnuooJiIB34gzJfsyvR4JdCYNUUPsRJc/w/oWkODu+hg==", + "version": "11.21.3", + "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-linux-riscv64-musl/-/binding-linux-riscv64-musl-11.21.3.tgz", + "integrity": "sha512-4UfsQvacV388y1zpXL7C1x1FNYaV52JtuNRiuzrfQA2z1z6ElVrsidkGsrvQ5EgeSq1Pj7kaKqrgGkvFuxJ/tw==", "cpu": [ "riscv64" ], "dev": true, + "libc": [ + "musl" + ], "license": "MIT", "optional": true, "os": [ @@ -689,13 +683,16 @@ ] }, "node_modules/@oxc-resolver/binding-linux-s390x-gnu": { - "version": "11.20.0", - "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-11.20.0.tgz", - "integrity": "sha512-aWPDUUmSeyHvlW+SoEUd+JIJsQhVhu6a5tBpDRMu058naPAchTgAVGCFy35zjbnFlt0i8hLWziff6HX0D3LU4g==", + "version": "11.21.3", + "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-11.21.3.tgz", + "integrity": "sha512-b5uH+HKH0MP5mNBYaK75SKsJbw52URqrx2LavYdq6wb0l3ExAG5niYRP9DWUNHdKilpaBVM2bXk9HNWrH3ew7Q==", "cpu": [ "s390x" ], "dev": true, + "libc": [ + "glibc" + ], "license": "MIT", "optional": true, "os": [ @@ -703,13 +700,16 @@ ] }, "node_modules/@oxc-resolver/binding-linux-x64-gnu": { - "version": "11.20.0", - "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-linux-x64-gnu/-/binding-linux-x64-gnu-11.20.0.tgz", - "integrity": "sha512-x2YeSimvhJjKLVD8KSu8f/rqU1potcdEMkApIPJqjZWN7c2Fpt4g2X32WDg1p+XDAmyT7nuQGe0vnhvXeLbH+g==", + "version": "11.21.3", + "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-linux-x64-gnu/-/binding-linux-x64-gnu-11.21.3.tgz", + "integrity": "sha512-PjYlmilBpNRh2ntXNYAK3Am5w/nPfEpnU/96iNx7CI8EzAn12J4JRiec63wHJTH31nLoCNxBg/829pN+3CfG3Q==", "cpu": [ "x64" ], "dev": true, + "libc": [ + "glibc" + ], "license": "MIT", "optional": true, "os": [ @@ -717,13 +717,16 @@ ] }, "node_modules/@oxc-resolver/binding-linux-x64-musl": { - "version": "11.20.0", - "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-linux-x64-musl/-/binding-linux-x64-musl-11.20.0.tgz", - "integrity": "sha512-kcRLEIxpZefeYfLChjpgFf3ilBzRDZ+yobMrpRsQlSrxuFGtm3U6PMU7AaEpMqo3NfDGVyJJseAjnRLzMFHjwQ==", + "version": "11.21.3", + "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-linux-x64-musl/-/binding-linux-x64-musl-11.21.3.tgz", + "integrity": "sha512-QTBAb7JuHlZ7JUEyM8UiQi2f7m/L4swBhP2TNpYIDc9Wp/wRw1G/8sl6i13aIzQAXH7LKIm294LeOHd0lQR8zA==", "cpu": [ "x64" ], "dev": true, + "libc": [ + "musl" + ], "license": "MIT", "optional": true, "os": [ @@ -731,9 +734,9 @@ ] }, "node_modules/@oxc-resolver/binding-openharmony-arm64": { - "version": "11.20.0", - "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-openharmony-arm64/-/binding-openharmony-arm64-11.20.0.tgz", - "integrity": "sha512-HHcfnApSZGtKhTiHqe8OZruOZe5XuFQH5/E0Yhj3u8fnFvzkM4/k6WjacUf4SvA0SPEAbfbgYmVPuo0VX/fIBQ==", + "version": "11.21.3", + "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-openharmony-arm64/-/binding-openharmony-arm64-11.21.3.tgz", + "integrity": "sha512-4j1DFwjwv36ec9kds0jU/ucQ5Ha4ERO/H95BxR5JFf0kqUUAJ1kwII7XhTc1vZrkdJkvLGC9Q2MbpObpum8RBg==", "cpu": [ "arm64" ], @@ -745,9 +748,9 @@ ] }, "node_modules/@oxc-resolver/binding-wasm32-wasi": { - "version": "11.20.0", - "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-wasm32-wasi/-/binding-wasm32-wasi-11.20.0.tgz", - "integrity": "sha512-Tn0y1XOFYHNfK1wp1Z5QK8Rcld/bsOwRISQXfqAZ5IBpv8Gz1IvV39fUWNprqNdRizgcvFhOzWwFun2zkJsyBg==", + "version": "11.21.3", + "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-wasm32-wasi/-/binding-wasm32-wasi-11.21.3.tgz", + "integrity": "sha512-i8oluoel5kru/j1WNrjmQSiA3GQ7wvIYVR1IwIoZtKogAhya2iub+ZKIeSIkcJOrnzQ18Tzl/F+kL3fYOxZLvA==", "cpu": [ "wasm32" ], @@ -755,18 +758,41 @@ "license": "MIT", "optional": true, "dependencies": { - "@emnapi/core": "1.10.0", - "@emnapi/runtime": "1.10.0", - "@napi-rs/wasm-runtime": "^1.1.4" + "@emnapi/core": "1.11.0", + "@emnapi/runtime": "1.11.0", + "@napi-rs/wasm-runtime": "^1.1.5" }, "engines": { "node": ">=14.0.0" } }, + "node_modules/@oxc-resolver/binding-wasm32-wasi/node_modules/@emnapi/core": { + "version": "1.11.0", + "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.11.0.tgz", + "integrity": "sha512-l9Oo58x0HOP5znGzVhYW9U3e5wVuA4LAZU2AGezTmkhO1CgQRFDhDg4nneHsu/t3WniXg9QrG2nIXL/ZS8ln8Q==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@emnapi/wasi-threads": "1.2.2", + "tslib": "^2.4.0" + } + }, + "node_modules/@oxc-resolver/binding-wasm32-wasi/node_modules/@emnapi/runtime": { + "version": "1.11.0", + "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.11.0.tgz", + "integrity": "sha512-55coeOFKHv1ywEcUXJtWU5f+Jr/W5tZDvZig8DLKSwUN1JpROQ4rk/SNOQiFWmaR/VKF4zuFyW1B8JduOSv6Pg==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, "node_modules/@oxc-resolver/binding-win32-arm64-msvc": { - "version": "11.20.0", - "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-11.20.0.tgz", - "integrity": "sha512-qPi25YNPe4YenS8MgsQU2+bIFHxxpLx1LVna2444cEHqNPhNjvWf9zqj4aWE43H9LpAsTmkkAlA3eL5ElBU3mA==", + "version": "11.21.3", + "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-11.21.3.tgz", + "integrity": "sha512-M/8dw8dD6aOs+NlPJax401CZB9I7Aut84isQLgALGGwke4Afvw+/7yYhZb94yXf6t2sPLhQLmSmtSV+2FhsOWg==", "cpu": [ "arm64" ], @@ -778,9 +804,9 @@ ] }, "node_modules/@oxc-resolver/binding-win32-x64-msvc": { - "version": "11.20.0", - "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-win32-x64-msvc/-/binding-win32-x64-msvc-11.20.0.tgz", - "integrity": "sha512-Wb14jWEW8huH6It9F6sXd9vrYmIS7pMrgkU6sxpLxkP+9z+wRgs71hUEhRpcn8FOXAFa27FVWfY2tRpbfTzfLw==", + "version": "11.21.3", + "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-win32-x64-msvc/-/binding-win32-x64-msvc-11.21.3.tgz", + "integrity": "sha512-H7BCt/VnS9hnmMp42eGhZ99izSCRvlnWwy/N71K1/J8QoExwY4262Z8QiEkMDtduRJrztayDxETTckmUuAVL9Q==", "cpu": [ "x64" ], @@ -792,13 +818,12 @@ ] }, "node_modules/@roostorg/db-migrator": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@roostorg/db-migrator/-/db-migrator-1.1.0.tgz", - "integrity": "sha512-yRP2KsgU+nTX8p4lKUvygILnpK6xOeKYiP1ilpdluWVXPHQkSs/ZsnkyHGFPKuFn1+i2D+i5gMXUb1c1Jjtg2A==", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@roostorg/db-migrator/-/db-migrator-1.1.1.tgz", + "integrity": "sha512-X7zlkiRVQkfk3IWQ2SXSrV8aGc27bf2mQrfsT/H50YQOE9oHCbTbkw14daehT/yGnnCFN0YPT+lI0X/a5FwrUQ==", "license": "ISC", "dependencies": { "@total-typescript/ts-reset": "^0.6.1", - "@types/yargs": "^17.0.24", "cassandra-driver": "^4.8.0", "sequelize": "^6.37.8", "umzug": "^3.0.0", @@ -806,19 +831,17 @@ } }, "node_modules/@rushstack/node-core-library": { - "version": "5.13.0", - "resolved": "https://registry.npmjs.org/@rushstack/node-core-library/-/node-core-library-5.13.0.tgz", - "integrity": "sha512-IGVhy+JgUacAdCGXKUrRhwHMTzqhWwZUI+qEPcdzsb80heOw0QPbhhoVsoiMF7Klp8eYsp7hzpScMXmOa3Uhfg==", + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/@rushstack/node-core-library/-/node-core-library-4.0.2.tgz", + "integrity": "sha512-hyES82QVpkfQMeBMteQUnrhASL/KHPhd7iJ8euduwNJG4mu2GSOKybf0rOEjOm1Wz7CwJEUm9y0yD7jg2C1bfg==", "license": "MIT", "dependencies": { - "ajv": "~8.13.0", - "ajv-draft-04": "~1.0.0", - "ajv-formats": "~3.0.1", - "fs-extra": "~11.3.0", + "fs-extra": "~7.0.1", "import-lazy": "~4.0.0", "jju": "~1.4.0", "resolve": "~1.22.1", - "semver": "~7.5.4" + "semver": "~7.5.4", + "z-schema": "~5.0.2" }, "peerDependencies": { "@types/node": "*" @@ -845,12 +868,12 @@ } }, "node_modules/@rushstack/terminal": { - "version": "0.15.2", - "resolved": "https://registry.npmjs.org/@rushstack/terminal/-/terminal-0.15.2.tgz", - "integrity": "sha512-7Hmc0ysK5077R/IkLS9hYu0QuNafm+TbZbtYVzCMbeOdMjaRboLKrhryjwZSRJGJzu+TV1ON7qZHeqf58XfLpA==", + "version": "0.10.0", + "resolved": "https://registry.npmjs.org/@rushstack/terminal/-/terminal-0.10.0.tgz", + "integrity": "sha512-UbELbXnUdc7EKwfH2sb8ChqNgapUOdqcCIdQP4NGxBpTZV2sQyeekuK3zmfQSa/MN+/7b4kBogl2wq0vpkpYGw==", "license": "MIT", "dependencies": { - "@rushstack/node-core-library": "5.13.0", + "@rushstack/node-core-library": "4.0.2", "supports-color": "~8.1.1" }, "peerDependencies": { @@ -863,12 +886,12 @@ } }, "node_modules/@rushstack/ts-command-line": { - "version": "4.23.7", - "resolved": "https://registry.npmjs.org/@rushstack/ts-command-line/-/ts-command-line-4.23.7.tgz", - "integrity": "sha512-Gr9cB7DGe6uz5vq2wdr89WbVDKz0UeuFEn5H2CfWDe7JvjFFaiV15gi6mqDBTbHhHCWS7w8mF1h3BnIfUndqdA==", + "version": "4.19.1", + "resolved": "https://registry.npmjs.org/@rushstack/ts-command-line/-/ts-command-line-4.19.1.tgz", + "integrity": "sha512-J7H768dgcpG60d7skZ5uSSwyCZs/S2HrWP1Ds8d1qYAyaaeJmpmmLr9BVw97RjFzmQPOYnoXcKA4GkqDCkduQg==", "license": "MIT", "dependencies": { - "@rushstack/terminal": "0.15.2", + "@rushstack/terminal": "0.10.0", "@types/argparse": "1.0.38", "argparse": "~1.0.9", "string-argv": "~0.3.1" @@ -905,9 +928,9 @@ "license": "MIT" }, "node_modules/@tybys/wasm-util": { - "version": "0.10.2", - "resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.10.2.tgz", - "integrity": "sha512-RoBvJ2X0wuKlWFIjrwffGw1IqZHKQqzIchKaadZZfnNpsAYp2mM0h36JtPCjNDAHGgYez/15uMBpfGwchhiMgg==", + "version": "0.10.3", + "resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.10.3.tgz", + "integrity": "sha512-F3fo1MYrRJYL3zER0OUOmkutjr1Vp23m7OsSgp7nq4SP6OqX6C/56XFIPAl5bt3zaBRjmW7SGz3u/6LwFpYcOg==", "dev": true, "license": "MIT", "optional": true, @@ -963,21 +986,6 @@ "integrity": "sha512-T8L6i7wCuyoK8A/ZeLYt1+q0ty3Zb9+qbSSvrIVitzT3YjZqkTZ40IbRsPanlB4h1QB3JVL1SYCdR6ngtFYcuA==", "license": "MIT" }, - "node_modules/@types/yargs": { - "version": "17.0.35", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.35.tgz", - "integrity": "sha512-qUHkeCyQFxMXg79wQfTtfndEC+N9ZZg76HJftDJp+qH2tV7Gj4OJi7l+PiWwJ+pWtW8GwSmqsDj/oymhrTWXjg==", - "license": "MIT", - "dependencies": { - "@types/yargs-parser": "*" - } - }, - "node_modules/@types/yargs-parser": { - "version": "21.0.3", - "resolved": "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-21.0.3.tgz", - "integrity": "sha512-I4q9QU9MQv4oEOz4tAHJtNz1cwuLxn2F3xcc2iV5WdqLPpUnj30aUuxt1mAxYTG+oe8CZMV/+6rU4S4gRDzqtQ==", - "license": "MIT" - }, "node_modules/acorn": { "version": "8.16.0", "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.16.0.tgz", @@ -1011,53 +1019,6 @@ "node": ">=12.0" } }, - "node_modules/ajv": { - "version": "8.18.0", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.18.0.tgz", - "integrity": "sha512-PlXPeEWMXMZ7sPYOHqmDyCJzcfNrUr3fGNKtezX14ykXOEIvyK81d+qydx89KY5O71FKMPaQ2vBfBFI5NHR63A==", - "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-draft-04": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/ajv-draft-04/-/ajv-draft-04-1.0.0.tgz", - "integrity": "sha512-mv00Te6nmYbRp5DCwclxtt7yV/joXJPGS7nM+97GdxvuttCOfgI3K4U25zboyeX0O+myI8ERluxQe5wljMmVIw==", - "license": "MIT", - "peerDependencies": { - "ajv": "^8.5.0" - }, - "peerDependenciesMeta": { - "ajv": { - "optional": true - } - } - }, - "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==", - "license": "MIT", - "dependencies": { - "ajv": "^8.0.0" - }, - "peerDependencies": { - "ajv": "^8.0.0" - }, - "peerDependenciesMeta": { - "ajv": { - "optional": true - } - } - }, "node_modules/ansi-regex": { "version": "6.2.2", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.2.tgz", @@ -1097,45 +1058,33 @@ "sprintf-js": "~1.0.2" } }, - "node_modules/braces": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", - "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", - "license": "MIT", - "dependencies": { - "fill-range": "^7.1.1" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/cassandra-driver": { - "version": "4.8.0", - "resolved": "https://registry.npmjs.org/cassandra-driver/-/cassandra-driver-4.8.0.tgz", - "integrity": "sha512-HritfMGq9V7SuESeSodHvArs0mLuMk7uh+7hQK2lqdvXrvm50aWxb4RPxkK3mPDdsgHjJ427xNRFITMH2ei+Sw==", + "version": "4.9.0", + "resolved": "https://registry.npmjs.org/cassandra-driver/-/cassandra-driver-4.9.0.tgz", + "integrity": "sha512-svYpdkLIGjD0WmuuwkkeYbfBdPX1zksK2cDyT1mWjX53OVTzuWBOVy54K6PPij8GgYpIG+K82OryrBv/xNeuWg==", "license": "Apache-2.0", "dependencies": { - "@types/node": "^18.11.18", + "@types/node": "^20.14.8", "adm-zip": "~0.5.10", "long": "~5.2.3" }, "engines": { - "node": ">=18" + "node": ">=20" } }, "node_modules/cassandra-driver/node_modules/@types/node": { - "version": "18.19.130", - "resolved": "https://registry.npmjs.org/@types/node/-/node-18.19.130.tgz", - "integrity": "sha512-GRaXQx6jGfL8sKfaIDD6OupbIHBr9jv7Jnaml9tB7l4v068PAOXqfcujMMo5PhbIs6ggR1XODELqahT2R8v0fg==", + "version": "20.19.43", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.19.43.tgz", + "integrity": "sha512-6oYBAi5ikg4Pl+kGsoYtawUMBT2zZMCvPNF7pVLnHZfd1zf38DRiWn/gT01RYCdUqkv7Fhr+C9ot4/tb+2sVvA==", "license": "MIT", "dependencies": { - "undici-types": "~5.26.4" + "undici-types": "~6.21.0" } }, "node_modules/cassandra-driver/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.21.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz", + "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==", "license": "MIT" }, "node_modules/cliui": { @@ -1152,6 +1101,16 @@ "node": ">=20" } }, + "node_modules/commander": { + "version": "9.5.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-9.5.0.tgz", + "integrity": "sha512-KRs7WVDKg86PWiuAqhDrAQnTXZKraVcCc6vFdL14qrZ/DcWwuRo7VoiYXalXO7S5GKpqYiVEwCbgFDfxNHKJBQ==", + "license": "MIT", + "optional": true, + "engines": { + "node": "^12.20.0 || >=14" + } + }, "node_modules/create-require": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", @@ -1184,19 +1143,6 @@ "node": ">=0.3.1" } }, - "node_modules/dotenv": { - "version": "17.4.2", - "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-17.4.2.tgz", - "integrity": "sha512-nI4U3TottKAcAD9LLud4Cb7b2QztQMUEfHbvhTH09bqXTxnSie8WnjPALV/WMCrJZ6UV/qHJ6L03OqO3LcdYZw==", - "dev": true, - "license": "BSD-2-Clause", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://dotenvx.com" - } - }, "node_modules/dottie": { "version": "2.0.7", "resolved": "https://registry.npmjs.org/dottie/-/dottie-2.0.7.tgz", @@ -1240,53 +1186,6 @@ "node": ">=6" } }, - "node_modules/fast-deep-equal": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", - "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", - "license": "MIT" - }, - "node_modules/fast-glob": { - "version": "3.3.3", - "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz", - "integrity": "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==", - "license": "MIT", - "dependencies": { - "@nodelib/fs.stat": "^2.0.2", - "@nodelib/fs.walk": "^1.2.3", - "glob-parent": "^5.1.2", - "merge2": "^1.3.0", - "micromatch": "^4.0.8" - }, - "engines": { - "node": ">=8.6.0" - } - }, - "node_modules/fast-uri": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.2.tgz", - "integrity": "sha512-rVjf7ArG3LTk+FS6Yw81V1DLuZl1bRbNrev6Tmd/9RaroeeRRJhAt7jg/6YFxbvAQXUCavSoZhPPj6oOx+5KjQ==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/fastify" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/fastify" - } - ], - "license": "BSD-3-Clause" - }, - "node_modules/fastq": { - "version": "1.20.1", - "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.20.1.tgz", - "integrity": "sha512-GGToxJ/w1x32s/D2EKND7kTil4n8OVk/9mycTc4VDza13lOvpUZTGX3mFSCtV9ksdGBVzvsyAVLM6mHFThxXxw==", - "license": "ISC", - "dependencies": { - "reusify": "^1.0.4" - } - }, "node_modules/fd-package-json": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/fd-package-json/-/fd-package-json-2.0.0.tgz", @@ -1297,18 +1196,6 @@ "walk-up-path": "^4.0.0" } }, - "node_modules/fill-range": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", - "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", - "license": "MIT", - "dependencies": { - "to-regex-range": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/formatly": { "version": "0.3.0", "resolved": "https://registry.npmjs.org/formatly/-/formatly-0.3.0.tgz", @@ -1326,17 +1213,17 @@ } }, "node_modules/fs-extra": { - "version": "11.3.4", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.3.4.tgz", - "integrity": "sha512-CTXd6rk/M3/ULNQj8FBqBWHYBVYybQ3VPBw0xGKFe3tuH7ytT6ACnvzpIQ3UZtB8yvUKC2cXn1a+x+5EVQLovA==", + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-7.0.1.tgz", + "integrity": "sha512-YJDaCJZEnBmcbw13fvdAM9AwNOJwOzrE4pqMqBq5nFiEqXUqHwlK4B+3pUw6JNvfSPtX05xFHtYy/1ni01eGCw==", "license": "MIT", "dependencies": { - "graceful-fs": "^4.2.0", - "jsonfile": "^6.0.1", - "universalify": "^2.0.0" + "graceful-fs": "^4.1.2", + "jsonfile": "^4.0.0", + "universalify": "^0.1.0" }, "engines": { - "node": ">=14.14" + "node": ">=6 <7 || >=8" } }, "node_modules/function-bind": { @@ -1382,18 +1269,6 @@ "url": "https://github.com/privatenumber/get-tsconfig?sponsor=1" } }, - "node_modules/glob-parent": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", - "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", - "license": "ISC", - "dependencies": { - "is-glob": "^4.0.1" - }, - "engines": { - "node": ">= 6" - } - }, "node_modules/graceful-fs": { "version": "4.2.11", "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", @@ -1410,9 +1285,9 @@ } }, "node_modules/hasown": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", - "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.4.tgz", + "integrity": "sha512-T2UbfbBEF32wiepXIsMlTW9+dDYC6wMh/t/vYA4tuOMKqWz/n3vr1NFSxQiyP+zk2mXsoMA/i/7qV6LKut1t1A==", "license": "MIT", "dependencies": { "function-bind": "^1.1.2" @@ -1440,12 +1315,12 @@ "license": "MIT" }, "node_modules/is-core-module": { - "version": "2.16.1", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.1.tgz", - "integrity": "sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==", + "version": "2.16.2", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.2.tgz", + "integrity": "sha512-evOr8xfXKxE6qSR0hSXL2r3sd7ALj8+7jQEUvPYcm5sgZFdJ+AYzT6yNmJenvIYQBgIGwfwz08sL8zoL7yq2BA==", "license": "MIT", "dependencies": { - "hasown": "^2.0.2" + "hasown": "^2.0.3" }, "engines": { "node": ">= 0.4" @@ -1454,36 +1329,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/is-extglob": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", - "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-glob": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", - "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", - "license": "MIT", - "dependencies": { - "is-extglob": "^2.1.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-number": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", - "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", - "license": "MIT", - "engines": { - "node": ">=0.12.0" - } - }, "node_modules/jiti": { "version": "2.7.0", "resolved": "https://registry.npmjs.org/jiti/-/jiti-2.7.0.tgz", @@ -1500,28 +1345,19 @@ "integrity": "sha512-8wb9Yw966OSxApiCt0K3yNJL8pnNeIv+OEq2YMidz4FKP6nonSRoOXc80iXY4JaN2FC11B9qsNmDsm+ZOfMROA==", "license": "MIT" }, - "node_modules/json-schema-traverse": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", - "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", - "license": "MIT" - }, "node_modules/jsonfile": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.2.0.tgz", - "integrity": "sha512-FGuPw30AdOIUTRMC2OMRtQV+jkVj2cfPqSeWXv1NEAJ1qZ5zb1X6z1mFhbfOB/iy3ssJCD+3KuZ8r8C3uVFlAg==", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz", + "integrity": "sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==", "license": "MIT", - "dependencies": { - "universalify": "^2.0.0" - }, "optionalDependencies": { "graceful-fs": "^4.1.6" } }, "node_modules/knip": { - "version": "6.16.1", - "resolved": "https://registry.npmjs.org/knip/-/knip-6.16.1.tgz", - "integrity": "sha512-TKMn1rxgH6h9vXR9Y0B+Cq7AdPTr9EI02IwoT65NzqYUkvoDQAaJ/aPybiFpAhZ1px6cNYYwXf86iHkBgzCo9w==", + "version": "6.26.0", + "resolved": "https://registry.npmjs.org/knip/-/knip-6.26.0.tgz", + "integrity": "sha512-e9eELEEpBpGTd4H4HB7818/DYj9dMzMyUqAddfYwUN/EbSkgIjOuWEF96W/xHsmV0SDrsdXjIM+oZ2xpPzPsBA==", "dev": true, "funding": [ { @@ -1539,13 +1375,13 @@ "formatly": "^0.3.0", "get-tsconfig": "4.14.0", "jiti": "^2.7.0", - "oxc-parser": "^0.133.0", - "oxc-resolver": "^11.20.0", + "oxc-parser": "^0.137.0", + "oxc-resolver": "11.21.3", "picomatch": "^4.0.4", "smol-toml": "^1.6.1", "strip-json-comments": "5.0.3", - "tinyglobby": "^0.2.16", - "unbash": "^3.0.0", + "tinyglobby": "^0.2.17", + "unbash": "^4.0.1", "yaml": "^2.9.0", "zod": "^4.1.11" }, @@ -1589,12 +1425,12 @@ } }, "node_modules/kysely": { - "version": "0.28.17", - "resolved": "https://registry.npmjs.org/kysely/-/kysely-0.28.17.tgz", - "integrity": "sha512-nbD8lB9EB3wNdMhOCdx5Li8DxnLbvKByylRLcJ1h+4SkrowVeECAyZlyiKMThF7xFdRz0jSQ2MoJr+wXux2y0Q==", + "version": "0.29.4", + "resolved": "https://registry.npmjs.org/kysely/-/kysely-0.29.4.tgz", + "integrity": "sha512-y5mVgQNkMbs1eK9Xyc0pmNdabN2wHhRYY/5r4W5HrUT1rYCEPeVNSj1RUJeSDKT3U0p+mXCvLgkrFuIafYI6BA==", "license": "MIT", "engines": { - "node": ">=20.0.0" + "node": ">=22.0.0" } }, "node_modules/lodash": { @@ -1603,6 +1439,20 @@ "integrity": "sha512-dMInicTPVE8d1e5otfwmmjlxkZoUpiVLwyeTdUsi/Caj/gfzzblBcCE5sRHV/AsjuCmxWrte2TNGSYuCeCq+0Q==", "license": "MIT" }, + "node_modules/lodash.get": { + "version": "4.4.2", + "resolved": "https://registry.npmjs.org/lodash.get/-/lodash.get-4.4.2.tgz", + "integrity": "sha512-z+Uw/vLuy6gQe8cfaFWD7p0wVv8fJl3mbzXh33RS+0oW2wvUqiRXiQ69gLWSLpgB5/6sU+r6BlQR0MBILadqTQ==", + "deprecated": "This package is deprecated. Use the optional chaining (?.) operator instead.", + "license": "MIT" + }, + "node_modules/lodash.isequal": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.isequal/-/lodash.isequal-4.5.0.tgz", + "integrity": "sha512-pDo3lu8Jhfjqls6GkMgpahsF9kCyayhgykjyLMNFTKWrpVdAQtYyB4muAMWozBB4ig/dtWAmsMxLEI8wuz+DYQ==", + "deprecated": "This package is deprecated. Use require('node:util').isDeepStrictEqual instead.", + "license": "MIT" + }, "node_modules/long": { "version": "5.2.5", "resolved": "https://registry.npmjs.org/long/-/long-5.2.5.tgz", @@ -1627,28 +1477,6 @@ "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", "license": "ISC" }, - "node_modules/merge2": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", - "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", - "license": "MIT", - "engines": { - "node": ">= 8" - } - }, - "node_modules/micromatch": { - "version": "4.0.8", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", - "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", - "license": "MIT", - "dependencies": { - "braces": "^3.0.3", - "picomatch": "^2.3.1" - }, - "engines": { - "node": ">=8.6" - } - }, "node_modules/moment": { "version": "2.30.1", "resolved": "https://registry.npmjs.org/moment/-/moment-2.30.1.tgz", @@ -1677,13 +1505,13 @@ "license": "MIT" }, "node_modules/oxc-parser": { - "version": "0.133.0", - "resolved": "https://registry.npmjs.org/oxc-parser/-/oxc-parser-0.133.0.tgz", - "integrity": "sha512-661RSx+ZcjBmjBYid+Fpp/2F5EbtildpeoZh5HdgnGs+jZ03nqQEQW8yGkt4BGyOC3OMPDQQRl8M5kqD2/g6jw==", + "version": "0.137.0", + "resolved": "https://registry.npmjs.org/oxc-parser/-/oxc-parser-0.137.0.tgz", + "integrity": "sha512-yFImD+WLElJpLKy8llG1qe4DCmMsL18peRp8XP1JKfig/gISbJkglnpDtX2aTmAn10kZF7164HbN2H8QPsXxGg==", "dev": true, "license": "MIT", "dependencies": { - "@oxc-project/types": "^0.133.0" + "@oxc-project/types": "^0.137.0" }, "engines": { "node": "^20.19.0 || >=22.12.0" @@ -1692,57 +1520,57 @@ "url": "https://github.com/sponsors/Boshen" }, "optionalDependencies": { - "@oxc-parser/binding-android-arm-eabi": "0.133.0", - "@oxc-parser/binding-android-arm64": "0.133.0", - "@oxc-parser/binding-darwin-arm64": "0.133.0", - "@oxc-parser/binding-darwin-x64": "0.133.0", - "@oxc-parser/binding-freebsd-x64": "0.133.0", - "@oxc-parser/binding-linux-arm-gnueabihf": "0.133.0", - "@oxc-parser/binding-linux-arm-musleabihf": "0.133.0", - "@oxc-parser/binding-linux-arm64-gnu": "0.133.0", - "@oxc-parser/binding-linux-arm64-musl": "0.133.0", - "@oxc-parser/binding-linux-ppc64-gnu": "0.133.0", - "@oxc-parser/binding-linux-riscv64-gnu": "0.133.0", - "@oxc-parser/binding-linux-riscv64-musl": "0.133.0", - "@oxc-parser/binding-linux-s390x-gnu": "0.133.0", - "@oxc-parser/binding-linux-x64-gnu": "0.133.0", - "@oxc-parser/binding-linux-x64-musl": "0.133.0", - "@oxc-parser/binding-openharmony-arm64": "0.133.0", - "@oxc-parser/binding-wasm32-wasi": "0.133.0", - "@oxc-parser/binding-win32-arm64-msvc": "0.133.0", - "@oxc-parser/binding-win32-ia32-msvc": "0.133.0", - "@oxc-parser/binding-win32-x64-msvc": "0.133.0" + "@oxc-parser/binding-android-arm-eabi": "0.137.0", + "@oxc-parser/binding-android-arm64": "0.137.0", + "@oxc-parser/binding-darwin-arm64": "0.137.0", + "@oxc-parser/binding-darwin-x64": "0.137.0", + "@oxc-parser/binding-freebsd-x64": "0.137.0", + "@oxc-parser/binding-linux-arm-gnueabihf": "0.137.0", + "@oxc-parser/binding-linux-arm-musleabihf": "0.137.0", + "@oxc-parser/binding-linux-arm64-gnu": "0.137.0", + "@oxc-parser/binding-linux-arm64-musl": "0.137.0", + "@oxc-parser/binding-linux-ppc64-gnu": "0.137.0", + "@oxc-parser/binding-linux-riscv64-gnu": "0.137.0", + "@oxc-parser/binding-linux-riscv64-musl": "0.137.0", + "@oxc-parser/binding-linux-s390x-gnu": "0.137.0", + "@oxc-parser/binding-linux-x64-gnu": "0.137.0", + "@oxc-parser/binding-linux-x64-musl": "0.137.0", + "@oxc-parser/binding-openharmony-arm64": "0.137.0", + "@oxc-parser/binding-wasm32-wasi": "0.137.0", + "@oxc-parser/binding-win32-arm64-msvc": "0.137.0", + "@oxc-parser/binding-win32-ia32-msvc": "0.137.0", + "@oxc-parser/binding-win32-x64-msvc": "0.137.0" } }, "node_modules/oxc-resolver": { - "version": "11.20.0", - "resolved": "https://registry.npmjs.org/oxc-resolver/-/oxc-resolver-11.20.0.tgz", - "integrity": "sha512-CblytBiV/a/ZXY34dsVU2NxhIOxMXst8CvDCtyBelVITgd7PLrKzbEbA6oKLdPjvDKDzCiW48qzmzZ+mYaqn+g==", + "version": "11.21.3", + "resolved": "https://registry.npmjs.org/oxc-resolver/-/oxc-resolver-11.21.3.tgz", + "integrity": "sha512-2Mx3fKQz7+xgrBONjsxOgCGtMHOn38/HxMzW1I5efwXB5a4lRN0Vp40gYUJFBWJslcrvwoofTrqoTnLbwTd3pA==", "dev": true, "license": "MIT", "funding": { "url": "https://github.com/sponsors/Boshen" }, "optionalDependencies": { - "@oxc-resolver/binding-android-arm-eabi": "11.20.0", - "@oxc-resolver/binding-android-arm64": "11.20.0", - "@oxc-resolver/binding-darwin-arm64": "11.20.0", - "@oxc-resolver/binding-darwin-x64": "11.20.0", - "@oxc-resolver/binding-freebsd-x64": "11.20.0", - "@oxc-resolver/binding-linux-arm-gnueabihf": "11.20.0", - "@oxc-resolver/binding-linux-arm-musleabihf": "11.20.0", - "@oxc-resolver/binding-linux-arm64-gnu": "11.20.0", - "@oxc-resolver/binding-linux-arm64-musl": "11.20.0", - "@oxc-resolver/binding-linux-ppc64-gnu": "11.20.0", - "@oxc-resolver/binding-linux-riscv64-gnu": "11.20.0", - "@oxc-resolver/binding-linux-riscv64-musl": "11.20.0", - "@oxc-resolver/binding-linux-s390x-gnu": "11.20.0", - "@oxc-resolver/binding-linux-x64-gnu": "11.20.0", - "@oxc-resolver/binding-linux-x64-musl": "11.20.0", - "@oxc-resolver/binding-openharmony-arm64": "11.20.0", - "@oxc-resolver/binding-wasm32-wasi": "11.20.0", - "@oxc-resolver/binding-win32-arm64-msvc": "11.20.0", - "@oxc-resolver/binding-win32-x64-msvc": "11.20.0" + "@oxc-resolver/binding-android-arm-eabi": "11.21.3", + "@oxc-resolver/binding-android-arm64": "11.21.3", + "@oxc-resolver/binding-darwin-arm64": "11.21.3", + "@oxc-resolver/binding-darwin-x64": "11.21.3", + "@oxc-resolver/binding-freebsd-x64": "11.21.3", + "@oxc-resolver/binding-linux-arm-gnueabihf": "11.21.3", + "@oxc-resolver/binding-linux-arm-musleabihf": "11.21.3", + "@oxc-resolver/binding-linux-arm64-gnu": "11.21.3", + "@oxc-resolver/binding-linux-arm64-musl": "11.21.3", + "@oxc-resolver/binding-linux-ppc64-gnu": "11.21.3", + "@oxc-resolver/binding-linux-riscv64-gnu": "11.21.3", + "@oxc-resolver/binding-linux-riscv64-musl": "11.21.3", + "@oxc-resolver/binding-linux-s390x-gnu": "11.21.3", + "@oxc-resolver/binding-linux-x64-gnu": "11.21.3", + "@oxc-resolver/binding-linux-x64-musl": "11.21.3", + "@oxc-resolver/binding-openharmony-arm64": "11.21.3", + "@oxc-resolver/binding-wasm32-wasi": "11.21.3", + "@oxc-resolver/binding-win32-arm64-msvc": "11.21.3", + "@oxc-resolver/binding-win32-x64-msvc": "11.21.3" } }, "node_modules/path-parse": { @@ -1752,14 +1580,14 @@ "license": "MIT" }, "node_modules/pg": { - "version": "8.20.0", - "resolved": "https://registry.npmjs.org/pg/-/pg-8.20.0.tgz", - "integrity": "sha512-ldhMxz2r8fl/6QkXnBD3CR9/xg694oT6DZQ2s6c/RI28OjtSOpxnPrUCGOBJ46RCUxcWdx3p6kw/xnDHjKvaRA==", + "version": "8.22.0", + "resolved": "https://registry.npmjs.org/pg/-/pg-8.22.0.tgz", + "integrity": "sha512-8wih1vVIBMxoUM2oB4soJsD9tDnDpLv4OXBJ+EJzFsvycD+lfyIreC2gGHq78f8jbLLt+bvlPTFdFZfJkOuzAA==", "license": "MIT", "dependencies": { - "pg-connection-string": "^2.12.0", - "pg-pool": "^3.13.0", - "pg-protocol": "^1.13.0", + "pg-connection-string": "^2.14.0", + "pg-pool": "^3.14.0", + "pg-protocol": "^1.15.0", "pg-types": "2.2.0", "pgpass": "1.0.5" }, @@ -1767,7 +1595,7 @@ "node": ">= 16.0.0" }, "optionalDependencies": { - "pg-cloudflare": "^1.3.0" + "pg-cloudflare": "^1.4.0" }, "peerDependencies": { "pg-native": ">=3.0.1" @@ -1779,16 +1607,16 @@ } }, "node_modules/pg-cloudflare": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/pg-cloudflare/-/pg-cloudflare-1.3.0.tgz", - "integrity": "sha512-6lswVVSztmHiRtD6I8hw4qP/nDm1EJbKMRhf3HCYaqud7frGysPv7FYJ5noZQdhQtN2xJnimfMtvQq21pdbzyQ==", + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/pg-cloudflare/-/pg-cloudflare-1.4.0.tgz", + "integrity": "sha512-Vo7z/6rrQYxpNRylp4Tlob2elzbh+N/MOQbxFVWCxS7oEx6jF53GTJFxK2WWpKuBRkmiin4Mt+xofFDjx09R0A==", "license": "MIT", "optional": true }, "node_modules/pg-connection-string": { - "version": "2.12.0", - "resolved": "https://registry.npmjs.org/pg-connection-string/-/pg-connection-string-2.12.0.tgz", - "integrity": "sha512-U7qg+bpswf3Cs5xLzRqbXbQl85ng0mfSV/J0nnA31MCLgvEaAo7CIhmeyrmJpOr7o+zm0rXK+hNnT5l9RHkCkQ==", + "version": "2.14.0", + "resolved": "https://registry.npmjs.org/pg-connection-string/-/pg-connection-string-2.14.0.tgz", + "integrity": "sha512-XwWDGcLRGCXAR8F/AM5bG7Q+A3Wm2s6QeEjlOKZLlH3UYcguiqCWKyWXVag5TLTIjR7oOJUY8kcADaZgWPyLeg==", "license": "MIT" }, "node_modules/pg-int8": { @@ -1801,18 +1629,18 @@ } }, "node_modules/pg-pool": { - "version": "3.13.0", - "resolved": "https://registry.npmjs.org/pg-pool/-/pg-pool-3.13.0.tgz", - "integrity": "sha512-gB+R+Xud1gLFuRD/QgOIgGOBE2KCQPaPwkzBBGC9oG69pHTkhQeIuejVIk3/cnDyX39av2AxomQiyPT13WKHQA==", + "version": "3.14.0", + "resolved": "https://registry.npmjs.org/pg-pool/-/pg-pool-3.14.0.tgz", + "integrity": "sha512-gKtPkFdQPU3DksooVLi9LsjZxrsBUZIpa+7aVx+LV5pNh0KzP4Zleud2po+ConrxbuXGBJ6Hfer6hdgpIBpBaw==", "license": "MIT", "peerDependencies": { "pg": ">=8.0" } }, "node_modules/pg-protocol": { - "version": "1.13.0", - "resolved": "https://registry.npmjs.org/pg-protocol/-/pg-protocol-1.13.0.tgz", - "integrity": "sha512-zzdvXfS6v89r6v7OcFCHfHlyG/wvry1ALxZo4LqgUoy7W9xhBDMaqOuMiF3qEV45VqsN6rdlcehHrfDtlCPc8w==", + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/pg-protocol/-/pg-protocol-1.15.0.tgz", + "integrity": "sha512-cq9sECI5s0+uPUXjbz8ioyPJni6RzsRib0US67i5IoTZKw8fNeYlVE7u8F4dG7vEJJtc5wdD1K189lCCUwqWTQ==", "license": "MIT" }, "node_modules/pg-types": { @@ -1840,18 +1668,6 @@ "split2": "^4.1.0" } }, - "node_modules/picomatch": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.2.tgz", - "integrity": "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==", - "license": "MIT", - "engines": { - "node": ">=8.6" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } - }, "node_modules/pony-cause": { "version": "2.1.11", "resolved": "https://registry.npmjs.org/pony-cause/-/pony-cause-2.1.11.tgz", @@ -1900,35 +1716,6 @@ "node": ">=0.10.0" } }, - "node_modules/queue-microtask": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", - "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", - "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/require-from-string": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", - "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/resolve": { "version": "1.22.12", "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.12.tgz", @@ -1966,39 +1753,6 @@ "integrity": "sha512-hMD7odLOt3LkTjcif8aRZqi/hybjpLNgSk5oF5FCowfCjok6LukpN2bDX7R5wDmbgBQFn7YoBxSagmtXHaJYJw==", "license": "MIT" }, - "node_modules/reusify": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.1.0.tgz", - "integrity": "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==", - "license": "MIT", - "engines": { - "iojs": ">=1.0.0", - "node": ">=0.10.0" - } - }, - "node_modules/run-parallel": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", - "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "MIT", - "dependencies": { - "queue-microtask": "^1.2.2" - } - }, "node_modules/semver": { "version": "7.7.4", "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz", @@ -2195,7 +1949,6 @@ "version": "0.2.17", "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.17.tgz", "integrity": "sha512-wXR/dYpcqKmfWpEdZjiKJOwCNFndD0DMnrW/cYjVGttEkBfVgcLFHoNrlj47mjOVic9yyNu65alsgF4NQyTa2g==", - "dev": true, "license": "MIT", "dependencies": { "fdir": "^6.5.0", @@ -2212,7 +1965,6 @@ "version": "6.5.0", "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", - "dev": true, "license": "MIT", "engines": { "node": ">=12.0.0" @@ -2230,7 +1982,6 @@ "version": "4.0.4", "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.4.tgz", "integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==", - "dev": true, "license": "MIT", "engines": { "node": ">=12" @@ -2239,18 +1990,6 @@ "url": "https://github.com/sponsors/jonschlinkert" } }, - "node_modules/to-regex-range": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", - "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", - "license": "MIT", - "dependencies": { - "is-number": "^7.0.0" - }, - "engines": { - "node": ">=8.0" - } - }, "node_modules/toposort-class": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/toposort-class/-/toposort-class-1.0.1.tgz", @@ -2334,15 +2073,15 @@ } }, "node_modules/umzug": { - "version": "3.8.2", - "resolved": "https://registry.npmjs.org/umzug/-/umzug-3.8.2.tgz", - "integrity": "sha512-BEWEF8OJjTYVC56GjELeHl/1XjFejrD7aHzn+HldRJTx+pL1siBrKHZC8n4K/xL3bEzVA9o++qD1tK2CpZu4KA==", + "version": "3.8.3", + "resolved": "https://registry.npmjs.org/umzug/-/umzug-3.8.3.tgz", + "integrity": "sha512-U9SRJI6LJvV0XwrqGMVPBkE26WHJklHZjtscJ2sEjUp7f+h4NH/25YGjPBernWLroVJvMnTkCAGC0bT0dd63qA==", "license": "MIT", "dependencies": { - "@rushstack/ts-command-line": "^4.12.2", + "@rushstack/ts-command-line": "4.19.1", "emittery": "^0.13.0", - "fast-glob": "^3.3.2", "pony-cause": "^2.1.4", + "tinyglobby": "^0.2.16", "type-fest": "^4.0.0" }, "engines": { @@ -2350,9 +2089,9 @@ } }, "node_modules/unbash": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/unbash/-/unbash-3.0.0.tgz", - "integrity": "sha512-FeFPZ/WFT0mbRCuydiZzpPFlrYN8ZUpphQKoq4EeElVIYjYyGzPMxQR/simUwCOJIyVhpFk4RbtyO7RuMpMnHA==", + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/unbash/-/unbash-4.0.3.tgz", + "integrity": "sha512-3cudTErfToSc4Ggv8XGXVNVli/xHKUtUZvaY5UVwhOcUPbQGz7PeaEnT/SAVgNziZtX67KEN9swMUYkLghxA1w==", "dev": true, "license": "ISC", "engines": { @@ -2366,12 +2105,12 @@ "license": "MIT" }, "node_modules/universalify": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", - "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", + "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==", "license": "MIT", "engines": { - "node": ">= 10.0.0" + "node": ">= 4.0.0" } }, "node_modules/uuid": { @@ -2513,6 +2252,26 @@ "node": ">=6" } }, + "node_modules/z-schema": { + "version": "5.0.5", + "resolved": "https://registry.npmjs.org/z-schema/-/z-schema-5.0.5.tgz", + "integrity": "sha512-D7eujBWkLa3p2sIpJA0d1pr7es+a7m0vFAnZLlCEKq/Ij2k0MLi9Br2UPxoxdYystm5K1yeBGzub0FlYUEWj2Q==", + "license": "MIT", + "dependencies": { + "lodash.get": "^4.4.2", + "lodash.isequal": "^4.5.0", + "validator": "^13.7.0" + }, + "bin": { + "z-schema": "bin/z-schema" + }, + "engines": { + "node": ">=8.0.0" + }, + "optionalDependencies": { + "commander": "^9.4.1" + } + }, "node_modules/zod": { "version": "4.4.3", "resolved": "https://registry.npmjs.org/zod/-/zod-4.4.3.tgz", diff --git a/db/package.json b/db/package.json index 894582de4..1528dcf94 100644 --- a/db/package.json +++ b/db/package.json @@ -18,24 +18,23 @@ "author": "Roostorg", "license": "ISC", "dependencies": { - "@clickhouse/client": "^1.18.3", - "@roostorg/db-migrator": "^1.1.0", - "cassandra-driver": "^4.8.0", - "kysely": "^0.28.17", - "pg": "^8.7.1", + "@clickhouse/client": "^1.23.1", + "@roostorg/db-migrator": "^1.1.1", + "cassandra-driver": "^4.9.0", + "kysely": "^0.29.4", + "pg": "^8.22.0", "sequelize": "^6.37.8", "ts-node": "^10.9.2", - "umzug": "^3.0.0" + "umzug": "^3.8.3" }, "overrides": { "ajv": "~8.18.0", - "fast-uri": "^3.1.2", + "fast-uri": "^3.1.4", "uuid": "^14.0.0" }, "devDependencies": { "@types/pg": "^8.10.2", - "dotenv": "^17.4.2", - "knip": "^6.16.1", + "knip": "^6.26.0", "typescript": "^6.0.3" } } diff --git a/db/src/configs/scylla.ts b/db/src/configs/scylla.ts index 766132c65..2e0b7e3c4 100644 --- a/db/src/configs/scylla.ts +++ b/db/src/configs/scylla.ts @@ -24,6 +24,9 @@ const driverOpts = { consistency: scyllaTypes.consistencies.localQuorum, }, keyspace: process.env.SCYLLA_KEYSPACE!, + protocolOptions: { + port: parseInt(process.env.SCYLLA_PORT ?? '9042'), + }, }; const scyllaEnvironmentDependentOptions = { diff --git a/db/src/index.ts b/db/src/index.ts index b240b12d6..40c254b4f 100755 --- a/db/src/index.ts +++ b/db/src/index.ts @@ -1,4 +1,4 @@ -#!/usr/bin/env -S node --loader ts-node/esm --require dotenv/config +#!/usr/bin/env -S node --env-file-if-exists=.env --loader ts-node/esm import { makeCli } from '@roostorg/db-migrator'; import apiServerPostgresConfig from './configs/api-server-pg.js'; diff --git a/db/src/scripts/api-server-pg/2026.06.30T03.50.47.allow_text_only_ncmec_reports.sql b/db/src/scripts/api-server-pg/2026.06.30T03.50.47.allow_text_only_ncmec_reports.sql new file mode 100644 index 000000000..7f2414677 --- /dev/null +++ b/db/src/scripts/api-server-pg/2026.06.30T03.50.47.allow_text_only_ncmec_reports.sql @@ -0,0 +1,30 @@ +-- The existing `reported_media_check_non_empty` CHECK on ncmec_reporting.ncmec_reports +-- requires >= 1 media item, which blocks storing legitimate text-only reports +-- even after the application-side media gates were removed (#661). +-- +-- We want to replace it with a constraint that matches the new logic. A +-- report must carry media OR messages (but there must be one). `array_length` of an +-- empty/NULL array is NULL, so coalesce to 0 before comparing. The column stays +-- `jsonb[] NOT NULL`, so rows still cannot be NULL. +-- +-- Safe for existing data: every current row satisfied the old "media non-empty" +-- constraint, so it satisfies "media OR messages non-empty". +-- +-- Rollback caveat: reverting to the media-only constraint will FAIL once any +-- text-only (empty reported_media) row exists. Reverting is only clean +-- before such rows are written; afterward it requires removing/backfilling +-- those rows, and deleting NCMEC report records has compliance implications. +-- Treat this as effectively forward-only. + +BEGIN; + +ALTER TABLE ncmec_reporting.ncmec_reports + DROP CONSTRAINT IF EXISTS reported_media_check_non_empty; + +ALTER TABLE ncmec_reporting.ncmec_reports + ADD CONSTRAINT reported_media_or_messages_non_empty CHECK ( + coalesce(array_length(reported_media, 1), 0) > 0 + OR coalesce(array_length(reported_messages, 1), 0) > 0 + ); + +COMMIT; diff --git a/db/src/scripts/api-server-pg/2026.07.07T14.27.13.add_sepia.sql b/db/src/scripts/api-server-pg/2026.07.07T14.27.13.add_sepia.sql new file mode 100644 index 000000000..51c835973 --- /dev/null +++ b/db/src/scripts/api-server-pg/2026.07.07T14.27.13.add_sepia.sql @@ -0,0 +1,5 @@ +ALTER TABLE user_management_service.user_interface_settings +ADD COLUMN moderator_safety_sepia boolean; + +ALTER TABLE user_management_service.org_default_user_interface_settings +ADD COLUMN moderator_safety_sepia boolean NOT NULL DEFAULT false; diff --git a/db/src/scripts/api-server-pg/2026.07.13T21.56.32.restrict_routing_rules_queue_fkeys.sql b/db/src/scripts/api-server-pg/2026.07.13T21.56.32.restrict_routing_rules_queue_fkeys.sql new file mode 100644 index 000000000..b1b88797c --- /dev/null +++ b/db/src/scripts/api-server-pg/2026.07.13T21.56.32.restrict_routing_rules_queue_fkeys.sql @@ -0,0 +1,18 @@ +-- Change routing_rules and appeals_routing_rules destination_queue_id FK from +-- CASCADE to RESTRICT so that deleting a queue that is still referenced by a +-- routing rule is rejected at the DB level (rather than silently cascade- +-- deleting the rule and breaking routing for the org). + +ALTER TABLE manual_review_tool.routing_rules + DROP CONSTRAINT routing_rules_destination_queue_id_fkey, + ADD CONSTRAINT routing_rules_destination_queue_id_fkey + FOREIGN KEY (destination_queue_id) + REFERENCES manual_review_tool.manual_review_queues(id) + ON DELETE RESTRICT; + +ALTER TABLE manual_review_tool.appeals_routing_rules + DROP CONSTRAINT appeals_routing_rules_destination_queue_id_fkey, + ADD CONSTRAINT appeals_routing_rules_destination_queue_id_fkey + FOREIGN KEY (destination_queue_id) + REFERENCES manual_review_tool.manual_review_queues(id) + ON DELETE RESTRICT; diff --git a/docker-compose.yaml b/docker-compose.yaml index 88b542f9f..5837d0283 100644 --- a/docker-compose.yaml +++ b/docker-compose.yaml @@ -21,7 +21,7 @@ services: # Runs all migrations from scratch, after clearing the database(s). migrations: - image: node:24.14.1-bullseye-slim + image: node:24.18.0-bullseye-slim command: bash -c 'set -e npm i && ( [ "$CI" = "true" ] && npm run db:clean -- --env staging || true ) && for db in api-server-pg scylla clickhouse; do npm run db:create -- --db "$$db" --env staging; npm run db:update -- --db "$$db" --env staging; done' @@ -41,7 +41,7 @@ services: condition: service_healthy drop_dbs: - image: node:24.14.1-bullseye-slim + image: node:24.18.0-bullseye-slim command: bash -c 'npm i && npm run db:drop -- --env staging' working_dir: /src env_file: ./.env.githubci diff --git a/docs/api/policies.md b/docs/api/policies.md index 67f8b43e1..85b33a8da 100644 --- a/docs/api/policies.md +++ b/docs/api/policies.md @@ -29,12 +29,12 @@ Authentication: `X-API-KEY` header. See [API Keys & Authentication](../developme ### Response fields -| Field | Type | Description | +| Field | Type | Description | | :-------------------- | :----- | :------------------------------------------- | ---------------------------------------------------------------- | -| `policies` | Array | All policies for your organization | -| `policies[].id` | String | Coop's unique, immutable ID for this policy | -| `policies[].name` | String | The display name you assigned to this policy | -| `policies[].parentId` | String | null | ID of the parent policy, or `null` if this is a top-level policy | +| `policies` | Array | All policies for your organization | +| `policies[].id` | String | Coop's unique, immutable ID for this policy | +| `policies[].name` | String | The display name you assigned to this policy | +| `policies[].parentId` | String | null | ID of the parent policy, or `null` if this is a top-level policy | ## Notes diff --git a/docs/development/README.md b/docs/development/README.md index 71a37e9f3..2af6f6ca7 100644 --- a/docs/development/README.md +++ b/docs/development/README.md @@ -27,9 +27,9 @@ To get Coop running: ``` 0.40.4 - Found '.nvmrc' with version <24.14.1> - v24.14.1 is already installed. - Now using node v24.14.1 (npm v11.11.0) + Found '.nvmrc' with version <24.18.0> + v24.18.0 is already installed. + Now using node v24.18.0 (npm v11.11.0) Docker version 29.4.3, build 055a478 ``` diff --git a/migrator/package-lock.json b/migrator/package-lock.json index 6998442e9..e1807b9ec 100644 --- a/migrator/package-lock.json +++ b/migrator/package-lock.json @@ -23,48 +23,44 @@ } }, "node_modules/@emnapi/core": { - "version": "1.10.0", - "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.10.0.tgz", - "integrity": "sha512-yq6OkJ4p82CAfPl0u9mQebQHKPJkY7WrIuk205cTYnYe+k2Z8YBh11FrbRG/H6ihirqcacOgl2BIO8oyMQLeXw==", + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.11.1.tgz", + "integrity": "sha512-RSvbQmHzdKzNsLYa/wHrbc3KN4sYLKAdPZxqiM2HATqv/SBk2/ENSHpvXGaLOMcsAyz0poEGqkmmKYG3OWiJEQ==", "dev": true, - "license": "MIT", "optional": true, "dependencies": { - "@emnapi/wasi-threads": "1.2.1", + "@emnapi/wasi-threads": "1.2.2", "tslib": "^2.4.0" } }, "node_modules/@emnapi/runtime": { - "version": "1.10.0", - "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.10.0.tgz", - "integrity": "sha512-ewvYlk86xUoGI0zQRNq/mC+16R1QeDlKQy21Ki3oSYXNgLb45GV1P6A0M+/s6nyCuNDqe5VpaY84BzXGwVbwFA==", + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.11.1.tgz", + "integrity": "sha512-vgj7R3y3Wgx24IQaGPA/R6YFXLHVMOZ0uVEyIQPaWs+rd1AzfEMXlAC22FYwO1XkKR6NPsq7mUandH8oIRdZFw==", "dev": true, - "license": "MIT", "optional": true, "dependencies": { "tslib": "^2.4.0" } }, "node_modules/@emnapi/wasi-threads": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.2.1.tgz", - "integrity": "sha512-uTII7OYF+/Mes/MrcIOYp5yOtSMLBWSIoLPpcgwipoiKbli6k322tcoFsxoIIxPDqW01SQGAgko4EzZi2BNv2w==", + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.2.2.tgz", + "integrity": "sha512-c95qOXkHdydNKhscBTebqEC1CVAZpyqOfVfBzQ1qgzyl3gfeldUjIggDbIZgDKsHLgnsM+igH7TJ/eAasaVuMA==", "dev": true, - "license": "MIT", "optional": true, "dependencies": { "tslib": "^2.4.0" } }, "node_modules/@napi-rs/wasm-runtime": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-1.1.4.tgz", - "integrity": "sha512-3NQNNgA1YSlJb/kMH1ildASP9HW7/7kYnRI2szWJaofaS1hWmbGI4H+d3+22aGzXXN9IJ+n+GiFVcGipJP18ow==", + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-1.1.6.tgz", + "integrity": "sha512-ZLv/JdUfkvOy9eCnnBaGfiO+XimbjebAeO+MRQqD/B+FR1tnRN0tpKSJHRbE8sFfS6aqsXZ67TQjfwfsxULVbg==", "dev": true, - "license": "MIT", "optional": true, "dependencies": { - "@tybys/wasm-util": "^0.10.1" + "@tybys/wasm-util": "^0.10.3" }, "funding": { "type": "github", @@ -75,50 +71,14 @@ "@emnapi/runtime": "^1.7.1" } }, - "node_modules/@nodelib/fs.scandir": { - "version": "2.1.5", - "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", - "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", - "license": "MIT", - "dependencies": { - "@nodelib/fs.stat": "2.0.5", - "run-parallel": "^1.1.9" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/@nodelib/fs.stat": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", - "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", - "license": "MIT", - "engines": { - "node": ">= 8" - } - }, - "node_modules/@nodelib/fs.walk": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", - "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", - "license": "MIT", - "dependencies": { - "@nodelib/fs.scandir": "2.1.5", - "fastq": "^1.6.0" - }, - "engines": { - "node": ">= 8" - } - }, "node_modules/@oxc-parser/binding-android-arm-eabi": { - "version": "0.133.0", - "resolved": "https://registry.npmjs.org/@oxc-parser/binding-android-arm-eabi/-/binding-android-arm-eabi-0.133.0.tgz", - "integrity": "sha512-l/44caGse+VpnY9gx0yvvc5QnnG3yG1FO3KZgYvNL1GZrfK86zIwAOgGEVlxDyRymzrU/KHiblPFpevKOmJmUA==", + "version": "0.137.0", + "resolved": "https://registry.npmjs.org/@oxc-parser/binding-android-arm-eabi/-/binding-android-arm-eabi-0.137.0.tgz", + "integrity": "sha512-KDs+0VPdEmasOkpuJHW9V5WCF+cvYdMQv2Jd+aJXt+cxIx12NToRQRbXaRwUEDsZw+/jMk81Ve8ZFbjUkJTOwA==", "cpu": [ "arm" ], "dev": true, - "license": "MIT", "optional": true, "os": [ "android" @@ -128,14 +88,13 @@ } }, "node_modules/@oxc-parser/binding-android-arm64": { - "version": "0.133.0", - "resolved": "https://registry.npmjs.org/@oxc-parser/binding-android-arm64/-/binding-android-arm64-0.133.0.tgz", - "integrity": "sha512-KUHmPMziLBp4u+zbrLdB7iWS7KshuZe+RAp7ELnY9SI9nNXBZ+dp8fiBqWOxhXqn+FQg3a4UcQhwmsJOKV8Jjg==", + "version": "0.137.0", + "resolved": "https://registry.npmjs.org/@oxc-parser/binding-android-arm64/-/binding-android-arm64-0.137.0.tgz", + "integrity": "sha512-WhALNzfy3x/RfC6bsqX+csavuUY0yHHE7XfgPE5M542uhoBZUUoGTPG+nkMbGoG4+gcfss5s7urMyn5QBHu0sw==", "cpu": [ "arm64" ], "dev": true, - "license": "MIT", "optional": true, "os": [ "android" @@ -145,14 +104,13 @@ } }, "node_modules/@oxc-parser/binding-darwin-arm64": { - "version": "0.133.0", - "resolved": "https://registry.npmjs.org/@oxc-parser/binding-darwin-arm64/-/binding-darwin-arm64-0.133.0.tgz", - "integrity": "sha512-q8dWmnU/8ea2tga9w2f1PinQ5rcMPDUGkF64T189b65YMjUomET4oy5oRldOr4AwOQkneOG/Zttnz1Dvrc62wg==", + "version": "0.137.0", + "resolved": "https://registry.npmjs.org/@oxc-parser/binding-darwin-arm64/-/binding-darwin-arm64-0.137.0.tgz", + "integrity": "sha512-bFPr5hgmNMOMoyPTGtdsK4Ug21RovIPojRMgDDhSp1LtCnc/DkLwGONKjgRjszg677RlGnkYSviQ8hHaUPOVYA==", "cpu": [ "arm64" ], "dev": true, - "license": "MIT", "optional": true, "os": [ "darwin" @@ -162,14 +120,13 @@ } }, "node_modules/@oxc-parser/binding-darwin-x64": { - "version": "0.133.0", - "resolved": "https://registry.npmjs.org/@oxc-parser/binding-darwin-x64/-/binding-darwin-x64-0.133.0.tgz", - "integrity": "sha512-cOKeIELIB2bJnCKwqx4Rdj+1Lss/U6uCbLxRySZrhyOOQa1flKhwZFjEHRHxk8fU1NKmhK5OnTdPQ4CpjuFuVw==", + "version": "0.137.0", + "resolved": "https://registry.npmjs.org/@oxc-parser/binding-darwin-x64/-/binding-darwin-x64-0.137.0.tgz", + "integrity": "sha512-CL5dMm1asqXIDZHg14FLxj3Mc36w8PI7xCWh1uA4is6z8g2XrIILoTcQYOxDbwzuk34RDPX5IAGUxZr6LA9KAg==", "cpu": [ "x64" ], "dev": true, - "license": "MIT", "optional": true, "os": [ "darwin" @@ -179,14 +136,13 @@ } }, "node_modules/@oxc-parser/binding-freebsd-x64": { - "version": "0.133.0", - "resolved": "https://registry.npmjs.org/@oxc-parser/binding-freebsd-x64/-/binding-freebsd-x64-0.133.0.tgz", - "integrity": "sha512-OpaSv4pW3KgFrMYQxTaS0aOE4T1DQF3qZE/4B6uqqv1KgPWWd4UQhJALi8PJPX1RRV5K7ThKXRfF7qGg2+3l1A==", + "version": "0.137.0", + "resolved": "https://registry.npmjs.org/@oxc-parser/binding-freebsd-x64/-/binding-freebsd-x64-0.137.0.tgz", + "integrity": "sha512-79h8rYGnSlKPGWo7mHr2ixO6ea7aW8B0CT965SZ8SLbNnCOH5aOYBTeVXUY6eMvEaiLyWr8Skuiugr5pDYgLGw==", "cpu": [ "x64" ], "dev": true, - "license": "MIT", "optional": true, "os": [ "freebsd" @@ -196,14 +152,13 @@ } }, "node_modules/@oxc-parser/binding-linux-arm-gnueabihf": { - "version": "0.133.0", - "resolved": "https://registry.npmjs.org/@oxc-parser/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-0.133.0.tgz", - "integrity": "sha512-JGK1wlGrGwxBIlVSF7KWTX1/ru6BEtf28fRROztDRkLfiW+Kxa4onnriezMIiogfn9hVw2KzYcKiLjkLR2ns8A==", + "version": "0.137.0", + "resolved": "https://registry.npmjs.org/@oxc-parser/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-0.137.0.tgz", + "integrity": "sha512-ASgmlSimhGyr0lksgVIo6hibz1obnDq4qJbiMX/AzltfgPnanRrzG1Q+23g8ljOHOjv6dsznkUuCYL3gg0sY1Q==", "cpu": [ "arm" ], "dev": true, - "license": "MIT", "optional": true, "os": [ "linux" @@ -213,14 +168,13 @@ } }, "node_modules/@oxc-parser/binding-linux-arm-musleabihf": { - "version": "0.133.0", - "resolved": "https://registry.npmjs.org/@oxc-parser/binding-linux-arm-musleabihf/-/binding-linux-arm-musleabihf-0.133.0.tgz", - "integrity": "sha512-yuZO533Ftonxn/iyoqQzURzLQHMspvsIyfiCSNi1t/ER4eIQaR0SsmUOUm5b/lmSig7IWIUa5/BrbEkAPwcilQ==", + "version": "0.137.0", + "resolved": "https://registry.npmjs.org/@oxc-parser/binding-linux-arm-musleabihf/-/binding-linux-arm-musleabihf-0.137.0.tgz", + "integrity": "sha512-AU2J9aa22Sx32wRGnDjybOU9TQXXQUud5sdUi+ZB0XxwM8aToWLweV+yA0wlQm0yIUVqljquqoHCYEq9II8gJQ==", "cpu": [ "arm" ], "dev": true, - "license": "MIT", "optional": true, "os": [ "linux" @@ -230,17 +184,13 @@ } }, "node_modules/@oxc-parser/binding-linux-arm64-gnu": { - "version": "0.133.0", - "resolved": "https://registry.npmjs.org/@oxc-parser/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-0.133.0.tgz", - "integrity": "sha512-hvpbqT5pN2rR+3+xtWeizwfR/aZ0vGceg6TqYMl+ToxMpk9/tmnX7kSvQnfEUkoua8mhogzvIKsAkn0wxgblBA==", + "version": "0.137.0", + "resolved": "https://registry.npmjs.org/@oxc-parser/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-0.137.0.tgz", + "integrity": "sha512-GdEtiG89yMr7XkUGxifgodXEEm2f+xW2f9CpDjlgAnBOwhTmrpQMvhOGobLVKUyzf/qHBXW16smk5zbF3nZU6w==", "cpu": [ "arm64" ], "dev": true, - "libc": [ - "glibc" - ], - "license": "MIT", "optional": true, "os": [ "linux" @@ -250,17 +200,13 @@ } }, "node_modules/@oxc-parser/binding-linux-arm64-musl": { - "version": "0.133.0", - "resolved": "https://registry.npmjs.org/@oxc-parser/binding-linux-arm64-musl/-/binding-linux-arm64-musl-0.133.0.tgz", - "integrity": "sha512-wJQGamIosQBoJHW9+S5XxrtKRo3eyJxsnS1XCPrqN0LHi8uw1pTqqTfn3t/NVuvbBg7Pumn4ez9Eidgcn0xbEg==", + "version": "0.137.0", + "resolved": "https://registry.npmjs.org/@oxc-parser/binding-linux-arm64-musl/-/binding-linux-arm64-musl-0.137.0.tgz", + "integrity": "sha512-EGJ+Bs8iXx8KBH8DQ5BLoEm5lnHaYjlh4/8j8vFhrr/6z4tqONy5BZDzLpKmmNWlN6Hlc5r8YOuBVHqZ9vRFEQ==", "cpu": [ "arm64" ], "dev": true, - "libc": [ - "musl" - ], - "license": "MIT", "optional": true, "os": [ "linux" @@ -270,17 +216,13 @@ } }, "node_modules/@oxc-parser/binding-linux-ppc64-gnu": { - "version": "0.133.0", - "resolved": "https://registry.npmjs.org/@oxc-parser/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-0.133.0.tgz", - "integrity": "sha512-Koaz32/O5+abIfrNGdyndgRvdOZ9jEf5/z3Ep9h3h2QWpdDiUQpVwgH0OcMXCs+l9aXxPLtkupqyVig9W6FDKw==", + "version": "0.137.0", + "resolved": "https://registry.npmjs.org/@oxc-parser/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-0.137.0.tgz", + "integrity": "sha512-vzFUQENy/fnbSe5DZWovq6tIBc1uhuMztanSW6rz1e9WdQE4gHwYuD7ZII6JnrJifd1R3RSoqiZbgRFlVL2tYQ==", "cpu": [ "ppc64" ], "dev": true, - "libc": [ - "glibc" - ], - "license": "MIT", "optional": true, "os": [ "linux" @@ -290,17 +232,13 @@ } }, "node_modules/@oxc-parser/binding-linux-riscv64-gnu": { - "version": "0.133.0", - "resolved": "https://registry.npmjs.org/@oxc-parser/binding-linux-riscv64-gnu/-/binding-linux-riscv64-gnu-0.133.0.tgz", - "integrity": "sha512-R4vOjWzxhnNWHnVLeiB6jNuIifdy9vcMXZGPc7StXcxBovI+U2zg1QhZ9o8OjV80oGivs1lX5NfPLzk4IPqlRA==", + "version": "0.137.0", + "resolved": "https://registry.npmjs.org/@oxc-parser/binding-linux-riscv64-gnu/-/binding-linux-riscv64-gnu-0.137.0.tgz", + "integrity": "sha512-SfVI14HBQs9gtLcUD5hTt5hsNbdrqSUNg9S8muN+LhVQ5nf1WwH3hAoK6B9NKgdYgWAQSXFXGiiBedQ4r/BKuw==", "cpu": [ "riscv64" ], "dev": true, - "libc": [ - "glibc" - ], - "license": "MIT", "optional": true, "os": [ "linux" @@ -310,17 +248,13 @@ } }, "node_modules/@oxc-parser/binding-linux-riscv64-musl": { - "version": "0.133.0", - "resolved": "https://registry.npmjs.org/@oxc-parser/binding-linux-riscv64-musl/-/binding-linux-riscv64-musl-0.133.0.tgz", - "integrity": "sha512-iwgBNUTHiMdxARLYuM0SBlnYeb19iw1Ea5M+4ERZupCsBMLArti6FyZ6UfFjJxIiTDr2oW2DGQFxlQVQ/dW9rA==", + "version": "0.137.0", + "resolved": "https://registry.npmjs.org/@oxc-parser/binding-linux-riscv64-musl/-/binding-linux-riscv64-musl-0.137.0.tgz", + "integrity": "sha512-e7Ppy4FCIFNQxT/ikSeIWFoQ0l+N9vgtRBtLcyZXeolTzApyVoPqEXsYPrcdM/9i0Bwk8knvYd37vaEMxHyi6g==", "cpu": [ "riscv64" ], "dev": true, - "libc": [ - "musl" - ], - "license": "MIT", "optional": true, "os": [ "linux" @@ -330,17 +264,13 @@ } }, "node_modules/@oxc-parser/binding-linux-s390x-gnu": { - "version": "0.133.0", - "resolved": "https://registry.npmjs.org/@oxc-parser/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-0.133.0.tgz", - "integrity": "sha512-ZwZNo8FZmB/gVfboQl+wXilBigGl+6nQQs+nITOeAP/HcAOjiHl6XZJL9F/KXNEspODQcbjAiyjUbeCJd9a0fA==", + "version": "0.137.0", + "resolved": "https://registry.npmjs.org/@oxc-parser/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-0.137.0.tgz", + "integrity": "sha512-Bho5qFwdhqsIFR7gipYEUlqvi3SRrY8sugxXig380MIaakBB1PyU9+7dBiBVScfImTNWhijUxdBwqrprGdq5WA==", "cpu": [ "s390x" ], "dev": true, - "libc": [ - "glibc" - ], - "license": "MIT", "optional": true, "os": [ "linux" @@ -350,17 +280,13 @@ } }, "node_modules/@oxc-parser/binding-linux-x64-gnu": { - "version": "0.133.0", - "resolved": "https://registry.npmjs.org/@oxc-parser/binding-linux-x64-gnu/-/binding-linux-x64-gnu-0.133.0.tgz", - "integrity": "sha512-govCvWx1dBlED3uu4qXctxpRcouu9I8Kn+DBktGCl760JtlGJzc9l/OmPJKlYWSbrRqKkMZehNeZ/4Wfma7uSA==", + "version": "0.137.0", + "resolved": "https://registry.npmjs.org/@oxc-parser/binding-linux-x64-gnu/-/binding-linux-x64-gnu-0.137.0.tgz", + "integrity": "sha512-36mGWtg7PyFzjJwGDkH6/F4o2nIDEoKXLPr/X/lwqklkomQwJJt1I5GJVmGhovUEmgPK5WAeAZMqlFCehwiy9Q==", "cpu": [ "x64" ], "dev": true, - "libc": [ - "glibc" - ], - "license": "MIT", "optional": true, "os": [ "linux" @@ -370,17 +296,13 @@ } }, "node_modules/@oxc-parser/binding-linux-x64-musl": { - "version": "0.133.0", - "resolved": "https://registry.npmjs.org/@oxc-parser/binding-linux-x64-musl/-/binding-linux-x64-musl-0.133.0.tgz", - "integrity": "sha512-ssTlpXD5Mq9uCssDJPzlRWqBt4Y7Zzd9i+XZhWmK/9Y6KUIuAxVYTYiI8lxcGWi0+3/Cz4A8q9UrD4NK9Y2j7g==", + "version": "0.137.0", + "resolved": "https://registry.npmjs.org/@oxc-parser/binding-linux-x64-musl/-/binding-linux-x64-musl-0.137.0.tgz", + "integrity": "sha512-/Jqx6+N7A44n2BdvUr7pXhVr2vFjs6WGH3unZRczwrfiH0H1zY0QwKQMG/dtRiTlKGDKGukznPT8lx84/oEsZg==", "cpu": [ "x64" ], "dev": true, - "libc": [ - "musl" - ], - "license": "MIT", "optional": true, "os": [ "linux" @@ -390,14 +312,13 @@ } }, "node_modules/@oxc-parser/binding-openharmony-arm64": { - "version": "0.133.0", - "resolved": "https://registry.npmjs.org/@oxc-parser/binding-openharmony-arm64/-/binding-openharmony-arm64-0.133.0.tgz", - "integrity": "sha512-51aByfXhPtLEdWG4a2Ihdw6cPWV1ei1AarALpFdDP8MLWDLE2NuUMgbo3DERR2Kt8fT/ok1GUvBiLxVGke9uUQ==", + "version": "0.137.0", + "resolved": "https://registry.npmjs.org/@oxc-parser/binding-openharmony-arm64/-/binding-openharmony-arm64-0.137.0.tgz", + "integrity": "sha512-9Uj0qHNNl+OgT1UTGwF7ixIXU6T1u2SbMidmgPy/h1h/fl2gRS6YpAxxY1gwHofcWjoTwkoMFd8xs5Vuj6GOFA==", "cpu": [ "arm64" ], "dev": true, - "license": "MIT", "optional": true, "os": [ "openharmony" @@ -407,33 +328,31 @@ } }, "node_modules/@oxc-parser/binding-wasm32-wasi": { - "version": "0.133.0", - "resolved": "https://registry.npmjs.org/@oxc-parser/binding-wasm32-wasi/-/binding-wasm32-wasi-0.133.0.tgz", - "integrity": "sha512-2e16tkKp+wDO2GTAmXfxbBcCmGEaFPIJEIRBBmVKNVXSc8/fJsSIaBGyFTPHM9ST5GNWgJcYIt94rDTks+PLwA==", + "version": "0.137.0", + "resolved": "https://registry.npmjs.org/@oxc-parser/binding-wasm32-wasi/-/binding-wasm32-wasi-0.137.0.tgz", + "integrity": "sha512-gW2vfkytNGgMVADiuzdvOfw0mWG9za20F/1fCJsif5aBMAvWJTSbpIXbIe0XkOe0VENk+PadpQ7cZgUy2sUJcA==", "cpu": [ "wasm32" ], "dev": true, - "license": "MIT", "optional": true, "dependencies": { - "@emnapi/core": "1.10.0", - "@emnapi/runtime": "1.10.0", - "@napi-rs/wasm-runtime": "^1.1.4" + "@emnapi/core": "1.11.1", + "@emnapi/runtime": "1.11.1", + "@napi-rs/wasm-runtime": "^1.1.5" }, "engines": { "node": "^20.19.0 || >=22.12.0" } }, "node_modules/@oxc-parser/binding-win32-arm64-msvc": { - "version": "0.133.0", - "resolved": "https://registry.npmjs.org/@oxc-parser/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-0.133.0.tgz", - "integrity": "sha512-KPTNDKbxH1cglrqTyVeXHb4Pk4oksz8EcE1/v8zqU7N4UXbiHfA/IwtXZ2U77fnRAWBbgVkl/lZbL7o3hRdejg==", + "version": "0.137.0", + "resolved": "https://registry.npmjs.org/@oxc-parser/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-0.137.0.tgz", + "integrity": "sha512-x+pFANF0yL5uK/6T7lu6SlR5qid6sp//eZXKLq5iNsIE+EQg6EaS8/wsW7E91nXXjpnPhSoMOHXShSVhGRdn8w==", "cpu": [ "arm64" ], "dev": true, - "license": "MIT", "optional": true, "os": [ "win32" @@ -443,14 +362,13 @@ } }, "node_modules/@oxc-parser/binding-win32-ia32-msvc": { - "version": "0.133.0", - "resolved": "https://registry.npmjs.org/@oxc-parser/binding-win32-ia32-msvc/-/binding-win32-ia32-msvc-0.133.0.tgz", - "integrity": "sha512-Una1bNYv9zCavQrfnDR9wuZVB3itLjCEH4Oz7i6CwAJN/Xq9b+zbbcxmvdkKvvJt4Ngc/MBmIYlbLo3zS4TQ0A==", + "version": "0.137.0", + "resolved": "https://registry.npmjs.org/@oxc-parser/binding-win32-ia32-msvc/-/binding-win32-ia32-msvc-0.137.0.tgz", + "integrity": "sha512-sQUqym80PFi6McRsIqfJrSu2JrSClEZIXXD+/FjAFoULEKzOPsldIdFBG96xdX8aVMzCNQ9792FPx3MfkEIrFA==", "cpu": [ "ia32" ], "dev": true, - "license": "MIT", "optional": true, "os": [ "win32" @@ -460,14 +378,13 @@ } }, "node_modules/@oxc-parser/binding-win32-x64-msvc": { - "version": "0.133.0", - "resolved": "https://registry.npmjs.org/@oxc-parser/binding-win32-x64-msvc/-/binding-win32-x64-msvc-0.133.0.tgz", - "integrity": "sha512-kjBhCiOGSYTwDJQuuZa7a94JbP8htWu7J0X1KwH74kV2K5eYf6eyJRYmkpCDvr0XEL8tMxYI4WU1VekblFCLgg==", + "version": "0.137.0", + "resolved": "https://registry.npmjs.org/@oxc-parser/binding-win32-x64-msvc/-/binding-win32-x64-msvc-0.137.0.tgz", + "integrity": "sha512-2AsevxlvNN4WKxpEn3RtqD5zbqMaXF+T7JXblsP4gVuY+vC9dXS4ED/PwfRCliFqoeisYS3Iro4DHzxr0TEvVA==", "cpu": [ "x64" ], "dev": true, - "license": "MIT", "optional": true, "os": [ "win32" @@ -477,324 +394,298 @@ } }, "node_modules/@oxc-project/types": { - "version": "0.133.0", - "resolved": "https://registry.npmjs.org/@oxc-project/types/-/types-0.133.0.tgz", - "integrity": "sha512-KzkdCd6Uxqnf6l3HOw1xfatAlUURA0g14cvBYFyJ5SaNOQbOUvBr9PKArcPcrNIeRsBdgcUzOGrhKveVpvOIGA==", + "version": "0.137.0", + "resolved": "https://registry.npmjs.org/@oxc-project/types/-/types-0.137.0.tgz", + "integrity": "sha512-WT+Gb24i8hmvo85AIv2oEYouEXkRlKAlT9WaCa3TfLgNCN+GhrJOGZuIlMouAh38Qe4QOx26eUOVsq70qXrywA==", "dev": true, - "license": "MIT", "funding": { "url": "https://github.com/sponsors/Boshen" } }, "node_modules/@oxc-resolver/binding-android-arm-eabi": { - "version": "11.20.0", - "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-android-arm-eabi/-/binding-android-arm-eabi-11.20.0.tgz", - "integrity": "sha512-IjfWOXRgJFNdORDl+Uf1aibNgZY2guOD3zmOhx1BGVb/MIiqlFTdmjpQNplSN58lhWehnX4UNqC3QwpUo8pjJg==", + "version": "11.21.3", + "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-android-arm-eabi/-/binding-android-arm-eabi-11.21.3.tgz", + "integrity": "sha512-eNU11A2WNizh04v3uyaJCootrHIaS0B9aHYXvAvVnPNk4xYSjMUjHnhQ6dewPN2MRYDskV85d1N0Aw0WNWhcyg==", "cpu": [ "arm" ], "dev": true, - "license": "MIT", "optional": true, "os": [ "android" ] }, "node_modules/@oxc-resolver/binding-android-arm64": { - "version": "11.20.0", - "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-android-arm64/-/binding-android-arm64-11.20.0.tgz", - "integrity": "sha512-QqslZAuFQG8Q9xm7JuIn8JUbvywhSBMVhuQHtYW+auirZJloS41oxUUaBXk7uUhZJgp44c5zQLeVvmFaDQB+2Q==", + "version": "11.21.3", + "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-android-arm64/-/binding-android-arm64-11.21.3.tgz", + "integrity": "sha512-8Q+ZjTLvn2dIcWsrmhdrEihm7q+ag/k+mkry7Z+t0QbbHaVxXQfvH9AewyVMh/WrpEKhQ3DDgx9fYbqeCpeOEw==", "cpu": [ "arm64" ], "dev": true, - "license": "MIT", "optional": true, "os": [ "android" ] }, "node_modules/@oxc-resolver/binding-darwin-arm64": { - "version": "11.20.0", - "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-darwin-arm64/-/binding-darwin-arm64-11.20.0.tgz", - "integrity": "sha512-MUcavykj2ewlR+kc5arpg4tC2RvzJkUxWtNv74pf7lcNk00GpIpN43vXMj+j6r4eMmfZhlb8hueKoIb8e9kAGQ==", + "version": "11.21.3", + "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-darwin-arm64/-/binding-darwin-arm64-11.21.3.tgz", + "integrity": "sha512-wkh0qKZGHXVUDxFw3oA1TXnU2BDYY/r775oJflGeIr8uDPPoN2pk8gijQIzYRT6hoql/lg3+Tx/SaTn9e2/aGg==", "cpu": [ "arm64" ], "dev": true, - "license": "MIT", "optional": true, "os": [ "darwin" ] }, "node_modules/@oxc-resolver/binding-darwin-x64": { - "version": "11.20.0", - "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-darwin-x64/-/binding-darwin-x64-11.20.0.tgz", - "integrity": "sha512-BGB16nRUK5Etiv//ihPyzj8Lj1px0mhh4YIfe0FDf045ywknfSm0GEbiRESpr6Q4K82AvnyaRIhhluHByvS4bg==", + "version": "11.21.3", + "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-darwin-x64/-/binding-darwin-x64-11.21.3.tgz", + "integrity": "sha512-HbNc23FAQYbuyDV2vBWMez4u4mrsm5RAkniGZAWqr6lYZ3N4beeqIb776jzwRl8qL2zRhHVXpUj97X0QgogVzg==", "cpu": [ "x64" ], "dev": true, - "license": "MIT", "optional": true, "os": [ "darwin" ] }, "node_modules/@oxc-resolver/binding-freebsd-x64": { - "version": "11.20.0", - "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-freebsd-x64/-/binding-freebsd-x64-11.20.0.tgz", - "integrity": "sha512-JZgtePaqj3qmD5XFHJaSLWzHRxQu0LaPkdoM1KJXYADvAaa83ijXHclV3ej3CueeW0wxfIAbGCZVP45J0CA7uQ==", + "version": "11.21.3", + "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-freebsd-x64/-/binding-freebsd-x64-11.21.3.tgz", + "integrity": "sha512-K6xNsTUPEUdfrn0+kbMq5nOUB5w1C5pavPQngt4TM2FpN91lP0PBe2srSpamb4d69O7h86oAi/qWX/kZNRSjkw==", "cpu": [ "x64" ], "dev": true, - "license": "MIT", "optional": true, "os": [ "freebsd" ] }, "node_modules/@oxc-resolver/binding-linux-arm-gnueabihf": { - "version": "11.20.0", - "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-11.20.0.tgz", - "integrity": "sha512-hOQ/p3ry3v3SchUBXicrrnszaI/UmYzM4wtS4RGfwgVUX7a+HbyQSzJ5aOzu+o6XZkFkS3ZXN4PZAzhOb77OSg==", + "version": "11.21.3", + "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-11.21.3.tgz", + "integrity": "sha512-VcFmOpcpWX1zoEy8M58tR2M9YxM+Z9RuQhqAx5q0CTmrruaP7Gveejg75hzd/5sg5nk9G3aLALEa3hE2FsmmTQ==", "cpu": [ "arm" ], "dev": true, - "license": "MIT", "optional": true, "os": [ "linux" ] }, "node_modules/@oxc-resolver/binding-linux-arm-musleabihf": { - "version": "11.20.0", - "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-linux-arm-musleabihf/-/binding-linux-arm-musleabihf-11.20.0.tgz", - "integrity": "sha512-2ArPksaw0AqeuGBfoS715VF+JvJQAhD2niWgjE5hVO+L+nAfikVQopvngCMX9x4BD8itWoQ3dnikrQyl5Ho5Jg==", + "version": "11.21.3", + "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-linux-arm-musleabihf/-/binding-linux-arm-musleabihf-11.21.3.tgz", + "integrity": "sha512-quVoxFLBy43hWaQbbDtQNRwAX5vX76mv7n64icAtQcJ3eNgVeblqmkupF/hAneNthdqSlnd1sTjb3aQSaDPaCQ==", "cpu": [ "arm" ], "dev": true, - "license": "MIT", "optional": true, "os": [ "linux" ] }, "node_modules/@oxc-resolver/binding-linux-arm64-gnu": { - "version": "11.20.0", - "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-11.20.0.tgz", - "integrity": "sha512-0bJnmYFp62JdZ4nVMDUZ/C58BCZOCcqgKtnUlp7L9Ojf/czIN+3j72YlLPeWLkzlr6SlYvIQA4SGV/HyO0d+qg==", + "version": "11.21.3", + "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-11.21.3.tgz", + "integrity": "sha512-X0AqNZgcD07Q4V3RDK18/vYOj/HQT/FnmEFGYS2jTWqY7JO13ryE3TEs3eAIgUJhBnNkpEaiXqz3VK8M7qQhWQ==", "cpu": [ "arm64" ], "dev": true, - "libc": [ - "glibc" - ], - "license": "MIT", "optional": true, "os": [ "linux" ] }, "node_modules/@oxc-resolver/binding-linux-arm64-musl": { - "version": "11.20.0", - "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-linux-arm64-musl/-/binding-linux-arm64-musl-11.20.0.tgz", - "integrity": "sha512-wKHHzPKZo7Ufhv/Bt6yxT7FOgnIgW4gwXcJUipkShGp68W3wGVqvr1Sr0fY65lN0Oy6y41+g2kIDvkgZaMMUkw==", + "version": "11.21.3", + "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-linux-arm64-musl/-/binding-linux-arm64-musl-11.21.3.tgz", + "integrity": "sha512-YkaQnaKYdbuaXvRt5Qd0GpbihzVnyfR6z1SpYfIUC6RTu4NF7lDKPjVkYb+jRI2gedVO2rVpN35Y6akG6ud4Lw==", "cpu": [ "arm64" ], "dev": true, - "libc": [ - "musl" - ], - "license": "MIT", "optional": true, "os": [ "linux" ] }, "node_modules/@oxc-resolver/binding-linux-ppc64-gnu": { - "version": "11.20.0", - "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-11.20.0.tgz", - "integrity": "sha512-RN8goF7Ie0B79L4i4G6OeBocTgSC56vJbQ65VJje+oXnldVpLnOU7j/AQ/dP94TcCS+Yh6WG8u3Qt4ETteXFNQ==", + "version": "11.21.3", + "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-11.21.3.tgz", + "integrity": "sha512-gB9HwhrPiFqUzDeEq+y/CgAijz1YdI6BnXz5GaH2Pa9cWdutchlkGFAiAuGb/PjVQpiK6NFKzFuztxrweoit7A==", "cpu": [ "ppc64" ], "dev": true, - "libc": [ - "glibc" - ], - "license": "MIT", "optional": true, "os": [ "linux" ] }, "node_modules/@oxc-resolver/binding-linux-riscv64-gnu": { - "version": "11.20.0", - "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-linux-riscv64-gnu/-/binding-linux-riscv64-gnu-11.20.0.tgz", - "integrity": "sha512-5l1yU6/xQEqLZRzxqmMxJfWPslpwCmBsdDGaBvABPehxquCXDC7dd7oraNdKSJUMDXSM7VvVj8H2D2FTjU7oWw==", + "version": "11.21.3", + "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-linux-riscv64-gnu/-/binding-linux-riscv64-gnu-11.21.3.tgz", + "integrity": "sha512-zjDWBlYk8QGv0H8dsPUWqkfjYIIjG2TvspGkzXL0eImbgxtZorA/klKeHyolevoT3Kvbi+1iMr9Lhrh7jf54Og==", "cpu": [ "riscv64" ], "dev": true, - "libc": [ - "glibc" - ], - "license": "MIT", "optional": true, "os": [ "linux" ] }, "node_modules/@oxc-resolver/binding-linux-riscv64-musl": { - "version": "11.20.0", - "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-linux-riscv64-musl/-/binding-linux-riscv64-musl-11.20.0.tgz", - "integrity": "sha512-xHEvkbgz6UC+A3JOyDQy76LkUaxsNSfIr3/GV8slwZsnuooJiIB34gzJfsyvR4JdCYNUUPsRJc/w/oWkODu+hg==", + "version": "11.21.3", + "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-linux-riscv64-musl/-/binding-linux-riscv64-musl-11.21.3.tgz", + "integrity": "sha512-4UfsQvacV388y1zpXL7C1x1FNYaV52JtuNRiuzrfQA2z1z6ElVrsidkGsrvQ5EgeSq1Pj7kaKqrgGkvFuxJ/tw==", "cpu": [ "riscv64" ], "dev": true, - "libc": [ - "musl" - ], - "license": "MIT", "optional": true, "os": [ "linux" ] }, "node_modules/@oxc-resolver/binding-linux-s390x-gnu": { - "version": "11.20.0", - "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-11.20.0.tgz", - "integrity": "sha512-aWPDUUmSeyHvlW+SoEUd+JIJsQhVhu6a5tBpDRMu058naPAchTgAVGCFy35zjbnFlt0i8hLWziff6HX0D3LU4g==", + "version": "11.21.3", + "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-11.21.3.tgz", + "integrity": "sha512-b5uH+HKH0MP5mNBYaK75SKsJbw52URqrx2LavYdq6wb0l3ExAG5niYRP9DWUNHdKilpaBVM2bXk9HNWrH3ew7Q==", "cpu": [ "s390x" ], "dev": true, - "libc": [ - "glibc" - ], - "license": "MIT", "optional": true, "os": [ "linux" ] }, "node_modules/@oxc-resolver/binding-linux-x64-gnu": { - "version": "11.20.0", - "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-linux-x64-gnu/-/binding-linux-x64-gnu-11.20.0.tgz", - "integrity": "sha512-x2YeSimvhJjKLVD8KSu8f/rqU1potcdEMkApIPJqjZWN7c2Fpt4g2X32WDg1p+XDAmyT7nuQGe0vnhvXeLbH+g==", + "version": "11.21.3", + "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-linux-x64-gnu/-/binding-linux-x64-gnu-11.21.3.tgz", + "integrity": "sha512-PjYlmilBpNRh2ntXNYAK3Am5w/nPfEpnU/96iNx7CI8EzAn12J4JRiec63wHJTH31nLoCNxBg/829pN+3CfG3Q==", "cpu": [ "x64" ], "dev": true, - "libc": [ - "glibc" - ], - "license": "MIT", "optional": true, "os": [ "linux" ] }, "node_modules/@oxc-resolver/binding-linux-x64-musl": { - "version": "11.20.0", - "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-linux-x64-musl/-/binding-linux-x64-musl-11.20.0.tgz", - "integrity": "sha512-kcRLEIxpZefeYfLChjpgFf3ilBzRDZ+yobMrpRsQlSrxuFGtm3U6PMU7AaEpMqo3NfDGVyJJseAjnRLzMFHjwQ==", + "version": "11.21.3", + "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-linux-x64-musl/-/binding-linux-x64-musl-11.21.3.tgz", + "integrity": "sha512-QTBAb7JuHlZ7JUEyM8UiQi2f7m/L4swBhP2TNpYIDc9Wp/wRw1G/8sl6i13aIzQAXH7LKIm294LeOHd0lQR8zA==", "cpu": [ "x64" ], "dev": true, - "libc": [ - "musl" - ], - "license": "MIT", "optional": true, "os": [ "linux" ] }, "node_modules/@oxc-resolver/binding-openharmony-arm64": { - "version": "11.20.0", - "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-openharmony-arm64/-/binding-openharmony-arm64-11.20.0.tgz", - "integrity": "sha512-HHcfnApSZGtKhTiHqe8OZruOZe5XuFQH5/E0Yhj3u8fnFvzkM4/k6WjacUf4SvA0SPEAbfbgYmVPuo0VX/fIBQ==", + "version": "11.21.3", + "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-openharmony-arm64/-/binding-openharmony-arm64-11.21.3.tgz", + "integrity": "sha512-4j1DFwjwv36ec9kds0jU/ucQ5Ha4ERO/H95BxR5JFf0kqUUAJ1kwII7XhTc1vZrkdJkvLGC9Q2MbpObpum8RBg==", "cpu": [ "arm64" ], "dev": true, - "license": "MIT", "optional": true, "os": [ "openharmony" ] }, "node_modules/@oxc-resolver/binding-wasm32-wasi": { - "version": "11.20.0", - "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-wasm32-wasi/-/binding-wasm32-wasi-11.20.0.tgz", - "integrity": "sha512-Tn0y1XOFYHNfK1wp1Z5QK8Rcld/bsOwRISQXfqAZ5IBpv8Gz1IvV39fUWNprqNdRizgcvFhOzWwFun2zkJsyBg==", + "version": "11.21.3", + "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-wasm32-wasi/-/binding-wasm32-wasi-11.21.3.tgz", + "integrity": "sha512-i8oluoel5kru/j1WNrjmQSiA3GQ7wvIYVR1IwIoZtKogAhya2iub+ZKIeSIkcJOrnzQ18Tzl/F+kL3fYOxZLvA==", "cpu": [ "wasm32" ], "dev": true, - "license": "MIT", "optional": true, "dependencies": { - "@emnapi/core": "1.10.0", - "@emnapi/runtime": "1.10.0", - "@napi-rs/wasm-runtime": "^1.1.4" + "@emnapi/core": "1.11.0", + "@emnapi/runtime": "1.11.0", + "@napi-rs/wasm-runtime": "^1.1.5" }, "engines": { "node": ">=14.0.0" } }, + "node_modules/@oxc-resolver/binding-wasm32-wasi/node_modules/@emnapi/core": { + "version": "1.11.0", + "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.11.0.tgz", + "integrity": "sha512-l9Oo58x0HOP5znGzVhYW9U3e5wVuA4LAZU2AGezTmkhO1CgQRFDhDg4nneHsu/t3WniXg9QrG2nIXL/ZS8ln8Q==", + "dev": true, + "optional": true, + "dependencies": { + "@emnapi/wasi-threads": "1.2.2", + "tslib": "^2.4.0" + } + }, + "node_modules/@oxc-resolver/binding-wasm32-wasi/node_modules/@emnapi/runtime": { + "version": "1.11.0", + "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.11.0.tgz", + "integrity": "sha512-55coeOFKHv1ywEcUXJtWU5f+Jr/W5tZDvZig8DLKSwUN1JpROQ4rk/SNOQiFWmaR/VKF4zuFyW1B8JduOSv6Pg==", + "dev": true, + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, "node_modules/@oxc-resolver/binding-win32-arm64-msvc": { - "version": "11.20.0", - "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-11.20.0.tgz", - "integrity": "sha512-qPi25YNPe4YenS8MgsQU2+bIFHxxpLx1LVna2444cEHqNPhNjvWf9zqj4aWE43H9LpAsTmkkAlA3eL5ElBU3mA==", + "version": "11.21.3", + "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-11.21.3.tgz", + "integrity": "sha512-M/8dw8dD6aOs+NlPJax401CZB9I7Aut84isQLgALGGwke4Afvw+/7yYhZb94yXf6t2sPLhQLmSmtSV+2FhsOWg==", "cpu": [ "arm64" ], "dev": true, - "license": "MIT", "optional": true, "os": [ "win32" ] }, "node_modules/@oxc-resolver/binding-win32-x64-msvc": { - "version": "11.20.0", - "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-win32-x64-msvc/-/binding-win32-x64-msvc-11.20.0.tgz", - "integrity": "sha512-Wb14jWEW8huH6It9F6sXd9vrYmIS7pMrgkU6sxpLxkP+9z+wRgs71hUEhRpcn8FOXAFa27FVWfY2tRpbfTzfLw==", + "version": "11.21.3", + "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-win32-x64-msvc/-/binding-win32-x64-msvc-11.21.3.tgz", + "integrity": "sha512-H7BCt/VnS9hnmMp42eGhZ99izSCRvlnWwy/N71K1/J8QoExwY4262Z8QiEkMDtduRJrztayDxETTckmUuAVL9Q==", "cpu": [ "x64" ], "dev": true, - "license": "MIT", "optional": true, "os": [ "win32" ] }, "node_modules/@rushstack/node-core-library": { - "version": "5.13.0", - "resolved": "https://registry.npmjs.org/@rushstack/node-core-library/-/node-core-library-5.13.0.tgz", - "integrity": "sha512-IGVhy+JgUacAdCGXKUrRhwHMTzqhWwZUI+qEPcdzsb80heOw0QPbhhoVsoiMF7Klp8eYsp7hzpScMXmOa3Uhfg==", - "license": "MIT", + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/@rushstack/node-core-library/-/node-core-library-4.0.2.tgz", + "integrity": "sha512-hyES82QVpkfQMeBMteQUnrhASL/KHPhd7iJ8euduwNJG4mu2GSOKybf0rOEjOm1Wz7CwJEUm9y0yD7jg2C1bfg==", "dependencies": { - "ajv": "~8.13.0", - "ajv-draft-04": "~1.0.0", - "ajv-formats": "~3.0.1", - "fs-extra": "~11.3.0", + "fs-extra": "~7.0.1", "import-lazy": "~4.0.0", "jju": "~1.4.0", "resolve": "~1.22.1", - "semver": "~7.5.4" + "semver": "~7.5.4", + "z-schema": "~5.0.2" }, "peerDependencies": { "@types/node": "*" @@ -806,12 +697,11 @@ } }, "node_modules/@rushstack/terminal": { - "version": "0.15.2", - "resolved": "https://registry.npmjs.org/@rushstack/terminal/-/terminal-0.15.2.tgz", - "integrity": "sha512-7Hmc0ysK5077R/IkLS9hYu0QuNafm+TbZbtYVzCMbeOdMjaRboLKrhryjwZSRJGJzu+TV1ON7qZHeqf58XfLpA==", - "license": "MIT", + "version": "0.10.0", + "resolved": "https://registry.npmjs.org/@rushstack/terminal/-/terminal-0.10.0.tgz", + "integrity": "sha512-UbELbXnUdc7EKwfH2sb8ChqNgapUOdqcCIdQP4NGxBpTZV2sQyeekuK3zmfQSa/MN+/7b4kBogl2wq0vpkpYGw==", "dependencies": { - "@rushstack/node-core-library": "5.13.0", + "@rushstack/node-core-library": "4.0.2", "supports-color": "~8.1.1" }, "peerDependencies": { @@ -824,12 +714,11 @@ } }, "node_modules/@rushstack/ts-command-line": { - "version": "4.23.7", - "resolved": "https://registry.npmjs.org/@rushstack/ts-command-line/-/ts-command-line-4.23.7.tgz", - "integrity": "sha512-Gr9cB7DGe6uz5vq2wdr89WbVDKz0UeuFEn5H2CfWDe7JvjFFaiV15gi6mqDBTbHhHCWS7w8mF1h3BnIfUndqdA==", - "license": "MIT", + "version": "4.19.1", + "resolved": "https://registry.npmjs.org/@rushstack/ts-command-line/-/ts-command-line-4.19.1.tgz", + "integrity": "sha512-J7H768dgcpG60d7skZ5uSSwyCZs/S2HrWP1Ds8d1qYAyaaeJmpmmLr9BVw97RjFzmQPOYnoXcKA4GkqDCkduQg==", "dependencies": { - "@rushstack/terminal": "0.15.2", + "@rushstack/terminal": "0.10.0", "@types/argparse": "1.0.38", "argparse": "~1.0.9", "string-argv": "~0.3.1" @@ -841,11 +730,10 @@ "integrity": "sha512-cka47fVSo6lfQDIATYqb/vO1nvFfbPw7uWLayIXIhGETj0wcOOlrlkobOMDNQOFr9QOafegUPq13V2+6vtD7yg==" }, "node_modules/@tybys/wasm-util": { - "version": "0.10.2", - "resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.10.2.tgz", - "integrity": "sha512-RoBvJ2X0wuKlWFIjrwffGw1IqZHKQqzIchKaadZZfnNpsAYp2mM0h36JtPCjNDAHGgYez/15uMBpfGwchhiMgg==", + "version": "0.10.3", + "resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.10.3.tgz", + "integrity": "sha512-F3fo1MYrRJYL3zER0OUOmkutjr1Vp23m7OsSgp7nq4SP6OqX6C/56XFIPAl5bt3zaBRjmW7SGz3u/6LwFpYcOg==", "dev": true, - "license": "MIT", "optional": true, "dependencies": { "tslib": "^2.4.0" @@ -854,8 +742,7 @@ "node_modules/@types/argparse": { "version": "1.0.38", "resolved": "https://registry.npmjs.org/@types/argparse/-/argparse-1.0.38.tgz", - "integrity": "sha512-ebDJ9b0e702Yr7pWgB0jzm+CX4Srzz8RcXtLJDJB+BSccqMa36uyH/zUsSYao5+BD1ytv3k3rPYCq4mAE1hsXA==", - "license": "MIT" + "integrity": "sha512-ebDJ9b0e702Yr7pWgB0jzm+CX4Srzz8RcXtLJDJB+BSccqMa36uyH/zUsSYao5+BD1ytv3k3rPYCq4mAE1hsXA==" }, "node_modules/@types/debug": { "version": "4.1.8", @@ -871,11 +758,11 @@ "integrity": "sha512-iiUgKzV9AuaEkZqkOLDIvlQiL6ltuZd9tGcW3gwpnX8JbuiuhFlEGmmFXEXkN50Cvq7Os88IY2v0dkDqXYWVgA==" }, "node_modules/@types/node": { - "version": "24.12.2", - "resolved": "https://registry.npmjs.org/@types/node/-/node-24.12.2.tgz", - "integrity": "sha512-A1sre26ke7HDIuY/M23nd9gfB+nrmhtYyMINbjI1zHJxYteKR6qSMX56FsmjMcDb3SMcjJg5BiRRgOCC/yBD0g==", + "version": "24.13.3", + "resolved": "https://registry.npmjs.org/@types/node/-/node-24.13.3.tgz", + "integrity": "sha512-Dh8vAsV36ig5wa9OX4pXvMc9D3Veibfw2wix0CUwYODLD8nkj9UsLjASr49nPg+2eKzxhBV+v7L8pXvT4e639Q==", "dependencies": { - "undici-types": "~7.16.0" + "undici-types": "~7.18.0" } }, "node_modules/@types/validator": { @@ -907,53 +794,6 @@ "node": ">=6.0" } }, - "node_modules/ajv": { - "version": "8.18.0", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.18.0.tgz", - "integrity": "sha512-PlXPeEWMXMZ7sPYOHqmDyCJzcfNrUr3fGNKtezX14ykXOEIvyK81d+qydx89KY5O71FKMPaQ2vBfBFI5NHR63A==", - "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-draft-04": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/ajv-draft-04/-/ajv-draft-04-1.0.0.tgz", - "integrity": "sha512-mv00Te6nmYbRp5DCwclxtt7yV/joXJPGS7nM+97GdxvuttCOfgI3K4U25zboyeX0O+myI8ERluxQe5wljMmVIw==", - "license": "MIT", - "peerDependencies": { - "ajv": "^8.5.0" - }, - "peerDependenciesMeta": { - "ajv": { - "optional": true - } - } - }, - "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==", - "license": "MIT", - "dependencies": { - "ajv": "^8.0.0" - }, - "peerDependencies": { - "ajv": "^8.0.0" - }, - "peerDependenciesMeta": { - "ajv": { - "optional": true - } - } - }, "node_modules/ansi-regex": { "version": "6.2.2", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.2.tgz", @@ -980,51 +820,35 @@ "version": "1.0.10", "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", - "license": "MIT", "dependencies": { "sprintf-js": "~1.0.2" } }, - "node_modules/braces": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", - "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", - "license": "MIT", - "dependencies": { - "fill-range": "^7.1.1" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/cassandra-driver": { - "version": "4.8.0", - "resolved": "https://registry.npmjs.org/cassandra-driver/-/cassandra-driver-4.8.0.tgz", - "integrity": "sha512-HritfMGq9V7SuESeSodHvArs0mLuMk7uh+7hQK2lqdvXrvm50aWxb4RPxkK3mPDdsgHjJ427xNRFITMH2ei+Sw==", - "license": "Apache-2.0", + "version": "4.9.0", + "resolved": "https://registry.npmjs.org/cassandra-driver/-/cassandra-driver-4.9.0.tgz", + "integrity": "sha512-svYpdkLIGjD0WmuuwkkeYbfBdPX1zksK2cDyT1mWjX53OVTzuWBOVy54K6PPij8GgYpIG+K82OryrBv/xNeuWg==", "dependencies": { - "@types/node": "^18.11.18", + "@types/node": "^20.14.8", "adm-zip": "~0.5.10", "long": "~5.2.3" }, "engines": { - "node": ">=18" + "node": ">=20" } }, "node_modules/cassandra-driver/node_modules/@types/node": { - "version": "18.19.130", - "resolved": "https://registry.npmjs.org/@types/node/-/node-18.19.130.tgz", - "integrity": "sha512-GRaXQx6jGfL8sKfaIDD6OupbIHBr9jv7Jnaml9tB7l4v068PAOXqfcujMMo5PhbIs6ggR1XODELqahT2R8v0fg==", - "license": "MIT", + "version": "20.19.43", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.19.43.tgz", + "integrity": "sha512-6oYBAi5ikg4Pl+kGsoYtawUMBT2zZMCvPNF7pVLnHZfd1zf38DRiWn/gT01RYCdUqkv7Fhr+C9ot4/tb+2sVvA==", "dependencies": { - "undici-types": "~5.26.4" + "undici-types": "~6.21.0" } }, "node_modules/cassandra-driver/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==", - "license": "MIT" + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz", + "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==" }, "node_modules/cliui": { "version": "9.0.1", @@ -1039,6 +863,15 @@ "node": ">=20" } }, + "node_modules/commander": { + "version": "9.5.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-9.5.0.tgz", + "integrity": "sha512-KRs7WVDKg86PWiuAqhDrAQnTXZKraVcCc6vFdL14qrZ/DcWwuRo7VoiYXalXO7S5GKpqYiVEwCbgFDfxNHKJBQ==", + "optional": true, + "engines": { + "node": "^12.20.0 || >=14" + } + }, "node_modules/debug": { "version": "4.3.4", "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", @@ -1082,7 +915,6 @@ "version": "1.3.0", "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", - "license": "MIT", "engines": { "node": ">= 0.4" } @@ -1095,53 +927,6 @@ "node": ">=6" } }, - "node_modules/fast-deep-equal": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", - "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", - "license": "MIT" - }, - "node_modules/fast-glob": { - "version": "3.3.3", - "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz", - "integrity": "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==", - "license": "MIT", - "dependencies": { - "@nodelib/fs.stat": "^2.0.2", - "@nodelib/fs.walk": "^1.2.3", - "glob-parent": "^5.1.2", - "merge2": "^1.3.0", - "micromatch": "^4.0.8" - }, - "engines": { - "node": ">=8.6.0" - } - }, - "node_modules/fast-uri": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.2.tgz", - "integrity": "sha512-rVjf7ArG3LTk+FS6Yw81V1DLuZl1bRbNrev6Tmd/9RaroeeRRJhAt7jg/6YFxbvAQXUCavSoZhPPj6oOx+5KjQ==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/fastify" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/fastify" - } - ], - "license": "BSD-3-Clause" - }, - "node_modules/fastq": { - "version": "1.20.1", - "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.20.1.tgz", - "integrity": "sha512-GGToxJ/w1x32s/D2EKND7kTil4n8OVk/9mycTc4VDza13lOvpUZTGX3mFSCtV9ksdGBVzvsyAVLM6mHFThxXxw==", - "license": "ISC", - "dependencies": { - "reusify": "^1.0.4" - } - }, "node_modules/fd-package-json": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/fd-package-json/-/fd-package-json-2.0.0.tgz", @@ -1152,18 +937,6 @@ "walk-up-path": "^4.0.0" } }, - "node_modules/fill-range": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", - "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", - "license": "MIT", - "dependencies": { - "to-regex-range": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/formatly": { "version": "0.3.0", "resolved": "https://registry.npmjs.org/formatly/-/formatly-0.3.0.tgz", @@ -1181,24 +954,22 @@ } }, "node_modules/fs-extra": { - "version": "11.3.4", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.3.4.tgz", - "integrity": "sha512-CTXd6rk/M3/ULNQj8FBqBWHYBVYybQ3VPBw0xGKFe3tuH7ytT6ACnvzpIQ3UZtB8yvUKC2cXn1a+x+5EVQLovA==", - "license": "MIT", + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-7.0.1.tgz", + "integrity": "sha512-YJDaCJZEnBmcbw13fvdAM9AwNOJwOzrE4pqMqBq5nFiEqXUqHwlK4B+3pUw6JNvfSPtX05xFHtYy/1ni01eGCw==", "dependencies": { - "graceful-fs": "^4.2.0", - "jsonfile": "^6.0.1", - "universalify": "^2.0.0" + "graceful-fs": "^4.1.2", + "jsonfile": "^4.0.0", + "universalify": "^0.1.0" }, "engines": { - "node": ">=14.14" + "node": ">=6 <7 || >=8" } }, "node_modules/function-bind": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", - "license": "MIT", "funding": { "url": "https://github.com/sponsors/ljharb" } @@ -1235,38 +1006,23 @@ "url": "https://github.com/privatenumber/get-tsconfig?sponsor=1" } }, - "node_modules/glob-parent": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", - "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", - "license": "ISC", - "dependencies": { - "is-glob": "^4.0.1" - }, - "engines": { - "node": ">= 6" - } - }, "node_modules/graceful-fs": { "version": "4.2.11", "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", - "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", - "license": "ISC" + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==" }, "node_modules/has-flag": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "license": "MIT", "engines": { "node": ">=8" } }, "node_modules/hasown": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.3.tgz", - "integrity": "sha512-ej4AhfhfL2Q2zpMmLo7U1Uv9+PyhIZpgQLGT1F9miIGmiCJIoCgSmczFdrc97mWT4kVY72KA+WnnhJ5pghSvSg==", - "license": "MIT", + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.4.tgz", + "integrity": "sha512-T2UbfbBEF32wiepXIsMlTW9+dDYC6wMh/t/vYA4tuOMKqWz/n3vr1NFSxQiyP+zk2mXsoMA/i/7qV6LKut1t1A==", "dependencies": { "function-bind": "^1.1.2" }, @@ -1278,7 +1034,6 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/import-lazy/-/import-lazy-4.0.0.tgz", "integrity": "sha512-rKtvo6a868b5Hu3heneU+L4yEQ4jYKLtjpnPeUdK7h0yzXGmyBTypknlkCvHFBqfX9YlorEiMM6Dnq/5atfHkw==", - "license": "MIT", "engines": { "node": ">=8" } @@ -1292,12 +1047,11 @@ ] }, "node_modules/is-core-module": { - "version": "2.16.1", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.1.tgz", - "integrity": "sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==", - "license": "MIT", + "version": "2.16.2", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.2.tgz", + "integrity": "sha512-evOr8xfXKxE6qSR0hSXL2r3sd7ALj8+7jQEUvPYcm5sgZFdJ+AYzT6yNmJenvIYQBgIGwfwz08sL8zoL7yq2BA==", "dependencies": { - "hasown": "^2.0.2" + "hasown": "^2.0.3" }, "engines": { "node": ">= 0.4" @@ -1306,36 +1060,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/is-extglob": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", - "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-glob": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", - "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", - "license": "MIT", - "dependencies": { - "is-extglob": "^2.1.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-number": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", - "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", - "license": "MIT", - "engines": { - "node": ">=0.12.0" - } - }, "node_modules/jiti": { "version": "2.7.0", "resolved": "https://registry.npmjs.org/jiti/-/jiti-2.7.0.tgz", @@ -1349,31 +1073,20 @@ "node_modules/jju": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/jju/-/jju-1.4.0.tgz", - "integrity": "sha512-8wb9Yw966OSxApiCt0K3yNJL8pnNeIv+OEq2YMidz4FKP6nonSRoOXc80iXY4JaN2FC11B9qsNmDsm+ZOfMROA==", - "license": "MIT" - }, - "node_modules/json-schema-traverse": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", - "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", - "license": "MIT" + "integrity": "sha512-8wb9Yw966OSxApiCt0K3yNJL8pnNeIv+OEq2YMidz4FKP6nonSRoOXc80iXY4JaN2FC11B9qsNmDsm+ZOfMROA==" }, "node_modules/jsonfile": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.2.0.tgz", - "integrity": "sha512-FGuPw30AdOIUTRMC2OMRtQV+jkVj2cfPqSeWXv1NEAJ1qZ5zb1X6z1mFhbfOB/iy3ssJCD+3KuZ8r8C3uVFlAg==", - "license": "MIT", - "dependencies": { - "universalify": "^2.0.0" - }, + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz", + "integrity": "sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==", "optionalDependencies": { "graceful-fs": "^4.1.6" } }, "node_modules/knip": { - "version": "6.16.1", - "resolved": "https://registry.npmjs.org/knip/-/knip-6.16.1.tgz", - "integrity": "sha512-TKMn1rxgH6h9vXR9Y0B+Cq7AdPTr9EI02IwoT65NzqYUkvoDQAaJ/aPybiFpAhZ1px6cNYYwXf86iHkBgzCo9w==", + "version": "6.27.0", + "resolved": "https://registry.npmjs.org/knip/-/knip-6.27.0.tgz", + "integrity": "sha512-CngYEYrD0n20N06FXA8n3u/0Wnnugoa+B9k14OP+iKIgkCHuzvIdsP3nfwjhByoc1WfogpxfiriMboAXFETDUw==", "dev": true, "funding": [ { @@ -1385,19 +1098,18 @@ "url": "https://opencollective.com/knip" } ], - "license": "ISC", "dependencies": { "fdir": "^6.5.0", "formatly": "^0.3.0", "get-tsconfig": "4.14.0", "jiti": "^2.7.0", - "oxc-parser": "^0.133.0", - "oxc-resolver": "^11.20.0", + "oxc-parser": "^0.137.0", + "oxc-resolver": "11.21.3", "picomatch": "^4.0.4", "smol-toml": "^1.6.1", "strip-json-comments": "5.0.3", - "tinyglobby": "^0.2.16", - "unbash": "^3.0.0", + "tinyglobby": "^0.2.17", + "unbash": "^4.0.1", "yaml": "^2.9.0", "zod": "^4.1.11" }, @@ -1446,6 +1158,18 @@ "integrity": "sha512-dMInicTPVE8d1e5otfwmmjlxkZoUpiVLwyeTdUsi/Caj/gfzzblBcCE5sRHV/AsjuCmxWrte2TNGSYuCeCq+0Q==", "license": "MIT" }, + "node_modules/lodash.get": { + "version": "4.4.2", + "resolved": "https://registry.npmjs.org/lodash.get/-/lodash.get-4.4.2.tgz", + "integrity": "sha512-z+Uw/vLuy6gQe8cfaFWD7p0wVv8fJl3mbzXh33RS+0oW2wvUqiRXiQ69gLWSLpgB5/6sU+r6BlQR0MBILadqTQ==", + "deprecated": "This package is deprecated. Use the optional chaining (?.) operator instead." + }, + "node_modules/lodash.isequal": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.isequal/-/lodash.isequal-4.5.0.tgz", + "integrity": "sha512-pDo3lu8Jhfjqls6GkMgpahsF9kCyayhgykjyLMNFTKWrpVdAQtYyB4muAMWozBB4ig/dtWAmsMxLEI8wuz+DYQ==", + "deprecated": "This package is deprecated. Use require('node:util').isDeepStrictEqual instead." + }, "node_modules/long": { "version": "5.2.5", "resolved": "https://registry.npmjs.org/long/-/long-5.2.5.tgz", @@ -1463,28 +1187,6 @@ "node": ">=10" } }, - "node_modules/merge2": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", - "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", - "license": "MIT", - "engines": { - "node": ">= 8" - } - }, - "node_modules/micromatch": { - "version": "4.0.8", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", - "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", - "license": "MIT", - "dependencies": { - "braces": "^3.0.3", - "picomatch": "^2.3.1" - }, - "engines": { - "node": ">=8.6" - } - }, "node_modules/moment": { "version": "2.29.4", "resolved": "https://registry.npmjs.org/moment/-/moment-2.29.4.tgz", @@ -1510,13 +1212,12 @@ "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" }, "node_modules/oxc-parser": { - "version": "0.133.0", - "resolved": "https://registry.npmjs.org/oxc-parser/-/oxc-parser-0.133.0.tgz", - "integrity": "sha512-661RSx+ZcjBmjBYid+Fpp/2F5EbtildpeoZh5HdgnGs+jZ03nqQEQW8yGkt4BGyOC3OMPDQQRl8M5kqD2/g6jw==", + "version": "0.137.0", + "resolved": "https://registry.npmjs.org/oxc-parser/-/oxc-parser-0.137.0.tgz", + "integrity": "sha512-yFImD+WLElJpLKy8llG1qe4DCmMsL18peRp8XP1JKfig/gISbJkglnpDtX2aTmAn10kZF7164HbN2H8QPsXxGg==", "dev": true, - "license": "MIT", "dependencies": { - "@oxc-project/types": "^0.133.0" + "@oxc-project/types": "^0.137.0" }, "engines": { "node": "^20.19.0 || >=22.12.0" @@ -1525,82 +1226,68 @@ "url": "https://github.com/sponsors/Boshen" }, "optionalDependencies": { - "@oxc-parser/binding-android-arm-eabi": "0.133.0", - "@oxc-parser/binding-android-arm64": "0.133.0", - "@oxc-parser/binding-darwin-arm64": "0.133.0", - "@oxc-parser/binding-darwin-x64": "0.133.0", - "@oxc-parser/binding-freebsd-x64": "0.133.0", - "@oxc-parser/binding-linux-arm-gnueabihf": "0.133.0", - "@oxc-parser/binding-linux-arm-musleabihf": "0.133.0", - "@oxc-parser/binding-linux-arm64-gnu": "0.133.0", - "@oxc-parser/binding-linux-arm64-musl": "0.133.0", - "@oxc-parser/binding-linux-ppc64-gnu": "0.133.0", - "@oxc-parser/binding-linux-riscv64-gnu": "0.133.0", - "@oxc-parser/binding-linux-riscv64-musl": "0.133.0", - "@oxc-parser/binding-linux-s390x-gnu": "0.133.0", - "@oxc-parser/binding-linux-x64-gnu": "0.133.0", - "@oxc-parser/binding-linux-x64-musl": "0.133.0", - "@oxc-parser/binding-openharmony-arm64": "0.133.0", - "@oxc-parser/binding-wasm32-wasi": "0.133.0", - "@oxc-parser/binding-win32-arm64-msvc": "0.133.0", - "@oxc-parser/binding-win32-ia32-msvc": "0.133.0", - "@oxc-parser/binding-win32-x64-msvc": "0.133.0" + "@oxc-parser/binding-android-arm-eabi": "0.137.0", + "@oxc-parser/binding-android-arm64": "0.137.0", + "@oxc-parser/binding-darwin-arm64": "0.137.0", + "@oxc-parser/binding-darwin-x64": "0.137.0", + "@oxc-parser/binding-freebsd-x64": "0.137.0", + "@oxc-parser/binding-linux-arm-gnueabihf": "0.137.0", + "@oxc-parser/binding-linux-arm-musleabihf": "0.137.0", + "@oxc-parser/binding-linux-arm64-gnu": "0.137.0", + "@oxc-parser/binding-linux-arm64-musl": "0.137.0", + "@oxc-parser/binding-linux-ppc64-gnu": "0.137.0", + "@oxc-parser/binding-linux-riscv64-gnu": "0.137.0", + "@oxc-parser/binding-linux-riscv64-musl": "0.137.0", + "@oxc-parser/binding-linux-s390x-gnu": "0.137.0", + "@oxc-parser/binding-linux-x64-gnu": "0.137.0", + "@oxc-parser/binding-linux-x64-musl": "0.137.0", + "@oxc-parser/binding-openharmony-arm64": "0.137.0", + "@oxc-parser/binding-wasm32-wasi": "0.137.0", + "@oxc-parser/binding-win32-arm64-msvc": "0.137.0", + "@oxc-parser/binding-win32-ia32-msvc": "0.137.0", + "@oxc-parser/binding-win32-x64-msvc": "0.137.0" } }, "node_modules/oxc-resolver": { - "version": "11.20.0", - "resolved": "https://registry.npmjs.org/oxc-resolver/-/oxc-resolver-11.20.0.tgz", - "integrity": "sha512-CblytBiV/a/ZXY34dsVU2NxhIOxMXst8CvDCtyBelVITgd7PLrKzbEbA6oKLdPjvDKDzCiW48qzmzZ+mYaqn+g==", + "version": "11.21.3", + "resolved": "https://registry.npmjs.org/oxc-resolver/-/oxc-resolver-11.21.3.tgz", + "integrity": "sha512-2Mx3fKQz7+xgrBONjsxOgCGtMHOn38/HxMzW1I5efwXB5a4lRN0Vp40gYUJFBWJslcrvwoofTrqoTnLbwTd3pA==", "dev": true, - "license": "MIT", "funding": { "url": "https://github.com/sponsors/Boshen" }, "optionalDependencies": { - "@oxc-resolver/binding-android-arm-eabi": "11.20.0", - "@oxc-resolver/binding-android-arm64": "11.20.0", - "@oxc-resolver/binding-darwin-arm64": "11.20.0", - "@oxc-resolver/binding-darwin-x64": "11.20.0", - "@oxc-resolver/binding-freebsd-x64": "11.20.0", - "@oxc-resolver/binding-linux-arm-gnueabihf": "11.20.0", - "@oxc-resolver/binding-linux-arm-musleabihf": "11.20.0", - "@oxc-resolver/binding-linux-arm64-gnu": "11.20.0", - "@oxc-resolver/binding-linux-arm64-musl": "11.20.0", - "@oxc-resolver/binding-linux-ppc64-gnu": "11.20.0", - "@oxc-resolver/binding-linux-riscv64-gnu": "11.20.0", - "@oxc-resolver/binding-linux-riscv64-musl": "11.20.0", - "@oxc-resolver/binding-linux-s390x-gnu": "11.20.0", - "@oxc-resolver/binding-linux-x64-gnu": "11.20.0", - "@oxc-resolver/binding-linux-x64-musl": "11.20.0", - "@oxc-resolver/binding-openharmony-arm64": "11.20.0", - "@oxc-resolver/binding-wasm32-wasi": "11.20.0", - "@oxc-resolver/binding-win32-arm64-msvc": "11.20.0", - "@oxc-resolver/binding-win32-x64-msvc": "11.20.0" + "@oxc-resolver/binding-android-arm-eabi": "11.21.3", + "@oxc-resolver/binding-android-arm64": "11.21.3", + "@oxc-resolver/binding-darwin-arm64": "11.21.3", + "@oxc-resolver/binding-darwin-x64": "11.21.3", + "@oxc-resolver/binding-freebsd-x64": "11.21.3", + "@oxc-resolver/binding-linux-arm-gnueabihf": "11.21.3", + "@oxc-resolver/binding-linux-arm-musleabihf": "11.21.3", + "@oxc-resolver/binding-linux-arm64-gnu": "11.21.3", + "@oxc-resolver/binding-linux-arm64-musl": "11.21.3", + "@oxc-resolver/binding-linux-ppc64-gnu": "11.21.3", + "@oxc-resolver/binding-linux-riscv64-gnu": "11.21.3", + "@oxc-resolver/binding-linux-riscv64-musl": "11.21.3", + "@oxc-resolver/binding-linux-s390x-gnu": "11.21.3", + "@oxc-resolver/binding-linux-x64-gnu": "11.21.3", + "@oxc-resolver/binding-linux-x64-musl": "11.21.3", + "@oxc-resolver/binding-openharmony-arm64": "11.21.3", + "@oxc-resolver/binding-wasm32-wasi": "11.21.3", + "@oxc-resolver/binding-win32-arm64-msvc": "11.21.3", + "@oxc-resolver/binding-win32-x64-msvc": "11.21.3" } }, "node_modules/path-parse": { "version": "1.0.7", "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", - "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", - "license": "MIT" + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==" }, "node_modules/pg-connection-string": { "version": "2.6.2", "resolved": "https://registry.npmjs.org/pg-connection-string/-/pg-connection-string-2.6.2.tgz", "integrity": "sha512-ch6OwaeaPYcova4kKZ15sbJ2hKb/VP48ZD2gE7i1J+L4MspCtBMAx8nMgz7bksc7IojCIIWuEhHibSMFH8m8oA==" }, - "node_modules/picomatch": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.2.tgz", - "integrity": "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==", - "license": "MIT", - "engines": { - "node": ">=8.6" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } - }, "node_modules/pony-cause": { "version": "2.1.11", "resolved": "https://registry.npmjs.org/pony-cause/-/pony-cause-2.1.11.tgz", @@ -1610,40 +1297,10 @@ "node": ">=12.0.0" } }, - "node_modules/queue-microtask": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", - "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", - "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/require-from-string": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", - "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/resolve": { "version": "1.22.12", "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.12.tgz", "integrity": "sha512-TyeJ1zif53BPfHootBGwPRYT1RUt6oGWsaQr8UyZW/eAm9bKoijtvruSDEmZHm92CwS9nj7/fWttqPCgzep8CA==", - "license": "MIT", "dependencies": { "es-errors": "^1.3.0", "is-core-module": "^2.16.1", @@ -1675,39 +1332,6 @@ "resolved": "https://registry.npmjs.org/retry-as-promised/-/retry-as-promised-7.0.4.tgz", "integrity": "sha512-XgmCoxKWkDofwH8WddD0w85ZfqYz+ZHlr5yo+3YUCfycWawU56T5ckWXsScsj5B8tqUcIG67DxXByo3VUgiAdA==" }, - "node_modules/reusify": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.1.0.tgz", - "integrity": "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==", - "license": "MIT", - "engines": { - "iojs": ">=1.0.0", - "node": ">=0.10.0" - } - }, - "node_modules/run-parallel": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", - "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "MIT", - "dependencies": { - "queue-microtask": "^1.2.2" - } - }, "node_modules/semver": { "version": "7.5.4", "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", @@ -1808,14 +1432,12 @@ "node_modules/sprintf-js": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", - "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==", - "license": "BSD-3-Clause" + "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==" }, "node_modules/string-argv": { "version": "0.3.2", "resolved": "https://registry.npmjs.org/string-argv/-/string-argv-0.3.2.tgz", "integrity": "sha512-aqD2Q0144Z+/RqG52NeHEkZauTAUWJO8c6yTftGJKO3Tja5tUgIfmIl6kExvhtxSDP7fXB6DvzkfMpCd/F3G+Q==", - "license": "MIT", "engines": { "node": ">=0.6.19" } @@ -1867,7 +1489,6 @@ "version": "8.1.1", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", - "license": "MIT", "dependencies": { "has-flag": "^4.0.0" }, @@ -1882,7 +1503,6 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", - "license": "MIT", "engines": { "node": ">= 0.4" }, @@ -1894,7 +1514,6 @@ "version": "0.2.17", "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.17.tgz", "integrity": "sha512-wXR/dYpcqKmfWpEdZjiKJOwCNFndD0DMnrW/cYjVGttEkBfVgcLFHoNrlj47mjOVic9yyNu65alsgF4NQyTa2g==", - "dev": true, "license": "MIT", "dependencies": { "fdir": "^6.5.0", @@ -1911,7 +1530,6 @@ "version": "6.5.0", "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", - "dev": true, "license": "MIT", "engines": { "node": ">=12.0.0" @@ -1929,7 +1547,6 @@ "version": "4.0.4", "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.4.tgz", "integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==", - "dev": true, "license": "MIT", "engines": { "node": ">=12" @@ -1938,18 +1555,6 @@ "url": "https://github.com/sponsors/jonschlinkert" } }, - "node_modules/to-regex-range": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", - "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", - "license": "MIT", - "dependencies": { - "is-number": "^7.0.0" - }, - "engines": { - "node": ">=8.0" - } - }, "node_modules/toposort-class": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/toposort-class/-/toposort-class-1.0.1.tgz", @@ -1960,7 +1565,6 @@ "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", "dev": true, - "license": "0BSD", "optional": true }, "node_modules/type-fest": { @@ -1989,15 +1593,14 @@ } }, "node_modules/umzug": { - "version": "3.8.2", - "resolved": "https://registry.npmjs.org/umzug/-/umzug-3.8.2.tgz", - "integrity": "sha512-BEWEF8OJjTYVC56GjELeHl/1XjFejrD7aHzn+HldRJTx+pL1siBrKHZC8n4K/xL3bEzVA9o++qD1tK2CpZu4KA==", - "license": "MIT", + "version": "3.8.3", + "resolved": "https://registry.npmjs.org/umzug/-/umzug-3.8.3.tgz", + "integrity": "sha512-U9SRJI6LJvV0XwrqGMVPBkE26WHJklHZjtscJ2sEjUp7f+h4NH/25YGjPBernWLroVJvMnTkCAGC0bT0dd63qA==", "dependencies": { - "@rushstack/ts-command-line": "^4.12.2", + "@rushstack/ts-command-line": "4.19.1", "emittery": "^0.13.0", - "fast-glob": "^3.3.2", "pony-cause": "^2.1.4", + "tinyglobby": "^0.2.16", "type-fest": "^4.0.0" }, "engines": { @@ -2005,28 +1608,25 @@ } }, "node_modules/unbash": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/unbash/-/unbash-3.0.0.tgz", - "integrity": "sha512-FeFPZ/WFT0mbRCuydiZzpPFlrYN8ZUpphQKoq4EeElVIYjYyGzPMxQR/simUwCOJIyVhpFk4RbtyO7RuMpMnHA==", + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/unbash/-/unbash-4.0.4.tgz", + "integrity": "sha512-60m9IVGbavD6jholbxt0jVBXZkEB/HsMZq7Tyaghseve2/Sf0zQRAIfWsD34sde+DKP2tBxJS2wP88ZM0D1FhA==", "dev": true, - "license": "ISC", "engines": { "node": ">=14" } }, "node_modules/undici-types": { - "version": "7.16.0", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.16.0.tgz", - "integrity": "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw==", - "license": "MIT" + "version": "7.18.2", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.18.2.tgz", + "integrity": "sha512-AsuCzffGHJybSaRrmr5eHr81mwJU3kjw6M+uprWvCXiNeN9SOGwQ3Jn8jb8m3Z6izVgknn1R0FTCEAP2QrLY/w==" }, "node_modules/universalify": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", - "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", - "license": "MIT", + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", + "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==", "engines": { - "node": ">= 10.0.0" + "node": ">= 4.0.0" } }, "node_modules/uuid": { @@ -2138,6 +1738,25 @@ "node": "^20.19.0 || ^22.12.0 || >=23" } }, + "node_modules/z-schema": { + "version": "5.0.5", + "resolved": "https://registry.npmjs.org/z-schema/-/z-schema-5.0.5.tgz", + "integrity": "sha512-D7eujBWkLa3p2sIpJA0d1pr7es+a7m0vFAnZLlCEKq/Ij2k0MLi9Br2UPxoxdYystm5K1yeBGzub0FlYUEWj2Q==", + "dependencies": { + "lodash.get": "^4.4.2", + "lodash.isequal": "^4.5.0", + "validator": "^13.7.0" + }, + "bin": { + "z-schema": "bin/z-schema" + }, + "engines": { + "node": ">=8.0.0" + }, + "optionalDependencies": { + "commander": "^9.4.1" + } + }, "node_modules/zod": { "version": "4.4.3", "resolved": "https://registry.npmjs.org/zod/-/zod-4.4.3.tgz", @@ -2151,20 +1770,20 @@ }, "dependencies": { "@emnapi/core": { - "version": "1.10.0", - "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.10.0.tgz", - "integrity": "sha512-yq6OkJ4p82CAfPl0u9mQebQHKPJkY7WrIuk205cTYnYe+k2Z8YBh11FrbRG/H6ihirqcacOgl2BIO8oyMQLeXw==", + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.11.1.tgz", + "integrity": "sha512-RSvbQmHzdKzNsLYa/wHrbc3KN4sYLKAdPZxqiM2HATqv/SBk2/ENSHpvXGaLOMcsAyz0poEGqkmmKYG3OWiJEQ==", "dev": true, "optional": true, "requires": { - "@emnapi/wasi-threads": "1.2.1", + "@emnapi/wasi-threads": "1.2.2", "tslib": "^2.4.0" } }, "@emnapi/runtime": { - "version": "1.10.0", - "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.10.0.tgz", - "integrity": "sha512-ewvYlk86xUoGI0zQRNq/mC+16R1QeDlKQy21Ki3oSYXNgLb45GV1P6A0M+/s6nyCuNDqe5VpaY84BzXGwVbwFA==", + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.11.1.tgz", + "integrity": "sha512-vgj7R3y3Wgx24IQaGPA/R6YFXLHVMOZ0uVEyIQPaWs+rd1AzfEMXlAC22FYwO1XkKR6NPsq7mUandH8oIRdZFw==", "dev": true, "optional": true, "requires": { @@ -2172,9 +1791,9 @@ } }, "@emnapi/wasi-threads": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.2.1.tgz", - "integrity": "sha512-uTII7OYF+/Mes/MrcIOYp5yOtSMLBWSIoLPpcgwipoiKbli6k322tcoFsxoIIxPDqW01SQGAgko4EzZi2BNv2w==", + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.2.2.tgz", + "integrity": "sha512-c95qOXkHdydNKhscBTebqEC1CVAZpyqOfVfBzQ1qgzyl3gfeldUjIggDbIZgDKsHLgnsM+igH7TJ/eAasaVuMA==", "dev": true, "optional": true, "requires": { @@ -2182,357 +1801,355 @@ } }, "@napi-rs/wasm-runtime": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-1.1.4.tgz", - "integrity": "sha512-3NQNNgA1YSlJb/kMH1ildASP9HW7/7kYnRI2szWJaofaS1hWmbGI4H+d3+22aGzXXN9IJ+n+GiFVcGipJP18ow==", + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-1.1.6.tgz", + "integrity": "sha512-ZLv/JdUfkvOy9eCnnBaGfiO+XimbjebAeO+MRQqD/B+FR1tnRN0tpKSJHRbE8sFfS6aqsXZ67TQjfwfsxULVbg==", "dev": true, "optional": true, "requires": { - "@tybys/wasm-util": "^0.10.1" - } - }, - "@nodelib/fs.scandir": { - "version": "2.1.5", - "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", - "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", - "requires": { - "@nodelib/fs.stat": "2.0.5", - "run-parallel": "^1.1.9" - } - }, - "@nodelib/fs.stat": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", - "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==" - }, - "@nodelib/fs.walk": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", - "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", - "requires": { - "@nodelib/fs.scandir": "2.1.5", - "fastq": "^1.6.0" + "@tybys/wasm-util": "^0.10.3" } }, "@oxc-parser/binding-android-arm-eabi": { - "version": "0.133.0", - "resolved": "https://registry.npmjs.org/@oxc-parser/binding-android-arm-eabi/-/binding-android-arm-eabi-0.133.0.tgz", - "integrity": "sha512-l/44caGse+VpnY9gx0yvvc5QnnG3yG1FO3KZgYvNL1GZrfK86zIwAOgGEVlxDyRymzrU/KHiblPFpevKOmJmUA==", + "version": "0.137.0", + "resolved": "https://registry.npmjs.org/@oxc-parser/binding-android-arm-eabi/-/binding-android-arm-eabi-0.137.0.tgz", + "integrity": "sha512-KDs+0VPdEmasOkpuJHW9V5WCF+cvYdMQv2Jd+aJXt+cxIx12NToRQRbXaRwUEDsZw+/jMk81Ve8ZFbjUkJTOwA==", "dev": true, "optional": true }, "@oxc-parser/binding-android-arm64": { - "version": "0.133.0", - "resolved": "https://registry.npmjs.org/@oxc-parser/binding-android-arm64/-/binding-android-arm64-0.133.0.tgz", - "integrity": "sha512-KUHmPMziLBp4u+zbrLdB7iWS7KshuZe+RAp7ELnY9SI9nNXBZ+dp8fiBqWOxhXqn+FQg3a4UcQhwmsJOKV8Jjg==", + "version": "0.137.0", + "resolved": "https://registry.npmjs.org/@oxc-parser/binding-android-arm64/-/binding-android-arm64-0.137.0.tgz", + "integrity": "sha512-WhALNzfy3x/RfC6bsqX+csavuUY0yHHE7XfgPE5M542uhoBZUUoGTPG+nkMbGoG4+gcfss5s7urMyn5QBHu0sw==", "dev": true, "optional": true }, "@oxc-parser/binding-darwin-arm64": { - "version": "0.133.0", - "resolved": "https://registry.npmjs.org/@oxc-parser/binding-darwin-arm64/-/binding-darwin-arm64-0.133.0.tgz", - "integrity": "sha512-q8dWmnU/8ea2tga9w2f1PinQ5rcMPDUGkF64T189b65YMjUomET4oy5oRldOr4AwOQkneOG/Zttnz1Dvrc62wg==", + "version": "0.137.0", + "resolved": "https://registry.npmjs.org/@oxc-parser/binding-darwin-arm64/-/binding-darwin-arm64-0.137.0.tgz", + "integrity": "sha512-bFPr5hgmNMOMoyPTGtdsK4Ug21RovIPojRMgDDhSp1LtCnc/DkLwGONKjgRjszg677RlGnkYSviQ8hHaUPOVYA==", "dev": true, "optional": true }, "@oxc-parser/binding-darwin-x64": { - "version": "0.133.0", - "resolved": "https://registry.npmjs.org/@oxc-parser/binding-darwin-x64/-/binding-darwin-x64-0.133.0.tgz", - "integrity": "sha512-cOKeIELIB2bJnCKwqx4Rdj+1Lss/U6uCbLxRySZrhyOOQa1flKhwZFjEHRHxk8fU1NKmhK5OnTdPQ4CpjuFuVw==", + "version": "0.137.0", + "resolved": "https://registry.npmjs.org/@oxc-parser/binding-darwin-x64/-/binding-darwin-x64-0.137.0.tgz", + "integrity": "sha512-CL5dMm1asqXIDZHg14FLxj3Mc36w8PI7xCWh1uA4is6z8g2XrIILoTcQYOxDbwzuk34RDPX5IAGUxZr6LA9KAg==", "dev": true, "optional": true }, "@oxc-parser/binding-freebsd-x64": { - "version": "0.133.0", - "resolved": "https://registry.npmjs.org/@oxc-parser/binding-freebsd-x64/-/binding-freebsd-x64-0.133.0.tgz", - "integrity": "sha512-OpaSv4pW3KgFrMYQxTaS0aOE4T1DQF3qZE/4B6uqqv1KgPWWd4UQhJALi8PJPX1RRV5K7ThKXRfF7qGg2+3l1A==", + "version": "0.137.0", + "resolved": "https://registry.npmjs.org/@oxc-parser/binding-freebsd-x64/-/binding-freebsd-x64-0.137.0.tgz", + "integrity": "sha512-79h8rYGnSlKPGWo7mHr2ixO6ea7aW8B0CT965SZ8SLbNnCOH5aOYBTeVXUY6eMvEaiLyWr8Skuiugr5pDYgLGw==", "dev": true, "optional": true }, "@oxc-parser/binding-linux-arm-gnueabihf": { - "version": "0.133.0", - "resolved": "https://registry.npmjs.org/@oxc-parser/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-0.133.0.tgz", - "integrity": "sha512-JGK1wlGrGwxBIlVSF7KWTX1/ru6BEtf28fRROztDRkLfiW+Kxa4onnriezMIiogfn9hVw2KzYcKiLjkLR2ns8A==", + "version": "0.137.0", + "resolved": "https://registry.npmjs.org/@oxc-parser/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-0.137.0.tgz", + "integrity": "sha512-ASgmlSimhGyr0lksgVIo6hibz1obnDq4qJbiMX/AzltfgPnanRrzG1Q+23g8ljOHOjv6dsznkUuCYL3gg0sY1Q==", "dev": true, "optional": true }, "@oxc-parser/binding-linux-arm-musleabihf": { - "version": "0.133.0", - "resolved": "https://registry.npmjs.org/@oxc-parser/binding-linux-arm-musleabihf/-/binding-linux-arm-musleabihf-0.133.0.tgz", - "integrity": "sha512-yuZO533Ftonxn/iyoqQzURzLQHMspvsIyfiCSNi1t/ER4eIQaR0SsmUOUm5b/lmSig7IWIUa5/BrbEkAPwcilQ==", + "version": "0.137.0", + "resolved": "https://registry.npmjs.org/@oxc-parser/binding-linux-arm-musleabihf/-/binding-linux-arm-musleabihf-0.137.0.tgz", + "integrity": "sha512-AU2J9aa22Sx32wRGnDjybOU9TQXXQUud5sdUi+ZB0XxwM8aToWLweV+yA0wlQm0yIUVqljquqoHCYEq9II8gJQ==", "dev": true, "optional": true }, "@oxc-parser/binding-linux-arm64-gnu": { - "version": "0.133.0", - "resolved": "https://registry.npmjs.org/@oxc-parser/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-0.133.0.tgz", - "integrity": "sha512-hvpbqT5pN2rR+3+xtWeizwfR/aZ0vGceg6TqYMl+ToxMpk9/tmnX7kSvQnfEUkoua8mhogzvIKsAkn0wxgblBA==", + "version": "0.137.0", + "resolved": "https://registry.npmjs.org/@oxc-parser/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-0.137.0.tgz", + "integrity": "sha512-GdEtiG89yMr7XkUGxifgodXEEm2f+xW2f9CpDjlgAnBOwhTmrpQMvhOGobLVKUyzf/qHBXW16smk5zbF3nZU6w==", "dev": true, "optional": true }, "@oxc-parser/binding-linux-arm64-musl": { - "version": "0.133.0", - "resolved": "https://registry.npmjs.org/@oxc-parser/binding-linux-arm64-musl/-/binding-linux-arm64-musl-0.133.0.tgz", - "integrity": "sha512-wJQGamIosQBoJHW9+S5XxrtKRo3eyJxsnS1XCPrqN0LHi8uw1pTqqTfn3t/NVuvbBg7Pumn4ez9Eidgcn0xbEg==", + "version": "0.137.0", + "resolved": "https://registry.npmjs.org/@oxc-parser/binding-linux-arm64-musl/-/binding-linux-arm64-musl-0.137.0.tgz", + "integrity": "sha512-EGJ+Bs8iXx8KBH8DQ5BLoEm5lnHaYjlh4/8j8vFhrr/6z4tqONy5BZDzLpKmmNWlN6Hlc5r8YOuBVHqZ9vRFEQ==", "dev": true, "optional": true }, "@oxc-parser/binding-linux-ppc64-gnu": { - "version": "0.133.0", - "resolved": "https://registry.npmjs.org/@oxc-parser/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-0.133.0.tgz", - "integrity": "sha512-Koaz32/O5+abIfrNGdyndgRvdOZ9jEf5/z3Ep9h3h2QWpdDiUQpVwgH0OcMXCs+l9aXxPLtkupqyVig9W6FDKw==", + "version": "0.137.0", + "resolved": "https://registry.npmjs.org/@oxc-parser/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-0.137.0.tgz", + "integrity": "sha512-vzFUQENy/fnbSe5DZWovq6tIBc1uhuMztanSW6rz1e9WdQE4gHwYuD7ZII6JnrJifd1R3RSoqiZbgRFlVL2tYQ==", "dev": true, "optional": true }, "@oxc-parser/binding-linux-riscv64-gnu": { - "version": "0.133.0", - "resolved": "https://registry.npmjs.org/@oxc-parser/binding-linux-riscv64-gnu/-/binding-linux-riscv64-gnu-0.133.0.tgz", - "integrity": "sha512-R4vOjWzxhnNWHnVLeiB6jNuIifdy9vcMXZGPc7StXcxBovI+U2zg1QhZ9o8OjV80oGivs1lX5NfPLzk4IPqlRA==", + "version": "0.137.0", + "resolved": "https://registry.npmjs.org/@oxc-parser/binding-linux-riscv64-gnu/-/binding-linux-riscv64-gnu-0.137.0.tgz", + "integrity": "sha512-SfVI14HBQs9gtLcUD5hTt5hsNbdrqSUNg9S8muN+LhVQ5nf1WwH3hAoK6B9NKgdYgWAQSXFXGiiBedQ4r/BKuw==", "dev": true, "optional": true }, "@oxc-parser/binding-linux-riscv64-musl": { - "version": "0.133.0", - "resolved": "https://registry.npmjs.org/@oxc-parser/binding-linux-riscv64-musl/-/binding-linux-riscv64-musl-0.133.0.tgz", - "integrity": "sha512-iwgBNUTHiMdxARLYuM0SBlnYeb19iw1Ea5M+4ERZupCsBMLArti6FyZ6UfFjJxIiTDr2oW2DGQFxlQVQ/dW9rA==", + "version": "0.137.0", + "resolved": "https://registry.npmjs.org/@oxc-parser/binding-linux-riscv64-musl/-/binding-linux-riscv64-musl-0.137.0.tgz", + "integrity": "sha512-e7Ppy4FCIFNQxT/ikSeIWFoQ0l+N9vgtRBtLcyZXeolTzApyVoPqEXsYPrcdM/9i0Bwk8knvYd37vaEMxHyi6g==", "dev": true, "optional": true }, "@oxc-parser/binding-linux-s390x-gnu": { - "version": "0.133.0", - "resolved": "https://registry.npmjs.org/@oxc-parser/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-0.133.0.tgz", - "integrity": "sha512-ZwZNo8FZmB/gVfboQl+wXilBigGl+6nQQs+nITOeAP/HcAOjiHl6XZJL9F/KXNEspODQcbjAiyjUbeCJd9a0fA==", + "version": "0.137.0", + "resolved": "https://registry.npmjs.org/@oxc-parser/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-0.137.0.tgz", + "integrity": "sha512-Bho5qFwdhqsIFR7gipYEUlqvi3SRrY8sugxXig380MIaakBB1PyU9+7dBiBVScfImTNWhijUxdBwqrprGdq5WA==", "dev": true, "optional": true }, "@oxc-parser/binding-linux-x64-gnu": { - "version": "0.133.0", - "resolved": "https://registry.npmjs.org/@oxc-parser/binding-linux-x64-gnu/-/binding-linux-x64-gnu-0.133.0.tgz", - "integrity": "sha512-govCvWx1dBlED3uu4qXctxpRcouu9I8Kn+DBktGCl760JtlGJzc9l/OmPJKlYWSbrRqKkMZehNeZ/4Wfma7uSA==", + "version": "0.137.0", + "resolved": "https://registry.npmjs.org/@oxc-parser/binding-linux-x64-gnu/-/binding-linux-x64-gnu-0.137.0.tgz", + "integrity": "sha512-36mGWtg7PyFzjJwGDkH6/F4o2nIDEoKXLPr/X/lwqklkomQwJJt1I5GJVmGhovUEmgPK5WAeAZMqlFCehwiy9Q==", "dev": true, "optional": true }, "@oxc-parser/binding-linux-x64-musl": { - "version": "0.133.0", - "resolved": "https://registry.npmjs.org/@oxc-parser/binding-linux-x64-musl/-/binding-linux-x64-musl-0.133.0.tgz", - "integrity": "sha512-ssTlpXD5Mq9uCssDJPzlRWqBt4Y7Zzd9i+XZhWmK/9Y6KUIuAxVYTYiI8lxcGWi0+3/Cz4A8q9UrD4NK9Y2j7g==", + "version": "0.137.0", + "resolved": "https://registry.npmjs.org/@oxc-parser/binding-linux-x64-musl/-/binding-linux-x64-musl-0.137.0.tgz", + "integrity": "sha512-/Jqx6+N7A44n2BdvUr7pXhVr2vFjs6WGH3unZRczwrfiH0H1zY0QwKQMG/dtRiTlKGDKGukznPT8lx84/oEsZg==", "dev": true, "optional": true }, "@oxc-parser/binding-openharmony-arm64": { - "version": "0.133.0", - "resolved": "https://registry.npmjs.org/@oxc-parser/binding-openharmony-arm64/-/binding-openharmony-arm64-0.133.0.tgz", - "integrity": "sha512-51aByfXhPtLEdWG4a2Ihdw6cPWV1ei1AarALpFdDP8MLWDLE2NuUMgbo3DERR2Kt8fT/ok1GUvBiLxVGke9uUQ==", + "version": "0.137.0", + "resolved": "https://registry.npmjs.org/@oxc-parser/binding-openharmony-arm64/-/binding-openharmony-arm64-0.137.0.tgz", + "integrity": "sha512-9Uj0qHNNl+OgT1UTGwF7ixIXU6T1u2SbMidmgPy/h1h/fl2gRS6YpAxxY1gwHofcWjoTwkoMFd8xs5Vuj6GOFA==", "dev": true, "optional": true }, "@oxc-parser/binding-wasm32-wasi": { - "version": "0.133.0", - "resolved": "https://registry.npmjs.org/@oxc-parser/binding-wasm32-wasi/-/binding-wasm32-wasi-0.133.0.tgz", - "integrity": "sha512-2e16tkKp+wDO2GTAmXfxbBcCmGEaFPIJEIRBBmVKNVXSc8/fJsSIaBGyFTPHM9ST5GNWgJcYIt94rDTks+PLwA==", + "version": "0.137.0", + "resolved": "https://registry.npmjs.org/@oxc-parser/binding-wasm32-wasi/-/binding-wasm32-wasi-0.137.0.tgz", + "integrity": "sha512-gW2vfkytNGgMVADiuzdvOfw0mWG9za20F/1fCJsif5aBMAvWJTSbpIXbIe0XkOe0VENk+PadpQ7cZgUy2sUJcA==", "dev": true, "optional": true, "requires": { - "@emnapi/core": "1.10.0", - "@emnapi/runtime": "1.10.0", - "@napi-rs/wasm-runtime": "^1.1.4" + "@emnapi/core": "1.11.1", + "@emnapi/runtime": "1.11.1", + "@napi-rs/wasm-runtime": "^1.1.5" } }, "@oxc-parser/binding-win32-arm64-msvc": { - "version": "0.133.0", - "resolved": "https://registry.npmjs.org/@oxc-parser/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-0.133.0.tgz", - "integrity": "sha512-KPTNDKbxH1cglrqTyVeXHb4Pk4oksz8EcE1/v8zqU7N4UXbiHfA/IwtXZ2U77fnRAWBbgVkl/lZbL7o3hRdejg==", + "version": "0.137.0", + "resolved": "https://registry.npmjs.org/@oxc-parser/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-0.137.0.tgz", + "integrity": "sha512-x+pFANF0yL5uK/6T7lu6SlR5qid6sp//eZXKLq5iNsIE+EQg6EaS8/wsW7E91nXXjpnPhSoMOHXShSVhGRdn8w==", "dev": true, "optional": true }, "@oxc-parser/binding-win32-ia32-msvc": { - "version": "0.133.0", - "resolved": "https://registry.npmjs.org/@oxc-parser/binding-win32-ia32-msvc/-/binding-win32-ia32-msvc-0.133.0.tgz", - "integrity": "sha512-Una1bNYv9zCavQrfnDR9wuZVB3itLjCEH4Oz7i6CwAJN/Xq9b+zbbcxmvdkKvvJt4Ngc/MBmIYlbLo3zS4TQ0A==", + "version": "0.137.0", + "resolved": "https://registry.npmjs.org/@oxc-parser/binding-win32-ia32-msvc/-/binding-win32-ia32-msvc-0.137.0.tgz", + "integrity": "sha512-sQUqym80PFi6McRsIqfJrSu2JrSClEZIXXD+/FjAFoULEKzOPsldIdFBG96xdX8aVMzCNQ9792FPx3MfkEIrFA==", "dev": true, "optional": true }, "@oxc-parser/binding-win32-x64-msvc": { - "version": "0.133.0", - "resolved": "https://registry.npmjs.org/@oxc-parser/binding-win32-x64-msvc/-/binding-win32-x64-msvc-0.133.0.tgz", - "integrity": "sha512-kjBhCiOGSYTwDJQuuZa7a94JbP8htWu7J0X1KwH74kV2K5eYf6eyJRYmkpCDvr0XEL8tMxYI4WU1VekblFCLgg==", + "version": "0.137.0", + "resolved": "https://registry.npmjs.org/@oxc-parser/binding-win32-x64-msvc/-/binding-win32-x64-msvc-0.137.0.tgz", + "integrity": "sha512-2AsevxlvNN4WKxpEn3RtqD5zbqMaXF+T7JXblsP4gVuY+vC9dXS4ED/PwfRCliFqoeisYS3Iro4DHzxr0TEvVA==", "dev": true, "optional": true }, "@oxc-project/types": { - "version": "0.133.0", - "resolved": "https://registry.npmjs.org/@oxc-project/types/-/types-0.133.0.tgz", - "integrity": "sha512-KzkdCd6Uxqnf6l3HOw1xfatAlUURA0g14cvBYFyJ5SaNOQbOUvBr9PKArcPcrNIeRsBdgcUzOGrhKveVpvOIGA==", + "version": "0.137.0", + "resolved": "https://registry.npmjs.org/@oxc-project/types/-/types-0.137.0.tgz", + "integrity": "sha512-WT+Gb24i8hmvo85AIv2oEYouEXkRlKAlT9WaCa3TfLgNCN+GhrJOGZuIlMouAh38Qe4QOx26eUOVsq70qXrywA==", "dev": true }, "@oxc-resolver/binding-android-arm-eabi": { - "version": "11.20.0", - "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-android-arm-eabi/-/binding-android-arm-eabi-11.20.0.tgz", - "integrity": "sha512-IjfWOXRgJFNdORDl+Uf1aibNgZY2guOD3zmOhx1BGVb/MIiqlFTdmjpQNplSN58lhWehnX4UNqC3QwpUo8pjJg==", + "version": "11.21.3", + "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-android-arm-eabi/-/binding-android-arm-eabi-11.21.3.tgz", + "integrity": "sha512-eNU11A2WNizh04v3uyaJCootrHIaS0B9aHYXvAvVnPNk4xYSjMUjHnhQ6dewPN2MRYDskV85d1N0Aw0WNWhcyg==", "dev": true, "optional": true }, "@oxc-resolver/binding-android-arm64": { - "version": "11.20.0", - "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-android-arm64/-/binding-android-arm64-11.20.0.tgz", - "integrity": "sha512-QqslZAuFQG8Q9xm7JuIn8JUbvywhSBMVhuQHtYW+auirZJloS41oxUUaBXk7uUhZJgp44c5zQLeVvmFaDQB+2Q==", + "version": "11.21.3", + "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-android-arm64/-/binding-android-arm64-11.21.3.tgz", + "integrity": "sha512-8Q+ZjTLvn2dIcWsrmhdrEihm7q+ag/k+mkry7Z+t0QbbHaVxXQfvH9AewyVMh/WrpEKhQ3DDgx9fYbqeCpeOEw==", "dev": true, "optional": true }, "@oxc-resolver/binding-darwin-arm64": { - "version": "11.20.0", - "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-darwin-arm64/-/binding-darwin-arm64-11.20.0.tgz", - "integrity": "sha512-MUcavykj2ewlR+kc5arpg4tC2RvzJkUxWtNv74pf7lcNk00GpIpN43vXMj+j6r4eMmfZhlb8hueKoIb8e9kAGQ==", + "version": "11.21.3", + "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-darwin-arm64/-/binding-darwin-arm64-11.21.3.tgz", + "integrity": "sha512-wkh0qKZGHXVUDxFw3oA1TXnU2BDYY/r775oJflGeIr8uDPPoN2pk8gijQIzYRT6hoql/lg3+Tx/SaTn9e2/aGg==", "dev": true, "optional": true }, "@oxc-resolver/binding-darwin-x64": { - "version": "11.20.0", - "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-darwin-x64/-/binding-darwin-x64-11.20.0.tgz", - "integrity": "sha512-BGB16nRUK5Etiv//ihPyzj8Lj1px0mhh4YIfe0FDf045ywknfSm0GEbiRESpr6Q4K82AvnyaRIhhluHByvS4bg==", + "version": "11.21.3", + "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-darwin-x64/-/binding-darwin-x64-11.21.3.tgz", + "integrity": "sha512-HbNc23FAQYbuyDV2vBWMez4u4mrsm5RAkniGZAWqr6lYZ3N4beeqIb776jzwRl8qL2zRhHVXpUj97X0QgogVzg==", "dev": true, "optional": true }, "@oxc-resolver/binding-freebsd-x64": { - "version": "11.20.0", - "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-freebsd-x64/-/binding-freebsd-x64-11.20.0.tgz", - "integrity": "sha512-JZgtePaqj3qmD5XFHJaSLWzHRxQu0LaPkdoM1KJXYADvAaa83ijXHclV3ej3CueeW0wxfIAbGCZVP45J0CA7uQ==", + "version": "11.21.3", + "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-freebsd-x64/-/binding-freebsd-x64-11.21.3.tgz", + "integrity": "sha512-K6xNsTUPEUdfrn0+kbMq5nOUB5w1C5pavPQngt4TM2FpN91lP0PBe2srSpamb4d69O7h86oAi/qWX/kZNRSjkw==", "dev": true, "optional": true }, "@oxc-resolver/binding-linux-arm-gnueabihf": { - "version": "11.20.0", - "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-11.20.0.tgz", - "integrity": "sha512-hOQ/p3ry3v3SchUBXicrrnszaI/UmYzM4wtS4RGfwgVUX7a+HbyQSzJ5aOzu+o6XZkFkS3ZXN4PZAzhOb77OSg==", + "version": "11.21.3", + "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-11.21.3.tgz", + "integrity": "sha512-VcFmOpcpWX1zoEy8M58tR2M9YxM+Z9RuQhqAx5q0CTmrruaP7Gveejg75hzd/5sg5nk9G3aLALEa3hE2FsmmTQ==", "dev": true, "optional": true }, "@oxc-resolver/binding-linux-arm-musleabihf": { - "version": "11.20.0", - "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-linux-arm-musleabihf/-/binding-linux-arm-musleabihf-11.20.0.tgz", - "integrity": "sha512-2ArPksaw0AqeuGBfoS715VF+JvJQAhD2niWgjE5hVO+L+nAfikVQopvngCMX9x4BD8itWoQ3dnikrQyl5Ho5Jg==", + "version": "11.21.3", + "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-linux-arm-musleabihf/-/binding-linux-arm-musleabihf-11.21.3.tgz", + "integrity": "sha512-quVoxFLBy43hWaQbbDtQNRwAX5vX76mv7n64icAtQcJ3eNgVeblqmkupF/hAneNthdqSlnd1sTjb3aQSaDPaCQ==", "dev": true, "optional": true }, "@oxc-resolver/binding-linux-arm64-gnu": { - "version": "11.20.0", - "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-11.20.0.tgz", - "integrity": "sha512-0bJnmYFp62JdZ4nVMDUZ/C58BCZOCcqgKtnUlp7L9Ojf/czIN+3j72YlLPeWLkzlr6SlYvIQA4SGV/HyO0d+qg==", + "version": "11.21.3", + "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-11.21.3.tgz", + "integrity": "sha512-X0AqNZgcD07Q4V3RDK18/vYOj/HQT/FnmEFGYS2jTWqY7JO13ryE3TEs3eAIgUJhBnNkpEaiXqz3VK8M7qQhWQ==", "dev": true, "optional": true }, "@oxc-resolver/binding-linux-arm64-musl": { - "version": "11.20.0", - "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-linux-arm64-musl/-/binding-linux-arm64-musl-11.20.0.tgz", - "integrity": "sha512-wKHHzPKZo7Ufhv/Bt6yxT7FOgnIgW4gwXcJUipkShGp68W3wGVqvr1Sr0fY65lN0Oy6y41+g2kIDvkgZaMMUkw==", + "version": "11.21.3", + "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-linux-arm64-musl/-/binding-linux-arm64-musl-11.21.3.tgz", + "integrity": "sha512-YkaQnaKYdbuaXvRt5Qd0GpbihzVnyfR6z1SpYfIUC6RTu4NF7lDKPjVkYb+jRI2gedVO2rVpN35Y6akG6ud4Lw==", "dev": true, "optional": true }, "@oxc-resolver/binding-linux-ppc64-gnu": { - "version": "11.20.0", - "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-11.20.0.tgz", - "integrity": "sha512-RN8goF7Ie0B79L4i4G6OeBocTgSC56vJbQ65VJje+oXnldVpLnOU7j/AQ/dP94TcCS+Yh6WG8u3Qt4ETteXFNQ==", + "version": "11.21.3", + "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-11.21.3.tgz", + "integrity": "sha512-gB9HwhrPiFqUzDeEq+y/CgAijz1YdI6BnXz5GaH2Pa9cWdutchlkGFAiAuGb/PjVQpiK6NFKzFuztxrweoit7A==", "dev": true, "optional": true }, "@oxc-resolver/binding-linux-riscv64-gnu": { - "version": "11.20.0", - "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-linux-riscv64-gnu/-/binding-linux-riscv64-gnu-11.20.0.tgz", - "integrity": "sha512-5l1yU6/xQEqLZRzxqmMxJfWPslpwCmBsdDGaBvABPehxquCXDC7dd7oraNdKSJUMDXSM7VvVj8H2D2FTjU7oWw==", + "version": "11.21.3", + "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-linux-riscv64-gnu/-/binding-linux-riscv64-gnu-11.21.3.tgz", + "integrity": "sha512-zjDWBlYk8QGv0H8dsPUWqkfjYIIjG2TvspGkzXL0eImbgxtZorA/klKeHyolevoT3Kvbi+1iMr9Lhrh7jf54Og==", "dev": true, "optional": true }, "@oxc-resolver/binding-linux-riscv64-musl": { - "version": "11.20.0", - "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-linux-riscv64-musl/-/binding-linux-riscv64-musl-11.20.0.tgz", - "integrity": "sha512-xHEvkbgz6UC+A3JOyDQy76LkUaxsNSfIr3/GV8slwZsnuooJiIB34gzJfsyvR4JdCYNUUPsRJc/w/oWkODu+hg==", + "version": "11.21.3", + "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-linux-riscv64-musl/-/binding-linux-riscv64-musl-11.21.3.tgz", + "integrity": "sha512-4UfsQvacV388y1zpXL7C1x1FNYaV52JtuNRiuzrfQA2z1z6ElVrsidkGsrvQ5EgeSq1Pj7kaKqrgGkvFuxJ/tw==", "dev": true, "optional": true }, "@oxc-resolver/binding-linux-s390x-gnu": { - "version": "11.20.0", - "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-11.20.0.tgz", - "integrity": "sha512-aWPDUUmSeyHvlW+SoEUd+JIJsQhVhu6a5tBpDRMu058naPAchTgAVGCFy35zjbnFlt0i8hLWziff6HX0D3LU4g==", + "version": "11.21.3", + "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-11.21.3.tgz", + "integrity": "sha512-b5uH+HKH0MP5mNBYaK75SKsJbw52URqrx2LavYdq6wb0l3ExAG5niYRP9DWUNHdKilpaBVM2bXk9HNWrH3ew7Q==", "dev": true, "optional": true }, "@oxc-resolver/binding-linux-x64-gnu": { - "version": "11.20.0", - "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-linux-x64-gnu/-/binding-linux-x64-gnu-11.20.0.tgz", - "integrity": "sha512-x2YeSimvhJjKLVD8KSu8f/rqU1potcdEMkApIPJqjZWN7c2Fpt4g2X32WDg1p+XDAmyT7nuQGe0vnhvXeLbH+g==", + "version": "11.21.3", + "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-linux-x64-gnu/-/binding-linux-x64-gnu-11.21.3.tgz", + "integrity": "sha512-PjYlmilBpNRh2ntXNYAK3Am5w/nPfEpnU/96iNx7CI8EzAn12J4JRiec63wHJTH31nLoCNxBg/829pN+3CfG3Q==", "dev": true, "optional": true }, "@oxc-resolver/binding-linux-x64-musl": { - "version": "11.20.0", - "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-linux-x64-musl/-/binding-linux-x64-musl-11.20.0.tgz", - "integrity": "sha512-kcRLEIxpZefeYfLChjpgFf3ilBzRDZ+yobMrpRsQlSrxuFGtm3U6PMU7AaEpMqo3NfDGVyJJseAjnRLzMFHjwQ==", + "version": "11.21.3", + "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-linux-x64-musl/-/binding-linux-x64-musl-11.21.3.tgz", + "integrity": "sha512-QTBAb7JuHlZ7JUEyM8UiQi2f7m/L4swBhP2TNpYIDc9Wp/wRw1G/8sl6i13aIzQAXH7LKIm294LeOHd0lQR8zA==", "dev": true, "optional": true }, "@oxc-resolver/binding-openharmony-arm64": { - "version": "11.20.0", - "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-openharmony-arm64/-/binding-openharmony-arm64-11.20.0.tgz", - "integrity": "sha512-HHcfnApSZGtKhTiHqe8OZruOZe5XuFQH5/E0Yhj3u8fnFvzkM4/k6WjacUf4SvA0SPEAbfbgYmVPuo0VX/fIBQ==", + "version": "11.21.3", + "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-openharmony-arm64/-/binding-openharmony-arm64-11.21.3.tgz", + "integrity": "sha512-4j1DFwjwv36ec9kds0jU/ucQ5Ha4ERO/H95BxR5JFf0kqUUAJ1kwII7XhTc1vZrkdJkvLGC9Q2MbpObpum8RBg==", "dev": true, "optional": true }, "@oxc-resolver/binding-wasm32-wasi": { - "version": "11.20.0", - "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-wasm32-wasi/-/binding-wasm32-wasi-11.20.0.tgz", - "integrity": "sha512-Tn0y1XOFYHNfK1wp1Z5QK8Rcld/bsOwRISQXfqAZ5IBpv8Gz1IvV39fUWNprqNdRizgcvFhOzWwFun2zkJsyBg==", + "version": "11.21.3", + "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-wasm32-wasi/-/binding-wasm32-wasi-11.21.3.tgz", + "integrity": "sha512-i8oluoel5kru/j1WNrjmQSiA3GQ7wvIYVR1IwIoZtKogAhya2iub+ZKIeSIkcJOrnzQ18Tzl/F+kL3fYOxZLvA==", "dev": true, "optional": true, "requires": { - "@emnapi/core": "1.10.0", - "@emnapi/runtime": "1.10.0", - "@napi-rs/wasm-runtime": "^1.1.4" + "@emnapi/core": "1.11.0", + "@emnapi/runtime": "1.11.0", + "@napi-rs/wasm-runtime": "^1.1.5" + }, + "dependencies": { + "@emnapi/core": { + "version": "1.11.0", + "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.11.0.tgz", + "integrity": "sha512-l9Oo58x0HOP5znGzVhYW9U3e5wVuA4LAZU2AGezTmkhO1CgQRFDhDg4nneHsu/t3WniXg9QrG2nIXL/ZS8ln8Q==", + "dev": true, + "optional": true, + "requires": { + "@emnapi/wasi-threads": "1.2.2", + "tslib": "^2.4.0" + } + }, + "@emnapi/runtime": { + "version": "1.11.0", + "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.11.0.tgz", + "integrity": "sha512-55coeOFKHv1ywEcUXJtWU5f+Jr/W5tZDvZig8DLKSwUN1JpROQ4rk/SNOQiFWmaR/VKF4zuFyW1B8JduOSv6Pg==", + "dev": true, + "optional": true, + "requires": { + "tslib": "^2.4.0" + } + } } }, "@oxc-resolver/binding-win32-arm64-msvc": { - "version": "11.20.0", - "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-11.20.0.tgz", - "integrity": "sha512-qPi25YNPe4YenS8MgsQU2+bIFHxxpLx1LVna2444cEHqNPhNjvWf9zqj4aWE43H9LpAsTmkkAlA3eL5ElBU3mA==", + "version": "11.21.3", + "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-11.21.3.tgz", + "integrity": "sha512-M/8dw8dD6aOs+NlPJax401CZB9I7Aut84isQLgALGGwke4Afvw+/7yYhZb94yXf6t2sPLhQLmSmtSV+2FhsOWg==", "dev": true, "optional": true }, "@oxc-resolver/binding-win32-x64-msvc": { - "version": "11.20.0", - "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-win32-x64-msvc/-/binding-win32-x64-msvc-11.20.0.tgz", - "integrity": "sha512-Wb14jWEW8huH6It9F6sXd9vrYmIS7pMrgkU6sxpLxkP+9z+wRgs71hUEhRpcn8FOXAFa27FVWfY2tRpbfTzfLw==", + "version": "11.21.3", + "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-win32-x64-msvc/-/binding-win32-x64-msvc-11.21.3.tgz", + "integrity": "sha512-H7BCt/VnS9hnmMp42eGhZ99izSCRvlnWwy/N71K1/J8QoExwY4262Z8QiEkMDtduRJrztayDxETTckmUuAVL9Q==", "dev": true, "optional": true }, "@rushstack/node-core-library": { - "version": "5.13.0", - "resolved": "https://registry.npmjs.org/@rushstack/node-core-library/-/node-core-library-5.13.0.tgz", - "integrity": "sha512-IGVhy+JgUacAdCGXKUrRhwHMTzqhWwZUI+qEPcdzsb80heOw0QPbhhoVsoiMF7Klp8eYsp7hzpScMXmOa3Uhfg==", + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/@rushstack/node-core-library/-/node-core-library-4.0.2.tgz", + "integrity": "sha512-hyES82QVpkfQMeBMteQUnrhASL/KHPhd7iJ8euduwNJG4mu2GSOKybf0rOEjOm1Wz7CwJEUm9y0yD7jg2C1bfg==", "requires": { - "ajv": "~8.18.0", - "ajv-draft-04": "~1.0.0", - "ajv-formats": "~3.0.1", - "fs-extra": "~11.3.0", + "fs-extra": "~7.0.1", "import-lazy": "~4.0.0", "jju": "~1.4.0", "resolve": "~1.22.1", - "semver": "~7.5.4" + "semver": "~7.5.4", + "z-schema": "~5.0.2" } }, "@rushstack/terminal": { - "version": "0.15.2", - "resolved": "https://registry.npmjs.org/@rushstack/terminal/-/terminal-0.15.2.tgz", - "integrity": "sha512-7Hmc0ysK5077R/IkLS9hYu0QuNafm+TbZbtYVzCMbeOdMjaRboLKrhryjwZSRJGJzu+TV1ON7qZHeqf58XfLpA==", + "version": "0.10.0", + "resolved": "https://registry.npmjs.org/@rushstack/terminal/-/terminal-0.10.0.tgz", + "integrity": "sha512-UbELbXnUdc7EKwfH2sb8ChqNgapUOdqcCIdQP4NGxBpTZV2sQyeekuK3zmfQSa/MN+/7b4kBogl2wq0vpkpYGw==", "requires": { - "@rushstack/node-core-library": "5.13.0", + "@rushstack/node-core-library": "4.0.2", "supports-color": "~8.1.1" } }, "@rushstack/ts-command-line": { - "version": "4.23.7", - "resolved": "https://registry.npmjs.org/@rushstack/ts-command-line/-/ts-command-line-4.23.7.tgz", - "integrity": "sha512-Gr9cB7DGe6uz5vq2wdr89WbVDKz0UeuFEn5H2CfWDe7JvjFFaiV15gi6mqDBTbHhHCWS7w8mF1h3BnIfUndqdA==", + "version": "4.19.1", + "resolved": "https://registry.npmjs.org/@rushstack/ts-command-line/-/ts-command-line-4.19.1.tgz", + "integrity": "sha512-J7H768dgcpG60d7skZ5uSSwyCZs/S2HrWP1Ds8d1qYAyaaeJmpmmLr9BVw97RjFzmQPOYnoXcKA4GkqDCkduQg==", "requires": { - "@rushstack/terminal": "0.15.2", + "@rushstack/terminal": "0.10.0", "@types/argparse": "1.0.38", "argparse": "~1.0.9", "string-argv": "~0.3.1" @@ -2544,9 +2161,9 @@ "integrity": "sha512-cka47fVSo6lfQDIATYqb/vO1nvFfbPw7uWLayIXIhGETj0wcOOlrlkobOMDNQOFr9QOafegUPq13V2+6vtD7yg==" }, "@tybys/wasm-util": { - "version": "0.10.2", - "resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.10.2.tgz", - "integrity": "sha512-RoBvJ2X0wuKlWFIjrwffGw1IqZHKQqzIchKaadZZfnNpsAYp2mM0h36JtPCjNDAHGgYez/15uMBpfGwchhiMgg==", + "version": "0.10.3", + "resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.10.3.tgz", + "integrity": "sha512-F3fo1MYrRJYL3zER0OUOmkutjr1Vp23m7OsSgp7nq4SP6OqX6C/56XFIPAl5bt3zaBRjmW7SGz3u/6LwFpYcOg==", "dev": true, "optional": true, "requires": { @@ -2572,11 +2189,11 @@ "integrity": "sha512-iiUgKzV9AuaEkZqkOLDIvlQiL6ltuZd9tGcW3gwpnX8JbuiuhFlEGmmFXEXkN50Cvq7Os88IY2v0dkDqXYWVgA==" }, "@types/node": { - "version": "24.12.2", - "resolved": "https://registry.npmjs.org/@types/node/-/node-24.12.2.tgz", - "integrity": "sha512-A1sre26ke7HDIuY/M23nd9gfB+nrmhtYyMINbjI1zHJxYteKR6qSMX56FsmjMcDb3SMcjJg5BiRRgOCC/yBD0g==", + "version": "24.13.3", + "resolved": "https://registry.npmjs.org/@types/node/-/node-24.13.3.tgz", + "integrity": "sha512-Dh8vAsV36ig5wa9OX4pXvMc9D3Veibfw2wix0CUwYODLD8nkj9UsLjASr49nPg+2eKzxhBV+v7L8pXvT4e639Q==", "requires": { - "undici-types": "~7.16.0" + "undici-types": "~7.18.0" } }, "@types/validator": { @@ -2604,31 +2221,6 @@ "resolved": "https://registry.npmjs.org/adm-zip/-/adm-zip-0.5.10.tgz", "integrity": "sha512-x0HvcHqVJNTPk/Bw8JbLWlWoo6Wwnsug0fnYYro1HBrjxZ3G7/AZk7Ahv8JwDe1uIcz8eBqvu86FuF1POiG7vQ==" }, - "ajv": { - "version": "8.18.0", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.18.0.tgz", - "integrity": "sha512-PlXPeEWMXMZ7sPYOHqmDyCJzcfNrUr3fGNKtezX14ykXOEIvyK81d+qydx89KY5O71FKMPaQ2vBfBFI5NHR63A==", - "requires": { - "fast-deep-equal": "^3.1.3", - "fast-uri": "^3.1.2", - "json-schema-traverse": "^1.0.0", - "require-from-string": "^2.0.2" - } - }, - "ajv-draft-04": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/ajv-draft-04/-/ajv-draft-04-1.0.0.tgz", - "integrity": "sha512-mv00Te6nmYbRp5DCwclxtt7yV/joXJPGS7nM+97GdxvuttCOfgI3K4U25zboyeX0O+myI8ERluxQe5wljMmVIw==", - "requires": {} - }, - "ajv-formats": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-3.0.1.tgz", - "integrity": "sha512-8iUql50EUR+uUcdRQ3HDqa6EVyo3docL8g5WJ3FNcWmu62IbkGUue/pEyLBW8VGKKucTPgqeks4fIU1DA4yowQ==", - "requires": { - "ajv": "~8.18.0" - } - }, "ansi-regex": { "version": "6.2.2", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.2.tgz", @@ -2647,36 +2239,28 @@ "sprintf-js": "~1.0.2" } }, - "braces": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", - "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", - "requires": { - "fill-range": "^7.1.1" - } - }, "cassandra-driver": { - "version": "4.8.0", - "resolved": "https://registry.npmjs.org/cassandra-driver/-/cassandra-driver-4.8.0.tgz", - "integrity": "sha512-HritfMGq9V7SuESeSodHvArs0mLuMk7uh+7hQK2lqdvXrvm50aWxb4RPxkK3mPDdsgHjJ427xNRFITMH2ei+Sw==", + "version": "4.9.0", + "resolved": "https://registry.npmjs.org/cassandra-driver/-/cassandra-driver-4.9.0.tgz", + "integrity": "sha512-svYpdkLIGjD0WmuuwkkeYbfBdPX1zksK2cDyT1mWjX53OVTzuWBOVy54K6PPij8GgYpIG+K82OryrBv/xNeuWg==", "requires": { - "@types/node": "^18.11.18", + "@types/node": "^20.14.8", "adm-zip": "~0.5.10", "long": "~5.2.3" }, "dependencies": { "@types/node": { - "version": "18.19.130", - "resolved": "https://registry.npmjs.org/@types/node/-/node-18.19.130.tgz", - "integrity": "sha512-GRaXQx6jGfL8sKfaIDD6OupbIHBr9jv7Jnaml9tB7l4v068PAOXqfcujMMo5PhbIs6ggR1XODELqahT2R8v0fg==", + "version": "20.19.43", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.19.43.tgz", + "integrity": "sha512-6oYBAi5ikg4Pl+kGsoYtawUMBT2zZMCvPNF7pVLnHZfd1zf38DRiWn/gT01RYCdUqkv7Fhr+C9ot4/tb+2sVvA==", "requires": { - "undici-types": "~5.26.4" + "undici-types": "~6.21.0" } }, "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.21.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz", + "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==" } } }, @@ -2690,6 +2274,12 @@ "wrap-ansi": "^9.0.0" } }, + "commander": { + "version": "9.5.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-9.5.0.tgz", + "integrity": "sha512-KRs7WVDKg86PWiuAqhDrAQnTXZKraVcCc6vFdL14qrZ/DcWwuRo7VoiYXalXO7S5GKpqYiVEwCbgFDfxNHKJBQ==", + "optional": true + }, "debug": { "version": "4.3.4", "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", @@ -2723,36 +2313,6 @@ "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==" }, - "fast-deep-equal": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", - "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==" - }, - "fast-glob": { - "version": "3.3.3", - "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz", - "integrity": "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==", - "requires": { - "@nodelib/fs.stat": "^2.0.2", - "@nodelib/fs.walk": "^1.2.3", - "glob-parent": "^5.1.2", - "merge2": "^1.3.0", - "micromatch": "^4.0.8" - } - }, - "fast-uri": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.2.tgz", - "integrity": "sha512-rVjf7ArG3LTk+FS6Yw81V1DLuZl1bRbNrev6Tmd/9RaroeeRRJhAt7jg/6YFxbvAQXUCavSoZhPPj6oOx+5KjQ==" - }, - "fastq": { - "version": "1.20.1", - "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.20.1.tgz", - "integrity": "sha512-GGToxJ/w1x32s/D2EKND7kTil4n8OVk/9mycTc4VDza13lOvpUZTGX3mFSCtV9ksdGBVzvsyAVLM6mHFThxXxw==", - "requires": { - "reusify": "^1.0.4" - } - }, "fd-package-json": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/fd-package-json/-/fd-package-json-2.0.0.tgz", @@ -2762,14 +2322,6 @@ "walk-up-path": "^4.0.0" } }, - "fill-range": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", - "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", - "requires": { - "to-regex-range": "^5.0.1" - } - }, "formatly": { "version": "0.3.0", "resolved": "https://registry.npmjs.org/formatly/-/formatly-0.3.0.tgz", @@ -2780,13 +2332,13 @@ } }, "fs-extra": { - "version": "11.3.4", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.3.4.tgz", - "integrity": "sha512-CTXd6rk/M3/ULNQj8FBqBWHYBVYybQ3VPBw0xGKFe3tuH7ytT6ACnvzpIQ3UZtB8yvUKC2cXn1a+x+5EVQLovA==", + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-7.0.1.tgz", + "integrity": "sha512-YJDaCJZEnBmcbw13fvdAM9AwNOJwOzrE4pqMqBq5nFiEqXUqHwlK4B+3pUw6JNvfSPtX05xFHtYy/1ni01eGCw==", "requires": { - "graceful-fs": "^4.2.0", - "jsonfile": "^6.0.1", - "universalify": "^2.0.0" + "graceful-fs": "^4.1.2", + "jsonfile": "^4.0.0", + "universalify": "^0.1.0" } }, "function-bind": { @@ -2813,14 +2365,6 @@ "resolve-pkg-maps": "^1.0.0" } }, - "glob-parent": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", - "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", - "requires": { - "is-glob": "^4.0.1" - } - }, "graceful-fs": { "version": "4.2.11", "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", @@ -2832,9 +2376,9 @@ "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==" }, "hasown": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.3.tgz", - "integrity": "sha512-ej4AhfhfL2Q2zpMmLo7U1Uv9+PyhIZpgQLGT1F9miIGmiCJIoCgSmczFdrc97mWT4kVY72KA+WnnhJ5pghSvSg==", + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.4.tgz", + "integrity": "sha512-T2UbfbBEF32wiepXIsMlTW9+dDYC6wMh/t/vYA4tuOMKqWz/n3vr1NFSxQiyP+zk2mXsoMA/i/7qV6LKut1t1A==", "requires": { "function-bind": "^1.1.2" } @@ -2850,31 +2394,13 @@ "integrity": "sha512-6I/HUDeYFfuNCVS3td055BaXBwKYuzw7K3ExVMStBowKo9oOAMJIXIHvdyR3iboTCp1b+1i5DSkIZTcwIktuDw==" }, "is-core-module": { - "version": "2.16.1", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.1.tgz", - "integrity": "sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==", + "version": "2.16.2", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.2.tgz", + "integrity": "sha512-evOr8xfXKxE6qSR0hSXL2r3sd7ALj8+7jQEUvPYcm5sgZFdJ+AYzT6yNmJenvIYQBgIGwfwz08sL8zoL7yq2BA==", "requires": { - "hasown": "^2.0.2" + "hasown": "^2.0.3" } }, - "is-extglob": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", - "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==" - }, - "is-glob": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", - "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", - "requires": { - "is-extglob": "^2.1.1" - } - }, - "is-number": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", - "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==" - }, "jiti": { "version": "2.7.0", "resolved": "https://registry.npmjs.org/jiti/-/jiti-2.7.0.tgz", @@ -2886,37 +2412,31 @@ "resolved": "https://registry.npmjs.org/jju/-/jju-1.4.0.tgz", "integrity": "sha512-8wb9Yw966OSxApiCt0K3yNJL8pnNeIv+OEq2YMidz4FKP6nonSRoOXc80iXY4JaN2FC11B9qsNmDsm+ZOfMROA==" }, - "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==" - }, "jsonfile": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.2.0.tgz", - "integrity": "sha512-FGuPw30AdOIUTRMC2OMRtQV+jkVj2cfPqSeWXv1NEAJ1qZ5zb1X6z1mFhbfOB/iy3ssJCD+3KuZ8r8C3uVFlAg==", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz", + "integrity": "sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==", "requires": { - "graceful-fs": "^4.1.6", - "universalify": "^2.0.0" + "graceful-fs": "^4.1.6" } }, "knip": { - "version": "6.16.1", - "resolved": "https://registry.npmjs.org/knip/-/knip-6.16.1.tgz", - "integrity": "sha512-TKMn1rxgH6h9vXR9Y0B+Cq7AdPTr9EI02IwoT65NzqYUkvoDQAaJ/aPybiFpAhZ1px6cNYYwXf86iHkBgzCo9w==", + "version": "6.27.0", + "resolved": "https://registry.npmjs.org/knip/-/knip-6.27.0.tgz", + "integrity": "sha512-CngYEYrD0n20N06FXA8n3u/0Wnnugoa+B9k14OP+iKIgkCHuzvIdsP3nfwjhByoc1WfogpxfiriMboAXFETDUw==", "dev": true, "requires": { "fdir": "^6.5.0", "formatly": "^0.3.0", "get-tsconfig": "4.14.0", "jiti": "^2.7.0", - "oxc-parser": "^0.133.0", - "oxc-resolver": "^11.20.0", + "oxc-parser": "^0.137.0", + "oxc-resolver": "11.21.3", "picomatch": "^4.0.4", "smol-toml": "^1.6.1", "strip-json-comments": "5.0.3", - "tinyglobby": "^0.2.16", - "unbash": "^3.0.0", + "tinyglobby": "^0.2.17", + "unbash": "^4.0.1", "yaml": "^2.9.0", "zod": "^4.1.11" }, @@ -2941,6 +2461,16 @@ "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.18.1.tgz", "integrity": "sha512-dMInicTPVE8d1e5otfwmmjlxkZoUpiVLwyeTdUsi/Caj/gfzzblBcCE5sRHV/AsjuCmxWrte2TNGSYuCeCq+0Q==" }, + "lodash.get": { + "version": "4.4.2", + "resolved": "https://registry.npmjs.org/lodash.get/-/lodash.get-4.4.2.tgz", + "integrity": "sha512-z+Uw/vLuy6gQe8cfaFWD7p0wVv8fJl3mbzXh33RS+0oW2wvUqiRXiQ69gLWSLpgB5/6sU+r6BlQR0MBILadqTQ==" + }, + "lodash.isequal": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.isequal/-/lodash.isequal-4.5.0.tgz", + "integrity": "sha512-pDo3lu8Jhfjqls6GkMgpahsF9kCyayhgykjyLMNFTKWrpVdAQtYyB4muAMWozBB4ig/dtWAmsMxLEI8wuz+DYQ==" + }, "long": { "version": "5.2.5", "resolved": "https://registry.npmjs.org/long/-/long-5.2.5.tgz", @@ -2954,20 +2484,6 @@ "yallist": "^4.0.0" } }, - "merge2": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", - "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==" - }, - "micromatch": { - "version": "4.0.8", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", - "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", - "requires": { - "braces": "^3.0.3", - "picomatch": "^2.3.1" - } - }, "moment": { "version": "2.29.4", "resolved": "https://registry.npmjs.org/moment/-/moment-2.29.4.tgz", @@ -2987,59 +2503,59 @@ "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" }, "oxc-parser": { - "version": "0.133.0", - "resolved": "https://registry.npmjs.org/oxc-parser/-/oxc-parser-0.133.0.tgz", - "integrity": "sha512-661RSx+ZcjBmjBYid+Fpp/2F5EbtildpeoZh5HdgnGs+jZ03nqQEQW8yGkt4BGyOC3OMPDQQRl8M5kqD2/g6jw==", + "version": "0.137.0", + "resolved": "https://registry.npmjs.org/oxc-parser/-/oxc-parser-0.137.0.tgz", + "integrity": "sha512-yFImD+WLElJpLKy8llG1qe4DCmMsL18peRp8XP1JKfig/gISbJkglnpDtX2aTmAn10kZF7164HbN2H8QPsXxGg==", "dev": true, "requires": { - "@oxc-parser/binding-android-arm-eabi": "0.133.0", - "@oxc-parser/binding-android-arm64": "0.133.0", - "@oxc-parser/binding-darwin-arm64": "0.133.0", - "@oxc-parser/binding-darwin-x64": "0.133.0", - "@oxc-parser/binding-freebsd-x64": "0.133.0", - "@oxc-parser/binding-linux-arm-gnueabihf": "0.133.0", - "@oxc-parser/binding-linux-arm-musleabihf": "0.133.0", - "@oxc-parser/binding-linux-arm64-gnu": "0.133.0", - "@oxc-parser/binding-linux-arm64-musl": "0.133.0", - "@oxc-parser/binding-linux-ppc64-gnu": "0.133.0", - "@oxc-parser/binding-linux-riscv64-gnu": "0.133.0", - "@oxc-parser/binding-linux-riscv64-musl": "0.133.0", - "@oxc-parser/binding-linux-s390x-gnu": "0.133.0", - "@oxc-parser/binding-linux-x64-gnu": "0.133.0", - "@oxc-parser/binding-linux-x64-musl": "0.133.0", - "@oxc-parser/binding-openharmony-arm64": "0.133.0", - "@oxc-parser/binding-wasm32-wasi": "0.133.0", - "@oxc-parser/binding-win32-arm64-msvc": "0.133.0", - "@oxc-parser/binding-win32-ia32-msvc": "0.133.0", - "@oxc-parser/binding-win32-x64-msvc": "0.133.0", - "@oxc-project/types": "^0.133.0" + "@oxc-parser/binding-android-arm-eabi": "0.137.0", + "@oxc-parser/binding-android-arm64": "0.137.0", + "@oxc-parser/binding-darwin-arm64": "0.137.0", + "@oxc-parser/binding-darwin-x64": "0.137.0", + "@oxc-parser/binding-freebsd-x64": "0.137.0", + "@oxc-parser/binding-linux-arm-gnueabihf": "0.137.0", + "@oxc-parser/binding-linux-arm-musleabihf": "0.137.0", + "@oxc-parser/binding-linux-arm64-gnu": "0.137.0", + "@oxc-parser/binding-linux-arm64-musl": "0.137.0", + "@oxc-parser/binding-linux-ppc64-gnu": "0.137.0", + "@oxc-parser/binding-linux-riscv64-gnu": "0.137.0", + "@oxc-parser/binding-linux-riscv64-musl": "0.137.0", + "@oxc-parser/binding-linux-s390x-gnu": "0.137.0", + "@oxc-parser/binding-linux-x64-gnu": "0.137.0", + "@oxc-parser/binding-linux-x64-musl": "0.137.0", + "@oxc-parser/binding-openharmony-arm64": "0.137.0", + "@oxc-parser/binding-wasm32-wasi": "0.137.0", + "@oxc-parser/binding-win32-arm64-msvc": "0.137.0", + "@oxc-parser/binding-win32-ia32-msvc": "0.137.0", + "@oxc-parser/binding-win32-x64-msvc": "0.137.0", + "@oxc-project/types": "^0.137.0" } }, "oxc-resolver": { - "version": "11.20.0", - "resolved": "https://registry.npmjs.org/oxc-resolver/-/oxc-resolver-11.20.0.tgz", - "integrity": "sha512-CblytBiV/a/ZXY34dsVU2NxhIOxMXst8CvDCtyBelVITgd7PLrKzbEbA6oKLdPjvDKDzCiW48qzmzZ+mYaqn+g==", + "version": "11.21.3", + "resolved": "https://registry.npmjs.org/oxc-resolver/-/oxc-resolver-11.21.3.tgz", + "integrity": "sha512-2Mx3fKQz7+xgrBONjsxOgCGtMHOn38/HxMzW1I5efwXB5a4lRN0Vp40gYUJFBWJslcrvwoofTrqoTnLbwTd3pA==", "dev": true, "requires": { - "@oxc-resolver/binding-android-arm-eabi": "11.20.0", - "@oxc-resolver/binding-android-arm64": "11.20.0", - "@oxc-resolver/binding-darwin-arm64": "11.20.0", - "@oxc-resolver/binding-darwin-x64": "11.20.0", - "@oxc-resolver/binding-freebsd-x64": "11.20.0", - "@oxc-resolver/binding-linux-arm-gnueabihf": "11.20.0", - "@oxc-resolver/binding-linux-arm-musleabihf": "11.20.0", - "@oxc-resolver/binding-linux-arm64-gnu": "11.20.0", - "@oxc-resolver/binding-linux-arm64-musl": "11.20.0", - "@oxc-resolver/binding-linux-ppc64-gnu": "11.20.0", - "@oxc-resolver/binding-linux-riscv64-gnu": "11.20.0", - "@oxc-resolver/binding-linux-riscv64-musl": "11.20.0", - "@oxc-resolver/binding-linux-s390x-gnu": "11.20.0", - "@oxc-resolver/binding-linux-x64-gnu": "11.20.0", - "@oxc-resolver/binding-linux-x64-musl": "11.20.0", - "@oxc-resolver/binding-openharmony-arm64": "11.20.0", - "@oxc-resolver/binding-wasm32-wasi": "11.20.0", - "@oxc-resolver/binding-win32-arm64-msvc": "11.20.0", - "@oxc-resolver/binding-win32-x64-msvc": "11.20.0" + "@oxc-resolver/binding-android-arm-eabi": "11.21.3", + "@oxc-resolver/binding-android-arm64": "11.21.3", + "@oxc-resolver/binding-darwin-arm64": "11.21.3", + "@oxc-resolver/binding-darwin-x64": "11.21.3", + "@oxc-resolver/binding-freebsd-x64": "11.21.3", + "@oxc-resolver/binding-linux-arm-gnueabihf": "11.21.3", + "@oxc-resolver/binding-linux-arm-musleabihf": "11.21.3", + "@oxc-resolver/binding-linux-arm64-gnu": "11.21.3", + "@oxc-resolver/binding-linux-arm64-musl": "11.21.3", + "@oxc-resolver/binding-linux-ppc64-gnu": "11.21.3", + "@oxc-resolver/binding-linux-riscv64-gnu": "11.21.3", + "@oxc-resolver/binding-linux-riscv64-musl": "11.21.3", + "@oxc-resolver/binding-linux-s390x-gnu": "11.21.3", + "@oxc-resolver/binding-linux-x64-gnu": "11.21.3", + "@oxc-resolver/binding-linux-x64-musl": "11.21.3", + "@oxc-resolver/binding-openharmony-arm64": "11.21.3", + "@oxc-resolver/binding-wasm32-wasi": "11.21.3", + "@oxc-resolver/binding-win32-arm64-msvc": "11.21.3", + "@oxc-resolver/binding-win32-x64-msvc": "11.21.3" } }, "path-parse": { @@ -3052,26 +2568,11 @@ "resolved": "https://registry.npmjs.org/pg-connection-string/-/pg-connection-string-2.6.2.tgz", "integrity": "sha512-ch6OwaeaPYcova4kKZ15sbJ2hKb/VP48ZD2gE7i1J+L4MspCtBMAx8nMgz7bksc7IojCIIWuEhHibSMFH8m8oA==" }, - "picomatch": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.2.tgz", - "integrity": "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==" - }, "pony-cause": { "version": "2.1.11", "resolved": "https://registry.npmjs.org/pony-cause/-/pony-cause-2.1.11.tgz", "integrity": "sha512-M7LhCsdNbNgiLYiP4WjsfLUuFmCfnjdF6jKe2R9NKl4WFN+HZPGHJZ9lnLP7f9ZnKe3U9nuWD0szirmj+migUg==" }, - "queue-microtask": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", - "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==" - }, - "require-from-string": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", - "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==" - }, "resolve": { "version": "1.22.12", "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.12.tgz", @@ -3094,19 +2595,6 @@ "resolved": "https://registry.npmjs.org/retry-as-promised/-/retry-as-promised-7.0.4.tgz", "integrity": "sha512-XgmCoxKWkDofwH8WddD0w85ZfqYz+ZHlr5yo+3YUCfycWawU56T5ckWXsScsj5B8tqUcIG67DxXByo3VUgiAdA==" }, - "reusify": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.1.0.tgz", - "integrity": "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==" - }, - "run-parallel": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", - "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", - "requires": { - "queue-microtask": "^1.2.2" - } - }, "semver": { "version": "7.5.4", "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", @@ -3200,7 +2688,6 @@ "version": "0.2.17", "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.17.tgz", "integrity": "sha512-wXR/dYpcqKmfWpEdZjiKJOwCNFndD0DMnrW/cYjVGttEkBfVgcLFHoNrlj47mjOVic9yyNu65alsgF4NQyTa2g==", - "dev": true, "requires": { "fdir": "^6.5.0", "picomatch": "^4.0.4" @@ -3210,25 +2697,15 @@ "version": "6.5.0", "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", - "dev": true, "requires": {} }, "picomatch": { "version": "4.0.4", "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.4.tgz", - "integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==", - "dev": true + "integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==" } } }, - "to-regex-range": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", - "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", - "requires": { - "is-number": "^7.0.0" - } - }, "toposort-class": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/toposort-class/-/toposort-class-1.0.1.tgz", @@ -3253,32 +2730,32 @@ "dev": true }, "umzug": { - "version": "3.8.2", - "resolved": "https://registry.npmjs.org/umzug/-/umzug-3.8.2.tgz", - "integrity": "sha512-BEWEF8OJjTYVC56GjELeHl/1XjFejrD7aHzn+HldRJTx+pL1siBrKHZC8n4K/xL3bEzVA9o++qD1tK2CpZu4KA==", + "version": "3.8.3", + "resolved": "https://registry.npmjs.org/umzug/-/umzug-3.8.3.tgz", + "integrity": "sha512-U9SRJI6LJvV0XwrqGMVPBkE26WHJklHZjtscJ2sEjUp7f+h4NH/25YGjPBernWLroVJvMnTkCAGC0bT0dd63qA==", "requires": { - "@rushstack/ts-command-line": "^4.12.2", + "@rushstack/ts-command-line": "4.19.1", "emittery": "^0.13.0", - "fast-glob": "^3.3.2", "pony-cause": "^2.1.4", + "tinyglobby": "^0.2.16", "type-fest": "^4.0.0" } }, "unbash": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/unbash/-/unbash-3.0.0.tgz", - "integrity": "sha512-FeFPZ/WFT0mbRCuydiZzpPFlrYN8ZUpphQKoq4EeElVIYjYyGzPMxQR/simUwCOJIyVhpFk4RbtyO7RuMpMnHA==", + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/unbash/-/unbash-4.0.4.tgz", + "integrity": "sha512-60m9IVGbavD6jholbxt0jVBXZkEB/HsMZq7Tyaghseve2/Sf0zQRAIfWsD34sde+DKP2tBxJS2wP88ZM0D1FhA==", "dev": true }, "undici-types": { - "version": "7.16.0", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.16.0.tgz", - "integrity": "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw==" + "version": "7.18.2", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.18.2.tgz", + "integrity": "sha512-AsuCzffGHJybSaRrmr5eHr81mwJU3kjw6M+uprWvCXiNeN9SOGwQ3Jn8jb8m3Z6izVgknn1R0FTCEAP2QrLY/w==" }, "universalify": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", - "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==" + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", + "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==" }, "uuid": { "version": "14.0.0", @@ -3348,6 +2825,17 @@ "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-22.0.0.tgz", "integrity": "sha512-rwu/ClNdSMpkSrUb+d6BRsSkLUq1fmfsY6TOpYzTwvwkg1/NRG85KBy3kq++A8LKQwX6lsu+aWad+2khvuXrqw==" }, + "z-schema": { + "version": "5.0.5", + "resolved": "https://registry.npmjs.org/z-schema/-/z-schema-5.0.5.tgz", + "integrity": "sha512-D7eujBWkLa3p2sIpJA0d1pr7es+a7m0vFAnZLlCEKq/Ij2k0MLi9Br2UPxoxdYystm5K1yeBGzub0FlYUEWj2Q==", + "requires": { + "commander": "^9.4.1", + "lodash.get": "^4.4.2", + "lodash.isequal": "^4.5.0", + "validator": "^13.7.0" + } + }, "zod": { "version": "4.4.3", "resolved": "https://registry.npmjs.org/zod/-/zod-4.4.3.tgz", diff --git a/migrator/package.json b/migrator/package.json index b65b74eea..45c5481de 100644 --- a/migrator/package.json +++ b/migrator/package.json @@ -40,7 +40,7 @@ "overrides": { "ajv": "~8.18.0", "uuid": "^14.0.0", - "fast-uri": "^3.1.2" + "fast-uri": "^3.1.4" }, "publishConfig": { "access": "public" diff --git a/nodejs-instrumentation/Dockerfile b/nodejs-instrumentation/Dockerfile index 694c14588..efdac8626 100644 --- a/nodejs-instrumentation/Dockerfile +++ b/nodejs-instrumentation/Dockerfile @@ -9,7 +9,7 @@ # - Grant the necessary access to `/autoinstrumentation` directory. `chmod -R go+r /autoinstrumentation` # - For auto-instrumentation by container injection, the Linux command cp is # used and must be availabe in the image. -FROM node:24.14.1 AS build +FROM node:24.18.0 AS build WORKDIR /operator-build diff --git a/nodejs-instrumentation/package-lock.json b/nodejs-instrumentation/package-lock.json index b2adf9116..2006e4347 100644 --- a/nodejs-instrumentation/package-lock.json +++ b/nodejs-instrumentation/package-lock.json @@ -10,18 +10,18 @@ "license": "ISC", "dependencies": { "@opentelemetry/api": "^1.9.0", - "@opentelemetry/auto-instrumentations-node": "^0.77.0", - "@opentelemetry/exporter-metrics-otlp-grpc": "^0.219.0", - "@opentelemetry/exporter-trace-otlp-grpc": "^0.219.0", + "@opentelemetry/auto-instrumentations-node": "^0.79.0", + "@opentelemetry/exporter-metrics-otlp-grpc": "^0.221.0", + "@opentelemetry/exporter-trace-otlp-grpc": "^0.221.0", "@opentelemetry/propagator-aws-xray": "^2.2.0", "@opentelemetry/resource-detector-aws": "^2.19.0", "@opentelemetry/resource-detector-container": "^0.8.10", "@opentelemetry/resources": "^2.8.0", "@opentelemetry/sdk-metrics": "^2.8.0", - "@opentelemetry/sdk-node": "^0.219.0", + "@opentelemetry/sdk-node": "^0.221.0", "@opentelemetry/sdk-trace-base": "^2.8.0", - "@opentelemetry/semantic-conventions": "^1.41.1", - "@opentelemetry/winston-transport": "^0.29.0" + "@opentelemetry/semantic-conventions": "^1.43.0", + "@opentelemetry/winston-transport": "^0.30.0" }, "devDependencies": { "typescript": "^6.0.3" @@ -104,9 +104,9 @@ } }, "node_modules/@opentelemetry/api-logs": { - "version": "0.219.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/api-logs/-/api-logs-0.219.0.tgz", - "integrity": "sha512-FFx7YnaYJlIjqWW/AG/yAZ0L/NEY724PipXXXQLdtZPbLwBGbUMTGL1i/esI56TWfTUXxhLfpgrnWJCG8aUJyg==", + "version": "0.220.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/api-logs/-/api-logs-0.220.0.tgz", + "integrity": "sha512-CmVa4ImJ+ynfrPMNaAXHET6Bhb44SwzmfyVJFq9ni2jgXJR/l7C6gfVFddNmHP+ZOkP9cf4f9DBe68qVLTHc9w==", "license": "Apache-2.0", "dependencies": { "@opentelemetry/api": "^1.3.0" @@ -116,60 +116,60 @@ } }, "node_modules/@opentelemetry/auto-instrumentations-node": { - "version": "0.77.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/auto-instrumentations-node/-/auto-instrumentations-node-0.77.0.tgz", - "integrity": "sha512-LkF930Cs+v+ZO/qV6LolbocvFkJJ812BBDyRNjQpwllBA+rFvGtP/voXPuh24QV3JKl5/3c3GulLHvMn8spqkQ==", - "license": "Apache-2.0", - "dependencies": { - "@opentelemetry/instrumentation": "^0.219.0", - "@opentelemetry/instrumentation-amqplib": "^0.66.0", - "@opentelemetry/instrumentation-aws-lambda": "^0.71.0", - "@opentelemetry/instrumentation-aws-sdk": "^0.74.0", - "@opentelemetry/instrumentation-bunyan": "^0.64.0", - "@opentelemetry/instrumentation-cassandra-driver": "^0.64.0", - "@opentelemetry/instrumentation-connect": "^0.62.0", - "@opentelemetry/instrumentation-cucumber": "^0.35.0", - "@opentelemetry/instrumentation-dataloader": "^0.36.0", - "@opentelemetry/instrumentation-dns": "^0.62.0", - "@opentelemetry/instrumentation-express": "^0.67.0", - "@opentelemetry/instrumentation-fs": "^0.38.0", - "@opentelemetry/instrumentation-generic-pool": "^0.62.0", - "@opentelemetry/instrumentation-graphql": "^0.67.0", - "@opentelemetry/instrumentation-grpc": "^0.219.0", - "@opentelemetry/instrumentation-hapi": "^0.65.0", - "@opentelemetry/instrumentation-host-metrics": "^0.2.0", - "@opentelemetry/instrumentation-http": "^0.219.0", - "@opentelemetry/instrumentation-ioredis": "^0.67.0", - "@opentelemetry/instrumentation-kafkajs": "^0.28.0", - "@opentelemetry/instrumentation-knex": "^0.63.0", - "@opentelemetry/instrumentation-koa": "^0.67.0", - "@opentelemetry/instrumentation-lru-memoizer": "^0.63.0", - "@opentelemetry/instrumentation-memcached": "^0.62.0", - "@opentelemetry/instrumentation-mongodb": "^0.72.0", - "@opentelemetry/instrumentation-mongoose": "^0.65.0", - "@opentelemetry/instrumentation-mysql": "^0.65.0", - "@opentelemetry/instrumentation-mysql2": "^0.65.0", - "@opentelemetry/instrumentation-nestjs-core": "^0.65.0", - "@opentelemetry/instrumentation-net": "^0.63.0", - "@opentelemetry/instrumentation-openai": "^0.17.0", - "@opentelemetry/instrumentation-oracledb": "^0.44.0", - "@opentelemetry/instrumentation-pg": "^0.71.0", - "@opentelemetry/instrumentation-pino": "^0.65.0", - "@opentelemetry/instrumentation-redis": "^0.67.0", - "@opentelemetry/instrumentation-restify": "^0.64.0", - "@opentelemetry/instrumentation-router": "^0.63.0", - "@opentelemetry/instrumentation-runtime-node": "^0.32.0", - "@opentelemetry/instrumentation-socket.io": "^0.66.0", - "@opentelemetry/instrumentation-tedious": "^0.38.0", - "@opentelemetry/instrumentation-undici": "^0.29.0", - "@opentelemetry/instrumentation-winston": "^0.63.0", - "@opentelemetry/resource-detector-alibaba-cloud": "^0.34.0", - "@opentelemetry/resource-detector-aws": "^2.19.0", - "@opentelemetry/resource-detector-azure": "^0.27.0", - "@opentelemetry/resource-detector-container": "^0.8.10", - "@opentelemetry/resource-detector-gcp": "^0.54.0", + "version": "0.79.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/auto-instrumentations-node/-/auto-instrumentations-node-0.79.0.tgz", + "integrity": "sha512-qL53aIjdw56sRDqz6LXD9h15vPTJgPpqv80rbsnRjzhuC9VqZ58fgk/lx0SdECJ2rcu8keeji5ZgjzJwiQZ0fg==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/instrumentation": "^0.221.0", + "@opentelemetry/instrumentation-amqplib": "^0.68.0", + "@opentelemetry/instrumentation-aws-lambda": "^0.73.0", + "@opentelemetry/instrumentation-aws-sdk": "^0.76.0", + "@opentelemetry/instrumentation-bunyan": "^0.66.0", + "@opentelemetry/instrumentation-cassandra-driver": "^0.66.0", + "@opentelemetry/instrumentation-connect": "^0.64.0", + "@opentelemetry/instrumentation-cucumber": "^0.37.0", + "@opentelemetry/instrumentation-dataloader": "^0.38.0", + "@opentelemetry/instrumentation-dns": "^0.64.0", + "@opentelemetry/instrumentation-express": "^0.69.0", + "@opentelemetry/instrumentation-fs": "^0.40.0", + "@opentelemetry/instrumentation-generic-pool": "^0.64.0", + "@opentelemetry/instrumentation-graphql": "^0.69.0", + "@opentelemetry/instrumentation-grpc": "^0.221.0", + "@opentelemetry/instrumentation-hapi": "^0.67.0", + "@opentelemetry/instrumentation-host-metrics": "^0.4.0", + "@opentelemetry/instrumentation-http": "^0.221.0", + "@opentelemetry/instrumentation-ioredis": "^0.69.0", + "@opentelemetry/instrumentation-kafkajs": "^0.30.0", + "@opentelemetry/instrumentation-knex": "^0.65.0", + "@opentelemetry/instrumentation-koa": "^0.69.0", + "@opentelemetry/instrumentation-lru-memoizer": "^0.65.0", + "@opentelemetry/instrumentation-memcached": "^0.64.0", + "@opentelemetry/instrumentation-mongodb": "^0.74.0", + "@opentelemetry/instrumentation-mongoose": "^0.67.0", + "@opentelemetry/instrumentation-mysql": "^0.67.0", + "@opentelemetry/instrumentation-mysql2": "^0.67.0", + "@opentelemetry/instrumentation-nestjs-core": "^0.67.0", + "@opentelemetry/instrumentation-net": "^0.65.0", + "@opentelemetry/instrumentation-openai": "^0.19.0", + "@opentelemetry/instrumentation-oracledb": "^0.46.0", + "@opentelemetry/instrumentation-pg": "^0.73.0", + "@opentelemetry/instrumentation-pino": "^0.67.0", + "@opentelemetry/instrumentation-redis": "^0.69.0", + "@opentelemetry/instrumentation-restify": "^0.66.0", + "@opentelemetry/instrumentation-router": "^0.65.0", + "@opentelemetry/instrumentation-runtime-node": "^0.34.0", + "@opentelemetry/instrumentation-socket.io": "^0.68.0", + "@opentelemetry/instrumentation-tedious": "^0.40.0", + "@opentelemetry/instrumentation-undici": "^0.31.0", + "@opentelemetry/instrumentation-winston": "^0.65.0", + "@opentelemetry/resource-detector-alibaba-cloud": "^0.36.0", + "@opentelemetry/resource-detector-aws": "^2.21.0", + "@opentelemetry/resource-detector-azure": "^0.29.0", + "@opentelemetry/resource-detector-container": "^0.8.12", + "@opentelemetry/resource-detector-gcp": "^0.56.0", "@opentelemetry/resources": "^2.0.0", - "@opentelemetry/sdk-node": "^0.219.0" + "@opentelemetry/sdk-node": "^0.221.0" }, "engines": { "node": "^18.19.0 || >=20.6.0" @@ -180,13 +180,13 @@ } }, "node_modules/@opentelemetry/configuration": { - "version": "0.219.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/configuration/-/configuration-0.219.0.tgz", - "integrity": "sha512-wXZUYv4ngu43nA4WEhuXNacm46LW+17LRM8nKyIhBzroRA24PBYjMnakwzR/w777nFUB5xlgsYTTeuXxumZM1Q==", + "version": "0.221.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/configuration/-/configuration-0.221.0.tgz", + "integrity": "sha512-uE9y56Zdi9Gt/RdxYnVOo3YmFZkKJJMA0gqtBe8wh8gdtF5Asqe+Oh/TWiDtFb1s+31jNY4CWgnfIB1KOITfFA==", "license": "Apache-2.0", "dependencies": { - "@opentelemetry/core": "2.8.0", - "yaml": "^2.0.0" + "@opentelemetry/core": "2.10.0", + "yaml": "^2.8.3" }, "engines": { "node": "^18.19.0 || >=20.6.0" @@ -196,9 +196,9 @@ } }, "node_modules/@opentelemetry/context-async-hooks": { - "version": "2.8.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/context-async-hooks/-/context-async-hooks-2.8.0.tgz", - "integrity": "sha512-/3FIraneMcng67SUJCxvyInk/oxzwsxyadufk0wwfOBLf5wqtAGX4MoQASwSbndBPeARzBryUM9Azr5kHIdWLw==", + "version": "2.10.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/context-async-hooks/-/context-async-hooks-2.10.0.tgz", + "integrity": "sha512-bvyMcgLEkozzSzpEEEo1OMoeQ97bxj6Qs2uN3mPrSdDvObMI1myffD/BPqcLlzZO9//d1SqQA/WPw7Cz2AiqhA==", "license": "Apache-2.0", "engines": { "node": "^18.19.0 || >=20.6.0" @@ -208,9 +208,9 @@ } }, "node_modules/@opentelemetry/core": { - "version": "2.8.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-2.8.0.tgz", - "integrity": "sha512-hd1Lfh8p545nNz+jq1Ejfz+Mn1hyLuxYn1YzTfFNrxr8urEWMNQLPf1Th8kjOH+HxwawCrtgBp8JpBUR4ZSgww==", + "version": "2.10.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-2.10.0.tgz", + "integrity": "sha512-/wNZ8twnEQQA4HoHu22+vcsdru6pWPWxW+7w+FlxT6Id7PE/WIbZmVKkte+PF72e0F2dnImFeHD2syyE1Mw6MQ==", "license": "Apache-2.0", "dependencies": { "@opentelemetry/semantic-conventions": "^1.29.0" @@ -223,17 +223,15 @@ } }, "node_modules/@opentelemetry/exporter-logs-otlp-grpc": { - "version": "0.219.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/exporter-logs-otlp-grpc/-/exporter-logs-otlp-grpc-0.219.0.tgz", - "integrity": "sha512-7SvzDCIclHWAcCwZ1MTOLcwn4BVNPGI3QxS/DJraPNe1TTL+4TvUBq5zeQV8tsnYvtDN7wKW2qocVmaCP2l7sQ==", + "version": "0.221.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/exporter-logs-otlp-grpc/-/exporter-logs-otlp-grpc-0.221.0.tgz", + "integrity": "sha512-txG1G0IrYSsKKMeiWZfj/i5cQmWB+h+hf3HzPpF3RqZVwp+iQQEIsv8Vtmzy6RWVdHdJZfygmVrBI39YTBvWcw==", "license": "Apache-2.0", "dependencies": { - "@grpc/grpc-js": "^1.14.3", - "@opentelemetry/core": "2.8.0", - "@opentelemetry/otlp-exporter-base": "0.219.0", - "@opentelemetry/otlp-grpc-exporter-base": "0.219.0", - "@opentelemetry/otlp-transformer": "0.219.0", - "@opentelemetry/sdk-logs": "0.219.0" + "@opentelemetry/otlp-exporter-base": "0.221.0", + "@opentelemetry/otlp-grpc-exporter-base": "0.221.0", + "@opentelemetry/otlp-transformer": "0.221.0", + "@opentelemetry/sdk-logs": "0.221.0" }, "engines": { "node": "^18.19.0 || >=20.6.0" @@ -243,16 +241,14 @@ } }, "node_modules/@opentelemetry/exporter-logs-otlp-http": { - "version": "0.219.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/exporter-logs-otlp-http/-/exporter-logs-otlp-http-0.219.0.tgz", - "integrity": "sha512-mhl2HL6GmZI8b8PwPfqMws/5ovJfbRTxwc9Y5agVVHiQ+e5SL1btsFr/kJDgt7YCexDtsUn5HAreHQO9szFS0A==", + "version": "0.221.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/exporter-logs-otlp-http/-/exporter-logs-otlp-http-0.221.0.tgz", + "integrity": "sha512-nKXkr4Tomi6fjYVOf+ytcW3dZAVr4v4Bv5gsT6dr2gvpUPJpKgHB4XbMufMsPotRE3g0XH2GwVVCkN2w6SON+Q==", "license": "Apache-2.0", "dependencies": { - "@opentelemetry/api-logs": "0.219.0", - "@opentelemetry/core": "2.8.0", - "@opentelemetry/otlp-exporter-base": "0.219.0", - "@opentelemetry/otlp-transformer": "0.219.0", - "@opentelemetry/sdk-logs": "0.219.0" + "@opentelemetry/otlp-exporter-base": "0.221.0", + "@opentelemetry/otlp-transformer": "0.221.0", + "@opentelemetry/sdk-logs": "0.221.0" }, "engines": { "node": "^18.19.0 || >=20.6.0" @@ -262,18 +258,14 @@ } }, "node_modules/@opentelemetry/exporter-logs-otlp-proto": { - "version": "0.219.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/exporter-logs-otlp-proto/-/exporter-logs-otlp-proto-0.219.0.tgz", - "integrity": "sha512-Ayw4Gf71PS9jhBVaYywa4WsajnqfDehMkTdVH3TSAVHqPcsAv/AhH/wTNRYNt99szeYr6Gbd/D6RjZD77wAxHg==", + "version": "0.221.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/exporter-logs-otlp-proto/-/exporter-logs-otlp-proto-0.221.0.tgz", + "integrity": "sha512-AH6EY+47gXFaWYgG3hfeOneGiE9xIZGtDBk+9g0sM8NZWzsQhhmqPbQQXJzS7pyCh5jRRr2nYNXVrkCmoojRvQ==", "license": "Apache-2.0", "dependencies": { - "@opentelemetry/api-logs": "0.219.0", - "@opentelemetry/core": "2.8.0", - "@opentelemetry/otlp-exporter-base": "0.219.0", - "@opentelemetry/otlp-transformer": "0.219.0", - "@opentelemetry/resources": "2.8.0", - "@opentelemetry/sdk-logs": "0.219.0", - "@opentelemetry/sdk-trace-base": "2.8.0" + "@opentelemetry/otlp-exporter-base": "0.221.0", + "@opentelemetry/otlp-transformer": "0.221.0", + "@opentelemetry/sdk-logs": "0.221.0" }, "engines": { "node": "^18.19.0 || >=20.6.0" @@ -283,19 +275,14 @@ } }, "node_modules/@opentelemetry/exporter-metrics-otlp-grpc": { - "version": "0.219.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/exporter-metrics-otlp-grpc/-/exporter-metrics-otlp-grpc-0.219.0.tgz", - "integrity": "sha512-6LaaSrPxK5L55bXevWajvOMxGOpNm0n12tG53TeZaUeNzXwLPg6d2KCC1zAlGsojan+xRG71mA4Qqs9K2VVrKQ==", + "version": "0.221.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/exporter-metrics-otlp-grpc/-/exporter-metrics-otlp-grpc-0.221.0.tgz", + "integrity": "sha512-KOgCtO15FC6C1T/xOqBcr7EyUs7B+7yomGNb5Y97d3s38rPbCCk5sewkmE2b0/itOkQ/PptX8CLlD+kn2mEtTg==", "license": "Apache-2.0", "dependencies": { - "@grpc/grpc-js": "^1.14.3", - "@opentelemetry/core": "2.8.0", - "@opentelemetry/exporter-metrics-otlp-http": "0.219.0", - "@opentelemetry/otlp-exporter-base": "0.219.0", - "@opentelemetry/otlp-grpc-exporter-base": "0.219.0", - "@opentelemetry/otlp-transformer": "0.219.0", - "@opentelemetry/resources": "2.8.0", - "@opentelemetry/sdk-metrics": "2.8.0" + "@opentelemetry/exporter-metrics-otlp-http": "0.221.0", + "@opentelemetry/otlp-grpc-exporter-base": "0.221.0", + "@opentelemetry/otlp-transformer": "0.221.0" }, "engines": { "node": "^18.19.0 || >=20.6.0" @@ -305,16 +292,16 @@ } }, "node_modules/@opentelemetry/exporter-metrics-otlp-http": { - "version": "0.219.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/exporter-metrics-otlp-http/-/exporter-metrics-otlp-http-0.219.0.tgz", - "integrity": "sha512-6CaDRbMVHZSDWzNXwrR8y/H4B/Z1eMNnkHiPQlTx3Ojz2OHY4X/aff/UC4P/3pHUQSuTfi3oh2UsPPZppw+Vrg==", + "version": "0.221.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/exporter-metrics-otlp-http/-/exporter-metrics-otlp-http-0.221.0.tgz", + "integrity": "sha512-sRfCKbOzgy8xZQV2as0RzIZlnCmCseCKZGLfRcrpo2CBngJDr+rPtX0zkG0+oUCV5kfQPUoW3W3C96Ag3Y/Clg==", "license": "Apache-2.0", "dependencies": { - "@opentelemetry/core": "2.8.0", - "@opentelemetry/otlp-exporter-base": "0.219.0", - "@opentelemetry/otlp-transformer": "0.219.0", - "@opentelemetry/resources": "2.8.0", - "@opentelemetry/sdk-metrics": "2.8.0" + "@opentelemetry/core": "2.10.0", + "@opentelemetry/otlp-exporter-base": "0.221.0", + "@opentelemetry/otlp-transformer": "0.221.0", + "@opentelemetry/resources": "2.10.0", + "@opentelemetry/sdk-metrics": "2.10.0" }, "engines": { "node": "^18.19.0 || >=20.6.0" @@ -324,17 +311,14 @@ } }, "node_modules/@opentelemetry/exporter-metrics-otlp-proto": { - "version": "0.219.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/exporter-metrics-otlp-proto/-/exporter-metrics-otlp-proto-0.219.0.tgz", - "integrity": "sha512-DUS7XyIiEnoeccQUvuKy0G2/YqeKhpN8FVIrGbrLNIVMj10yeIFLRzRv0tibCI2kXXvlTTABVexGAk78wHk2ug==", + "version": "0.221.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/exporter-metrics-otlp-proto/-/exporter-metrics-otlp-proto-0.221.0.tgz", + "integrity": "sha512-YMF4LveY2I3yhw61rn6nmC9FE8U24IZHPeKU1Duc5+sbwjMd8FwZAwba318ImdThCg/HuVQvhm2y6bfgNPnfYg==", "license": "Apache-2.0", "dependencies": { - "@opentelemetry/core": "2.8.0", - "@opentelemetry/exporter-metrics-otlp-http": "0.219.0", - "@opentelemetry/otlp-exporter-base": "0.219.0", - "@opentelemetry/otlp-transformer": "0.219.0", - "@opentelemetry/resources": "2.8.0", - "@opentelemetry/sdk-metrics": "2.8.0" + "@opentelemetry/exporter-metrics-otlp-http": "0.221.0", + "@opentelemetry/otlp-exporter-base": "0.221.0", + "@opentelemetry/otlp-transformer": "0.221.0" }, "engines": { "node": "^18.19.0 || >=20.6.0" @@ -344,14 +328,14 @@ } }, "node_modules/@opentelemetry/exporter-prometheus": { - "version": "0.219.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/exporter-prometheus/-/exporter-prometheus-0.219.0.tgz", - "integrity": "sha512-TxOnJ85eWJY5JyOJsNMXiRTYlkDcOv0u3KbXEzWCc+tUS9sjL/BC6BcdxZ0B9r2OFVqsrZFXUzSD2sZUy42Ucw==", + "version": "0.221.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/exporter-prometheus/-/exporter-prometheus-0.221.0.tgz", + "integrity": "sha512-kW79a20qWESIuAdDrxzg9WKM98twV/NBWBFRAH57ap/+ssZhiCo0hckzKT0zpuwR/gSHrFAQhJL0bYDrnEM34g==", "license": "Apache-2.0", "dependencies": { - "@opentelemetry/core": "2.8.0", - "@opentelemetry/resources": "2.8.0", - "@opentelemetry/sdk-metrics": "2.8.0", + "@opentelemetry/core": "2.10.0", + "@opentelemetry/resources": "2.10.0", + "@opentelemetry/sdk-metrics": "2.10.0", "@opentelemetry/semantic-conventions": "^1.29.0" }, "engines": { @@ -362,18 +346,15 @@ } }, "node_modules/@opentelemetry/exporter-trace-otlp-grpc": { - "version": "0.219.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/exporter-trace-otlp-grpc/-/exporter-trace-otlp-grpc-0.219.0.tgz", - "integrity": "sha512-BkDNv1UD6BscW19MxbAxVmSYSSFuyeqR6buV2/HTYqA7GrR0EbTFzqG6h86T3PtXmpdbsWjMGLDdjG2rikG27Q==", + "version": "0.221.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/exporter-trace-otlp-grpc/-/exporter-trace-otlp-grpc-0.221.0.tgz", + "integrity": "sha512-zXminlZedtq9LvOW64CnNkOqk15zV75k8JgtdTuWFge6+jk2m4GmAUm6L2eIiG1o2a2bZxXw2PDrszm+bps0IA==", "license": "Apache-2.0", "dependencies": { - "@grpc/grpc-js": "^1.14.3", - "@opentelemetry/core": "2.8.0", - "@opentelemetry/otlp-exporter-base": "0.219.0", - "@opentelemetry/otlp-grpc-exporter-base": "0.219.0", - "@opentelemetry/otlp-transformer": "0.219.0", - "@opentelemetry/resources": "2.8.0", - "@opentelemetry/sdk-trace-base": "2.8.0" + "@opentelemetry/otlp-exporter-base": "0.221.0", + "@opentelemetry/otlp-grpc-exporter-base": "0.221.0", + "@opentelemetry/otlp-transformer": "0.221.0", + "@opentelemetry/sdk-trace": "2.10.0" }, "engines": { "node": "^18.19.0 || >=20.6.0" @@ -383,16 +364,14 @@ } }, "node_modules/@opentelemetry/exporter-trace-otlp-http": { - "version": "0.219.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/exporter-trace-otlp-http/-/exporter-trace-otlp-http-0.219.0.tgz", - "integrity": "sha512-9t6SvBXXBEjOBcIzgozvBbd3jWrv3Gt3ngGhl1fhdZ/zRc7oZDVOFEqbi2zlBpW9BXhgDMKv422J0DL/3iQWfw==", + "version": "0.221.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/exporter-trace-otlp-http/-/exporter-trace-otlp-http-0.221.0.tgz", + "integrity": "sha512-AySXiKoC+meiWm6zdVj5T2LnPDZuatveBby1cMOeQteIWsYXAUxs8Sru13G2pVSPrUXz6vF+og7QVBX6GdC/oQ==", "license": "Apache-2.0", "dependencies": { - "@opentelemetry/core": "2.8.0", - "@opentelemetry/otlp-exporter-base": "0.219.0", - "@opentelemetry/otlp-transformer": "0.219.0", - "@opentelemetry/resources": "2.8.0", - "@opentelemetry/sdk-trace-base": "2.8.0" + "@opentelemetry/otlp-exporter-base": "0.221.0", + "@opentelemetry/otlp-transformer": "0.221.0", + "@opentelemetry/sdk-trace": "2.10.0" }, "engines": { "node": "^18.19.0 || >=20.6.0" @@ -402,16 +381,14 @@ } }, "node_modules/@opentelemetry/exporter-trace-otlp-proto": { - "version": "0.219.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/exporter-trace-otlp-proto/-/exporter-trace-otlp-proto-0.219.0.tgz", - "integrity": "sha512-lF/LUBfhOFmxJa+SQsLN7ziV4MHa2pyKgOM6JNehSOfU+npjM4gwm9oIKEJrzrWcexMcqydiyoFy0XCb1Ql3wQ==", + "version": "0.221.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/exporter-trace-otlp-proto/-/exporter-trace-otlp-proto-0.221.0.tgz", + "integrity": "sha512-Z9i2T7vgZbWe9rSLYxXVIbeW+XyzUq4rZanW3ZyVNwVDqCsh0EJKUgBWWQ0CZfeuUA+RQPzKgJQHMuWAUnKqXw==", "license": "Apache-2.0", "dependencies": { - "@opentelemetry/core": "2.8.0", - "@opentelemetry/otlp-exporter-base": "0.219.0", - "@opentelemetry/otlp-transformer": "0.219.0", - "@opentelemetry/resources": "2.8.0", - "@opentelemetry/sdk-trace-base": "2.8.0" + "@opentelemetry/otlp-exporter-base": "0.221.0", + "@opentelemetry/otlp-transformer": "0.221.0", + "@opentelemetry/sdk-trace": "2.10.0" }, "engines": { "node": "^18.19.0 || >=20.6.0" @@ -421,14 +398,14 @@ } }, "node_modules/@opentelemetry/exporter-zipkin": { - "version": "2.8.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/exporter-zipkin/-/exporter-zipkin-2.8.0.tgz", - "integrity": "sha512-Mj84UkEa17BK2o903VTXW3wM8CrSZexGs4tRGVZVIMM9ni1T6TuGx5IrRfoWKAbshx42D5/kc7YV+axypLPYyA==", + "version": "2.10.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/exporter-zipkin/-/exporter-zipkin-2.10.0.tgz", + "integrity": "sha512-7gsvgf0UDoJ4l9ObrwBmz5G/ZogiPk+lq+g5GpLp24YQF/vPM/BSsnOfcLnfinast5ASUgLo78uSC/ObjlnXgg==", "license": "Apache-2.0", "dependencies": { - "@opentelemetry/core": "2.8.0", - "@opentelemetry/resources": "2.8.0", - "@opentelemetry/sdk-trace-base": "2.8.0", + "@opentelemetry/core": "2.10.0", + "@opentelemetry/resources": "2.10.0", + "@opentelemetry/sdk-trace": "2.10.0", "@opentelemetry/semantic-conventions": "^1.29.0" }, "engines": { @@ -439,12 +416,12 @@ } }, "node_modules/@opentelemetry/instrumentation": { - "version": "0.219.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation/-/instrumentation-0.219.0.tgz", - "integrity": "sha512-X5t7I8GyIO9rmGHwoedZLREpQqrF1WW2nxzNNym6HOKpFiE+rvqV3ngC0xcZVO2YwIGf3KKmRdWrYwdwz3H9RQ==", + "version": "0.221.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation/-/instrumentation-0.221.0.tgz", + "integrity": "sha512-cCk80Z/iRDf/5gfsKMB4f74LqVA5yKETB/9ojPzVW/6/f70iu89nJvGxsFCxx4XfSohaOofkU19kiYm84AiAlw==", "license": "Apache-2.0", "dependencies": { - "@opentelemetry/api-logs": "0.219.0", + "@opentelemetry/api-logs": "0.221.0", "import-in-the-middle": "^3.0.0", "require-in-the-middle": "^8.0.0" }, @@ -456,13 +433,13 @@ } }, "node_modules/@opentelemetry/instrumentation-amqplib": { - "version": "0.66.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-amqplib/-/instrumentation-amqplib-0.66.0.tgz", - "integrity": "sha512-lyJgobzP0Ce+tRGOkdnrb60apfqU89xB9FeMTmo1TJU007KTMLiLFd4iCfTiBEXzBsVplx7kwrVN4FqGBUcD2Q==", + "version": "0.68.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-amqplib/-/instrumentation-amqplib-0.68.0.tgz", + "integrity": "sha512-U9Fc3C061q+AGxP3xEJTIAJtBduY1GL21J4SjOSxCmmls4UUva16jzQ5ZkunQe0pKalrRjZ/DlZ+wgfgQxqjBw==", "license": "Apache-2.0", "dependencies": { "@opentelemetry/core": "^2.0.0", - "@opentelemetry/instrumentation": "^0.219.0", + "@opentelemetry/instrumentation": "^0.221.0", "@opentelemetry/semantic-conventions": "^1.33.0" }, "engines": { @@ -473,12 +450,12 @@ } }, "node_modules/@opentelemetry/instrumentation-aws-lambda": { - "version": "0.71.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-aws-lambda/-/instrumentation-aws-lambda-0.71.0.tgz", - "integrity": "sha512-9Sv6flQDeNNF6ZbiLgn+NYJa220yRZdDSIdgDZsqNubpDnYAPF33OHcQDlE8mFiaOq14mngoPS48JnYc8JT+Ug==", + "version": "0.73.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-aws-lambda/-/instrumentation-aws-lambda-0.73.0.tgz", + "integrity": "sha512-N2BZFlWmVt2zjpiqPnfmIlj7tV/wfKSZCFF/laLAnJSTZjSEdc9JYSXW+KUV0FMUNDfpLCeAcI1xDMdGLdxFJg==", "license": "Apache-2.0", "dependencies": { - "@opentelemetry/instrumentation": "^0.219.0", + "@opentelemetry/instrumentation": "^0.221.0", "@opentelemetry/propagator-aws-xray": "^2.1.4", "@opentelemetry/semantic-conventions": "^1.27.0", "@types/aws-lambda": "^8.10.155" @@ -491,13 +468,13 @@ } }, "node_modules/@opentelemetry/instrumentation-aws-sdk": { - "version": "0.74.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-aws-sdk/-/instrumentation-aws-sdk-0.74.0.tgz", - "integrity": "sha512-EMLUGgx2wJSXdwMEFdwd3IaW+mkUF8PENdzDFQ1FRdztzMG1d1XN76ORIjAMsXcKQISlRRcz93AWPQeBPn4EKA==", + "version": "0.76.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-aws-sdk/-/instrumentation-aws-sdk-0.76.0.tgz", + "integrity": "sha512-gg2QaDtWeFezRt2mAl9vBQ38y60tzUShKg1KA9uTgsMJimUHaBnB3X8kEH9Of8jKWT4DDoDcSA37gPcoEc0hiA==", "license": "Apache-2.0", "dependencies": { "@opentelemetry/core": "^2.0.0", - "@opentelemetry/instrumentation": "^0.219.0", + "@opentelemetry/instrumentation": "^0.221.0", "@opentelemetry/semantic-conventions": "^1.34.0" }, "engines": { @@ -508,13 +485,14 @@ } }, "node_modules/@opentelemetry/instrumentation-bunyan": { - "version": "0.64.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-bunyan/-/instrumentation-bunyan-0.64.0.tgz", - "integrity": "sha512-jrRNFvpREutmpoWhk1T8n9q/RYdxbViXwSUPHN8yQR1bzgtwfOl/y8G/p8Xfudlky9GGsqw5WRc6q6QrfgF3pw==", + "version": "0.66.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-bunyan/-/instrumentation-bunyan-0.66.0.tgz", + "integrity": "sha512-IqYQC1dav35NHlD5nYnpBXK8tI6KJ9/MIt8LYKFxwlhMIwfBCfavaklyP8NtVKoJ0WZKzX2v9Sh+xGK1XvSniQ==", "license": "Apache-2.0", "dependencies": { - "@opentelemetry/api-logs": "^0.219.0", - "@opentelemetry/instrumentation": "^0.219.0", + "@opentelemetry/api-logs": "^0.221.0", + "@opentelemetry/instrumentation": "^0.221.0", + "@opentelemetry/semantic-conventions": "^1.41.1", "@types/bunyan": "1.8.11" }, "engines": { @@ -524,13 +502,25 @@ "@opentelemetry/api": "^1.3.0" } }, + "node_modules/@opentelemetry/instrumentation-bunyan/node_modules/@opentelemetry/api-logs": { + "version": "0.221.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/api-logs/-/api-logs-0.221.0.tgz", + "integrity": "sha512-OlanaW1vv7ufTqQ3/fPLI4arGt5ZoM+P8abOMki6uEYnpRazepSWDwDnnw+la7kE26SHVC18//SMccrDvLKOXQ==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/api": "^1.3.0" + }, + "engines": { + "node": ">=8.0.0" + } + }, "node_modules/@opentelemetry/instrumentation-cassandra-driver": { - "version": "0.64.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-cassandra-driver/-/instrumentation-cassandra-driver-0.64.0.tgz", - "integrity": "sha512-KN+iOsmPI0nkX2lfgNgHBrHNaDuxDwIbwFrlvyrZ4bAT8bTKcCOXhne70O9qihM8+T1F4tjvPXpCfhKZbmsYiw==", + "version": "0.66.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-cassandra-driver/-/instrumentation-cassandra-driver-0.66.0.tgz", + "integrity": "sha512-4ksN7PfXLg7raDyXIjIrtxxzuuDlUx6Fh0s8VXojdl+nn2o0xkYa9jrq1f9Bfqhm+Sce3GOa1RjE3ycvAwk6Xw==", "license": "Apache-2.0", "dependencies": { - "@opentelemetry/instrumentation": "^0.219.0", + "@opentelemetry/instrumentation": "^0.221.0", "@opentelemetry/semantic-conventions": "^1.37.0" }, "engines": { @@ -541,13 +531,13 @@ } }, "node_modules/@opentelemetry/instrumentation-connect": { - "version": "0.62.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-connect/-/instrumentation-connect-0.62.0.tgz", - "integrity": "sha512-ZGV2sOyeffqMiqoh4RpsPTs/TUI5cCS+cEWvC9wUfvaEekR5omR6P/ClG+QDwasGBlKx2zfFPjSYPpzUo81XAw==", + "version": "0.64.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-connect/-/instrumentation-connect-0.64.0.tgz", + "integrity": "sha512-D1Tpom3BpY8g29FFOEQ2FZioVFjyXwXHsh3BOn2BHcg7Taipg+yc+DPGUwvdR4WZrKnNMAG/+FBXNa0S0KhJYA==", "license": "Apache-2.0", "dependencies": { "@opentelemetry/core": "^2.0.0", - "@opentelemetry/instrumentation": "^0.219.0", + "@opentelemetry/instrumentation": "^0.221.0", "@opentelemetry/semantic-conventions": "^1.27.0", "@types/connect": "3.4.38" }, @@ -559,12 +549,12 @@ } }, "node_modules/@opentelemetry/instrumentation-cucumber": { - "version": "0.35.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-cucumber/-/instrumentation-cucumber-0.35.0.tgz", - "integrity": "sha512-H9NsbcFiVOFOcOu40+VOOjxdTeonu0VHI3mte5ie5ka/bazzIPGncQoHpL6su53C3/sgMdKjE6ZuwsB7Y8gQpA==", + "version": "0.37.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-cucumber/-/instrumentation-cucumber-0.37.0.tgz", + "integrity": "sha512-ezhn6D0DSUZkwLBGdfnr+PAENvB92AhbEH/dkcSLYB8dbQiw/NQ8y19jOn3E6MZTt0+FD1YyHtrJS2skDO4nDQ==", "license": "Apache-2.0", "dependencies": { - "@opentelemetry/instrumentation": "^0.219.0", + "@opentelemetry/instrumentation": "^0.221.0", "@opentelemetry/semantic-conventions": "^1.27.0" }, "engines": { @@ -575,12 +565,12 @@ } }, "node_modules/@opentelemetry/instrumentation-dataloader": { - "version": "0.36.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-dataloader/-/instrumentation-dataloader-0.36.0.tgz", - "integrity": "sha512-gE0mTk+EnVaBN0mPRM1V6FqzQ9VckTp6ZFIssU5hxy+e3sqspYILBhV/0IHZ33qxGa3B9buLVZnuzVjUISfyoQ==", + "version": "0.38.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-dataloader/-/instrumentation-dataloader-0.38.0.tgz", + "integrity": "sha512-OmOVadK0m7sdlvMwbt1gb2iVUCyvVNDo3x5JLGgnKggLTJBgTcQxgMl3pAhFAMzWGo9URuMxuh3Bphy9Pb9nZw==", "license": "Apache-2.0", "dependencies": { - "@opentelemetry/instrumentation": "^0.219.0" + "@opentelemetry/instrumentation": "^0.221.0" }, "engines": { "node": "^18.19.0 || >=20.6.0" @@ -590,12 +580,12 @@ } }, "node_modules/@opentelemetry/instrumentation-dns": { - "version": "0.62.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-dns/-/instrumentation-dns-0.62.0.tgz", - "integrity": "sha512-6v0X8wEqhIyv2b7MXhmipyCitJfm0vnF5mLBTWXojovoHp7P1EpN5sb12LgdhVlozhGwRZdzb6OvKUVsxKMD8g==", + "version": "0.64.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-dns/-/instrumentation-dns-0.64.0.tgz", + "integrity": "sha512-La6s9SdKgojZQVFD7AclQBYe3WioVe6zicJswM3QPPPHpCufJYnw8rO/G9o2Yl/OUeS7PYpzwHh4N6lexzbEcA==", "license": "Apache-2.0", "dependencies": { - "@opentelemetry/instrumentation": "^0.219.0" + "@opentelemetry/instrumentation": "^0.221.0" }, "engines": { "node": "^18.19.0 || >=20.6.0" @@ -605,13 +595,13 @@ } }, "node_modules/@opentelemetry/instrumentation-express": { - "version": "0.67.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-express/-/instrumentation-express-0.67.0.tgz", - "integrity": "sha512-1WTWX2YNZIV+jPmEqdf/Vd1gHMT92TKA/0pf/iIItWhV6+RhzhnUUW4kSWQn8L3qVcgWEzQ860/ZOwaIwayi8A==", + "version": "0.69.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-express/-/instrumentation-express-0.69.0.tgz", + "integrity": "sha512-91pHMujQgDyhEQrdg8RriMBrRZ/qPaJ0Y2dopQ6lHjW5YjoeytWi8ruM//T6f5o0D95hnqRlv79Pel1lGPqaYg==", "license": "Apache-2.0", "dependencies": { - "@opentelemetry/core": "^2.0.0", - "@opentelemetry/instrumentation": "^0.219.0", + "@opentelemetry/core": "^2.9.0", + "@opentelemetry/instrumentation": "^0.221.0", "@opentelemetry/semantic-conventions": "^1.27.0" }, "engines": { @@ -622,13 +612,13 @@ } }, "node_modules/@opentelemetry/instrumentation-fs": { - "version": "0.38.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-fs/-/instrumentation-fs-0.38.0.tgz", - "integrity": "sha512-6OBofWODg0RcPkl3bA+7yPf0e4Vi3O7ZxlFGY5QHPMMLxWVMnjWPEXQ2NlLBk19Sr8LcvEyyZOStdLJrT5o2dQ==", + "version": "0.40.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-fs/-/instrumentation-fs-0.40.0.tgz", + "integrity": "sha512-p39axaaYVKhnl5l4M+1aiXmxrAG2HuTti7DHxs2jDJRst828y5iwqUZLC1UWIKIhW9FfdV5gogXg+nRRhSc0EA==", "license": "Apache-2.0", "dependencies": { - "@opentelemetry/core": "^2.0.0", - "@opentelemetry/instrumentation": "^0.219.0" + "@opentelemetry/core": "^2.9.0", + "@opentelemetry/instrumentation": "^0.221.0" }, "engines": { "node": "^18.19.0 || >=20.6.0" @@ -638,12 +628,12 @@ } }, "node_modules/@opentelemetry/instrumentation-generic-pool": { - "version": "0.62.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-generic-pool/-/instrumentation-generic-pool-0.62.0.tgz", - "integrity": "sha512-IhO2y/MaK1oZ4EbUgdkSVT+XViPq86IAz4lwPe9jaba00M2yDWs8+f9xjcH3tK5IC3dZuAxXmifGmfvuJp92jw==", + "version": "0.64.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-generic-pool/-/instrumentation-generic-pool-0.64.0.tgz", + "integrity": "sha512-wM939j8Ox5BBHoA0r/p9etdpyS3GcUf/sfrUx1dtZdqGKU4ZcBxLyPD8QntwFkSI9PHql+rRejTCA6Btktz8Kg==", "license": "Apache-2.0", "dependencies": { - "@opentelemetry/instrumentation": "^0.219.0" + "@opentelemetry/instrumentation": "^0.221.0" }, "engines": { "node": "^18.19.0 || >=20.6.0" @@ -653,12 +643,12 @@ } }, "node_modules/@opentelemetry/instrumentation-graphql": { - "version": "0.67.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-graphql/-/instrumentation-graphql-0.67.0.tgz", - "integrity": "sha512-NMUmuhtYvv3AwkK4zsHQbTCXS81QS63hbNZRKfXc7W8f4KbWeKLmqKqvnfjUcqZoGS0eAw2kNKNYcbtbQ7baAg==", + "version": "0.69.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-graphql/-/instrumentation-graphql-0.69.0.tgz", + "integrity": "sha512-vyKzuiBoEulV1FjMSe4iiuwZedt+nNAuaSVOh/3WxjDIuGQ9WsH+0ohd9snhIY6guAjYOGjqvTiYLs6K+OTQQg==", "license": "Apache-2.0", "dependencies": { - "@opentelemetry/instrumentation": "^0.219.0" + "@opentelemetry/instrumentation": "^0.221.0" }, "engines": { "node": "^18.19.0 || >=20.6.0" @@ -668,12 +658,12 @@ } }, "node_modules/@opentelemetry/instrumentation-grpc": { - "version": "0.219.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-grpc/-/instrumentation-grpc-0.219.0.tgz", - "integrity": "sha512-GyW1Kfbf7uiJXeBZovB/uXPUdkaZYWzB2ZPCdY2CU7+6V207u8wlCM+zVd3UwhEjOBrMbZAGq+m38aBEI/EVtA==", + "version": "0.221.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-grpc/-/instrumentation-grpc-0.221.0.tgz", + "integrity": "sha512-1U45172SiPWG1MPfrgLItIuXZO/RfJqt5sxsrdlKN1NRV0pUtv7lbpgB1nShwP/SGSKaAFkovbVg3a1hfy3mpQ==", "license": "Apache-2.0", "dependencies": { - "@opentelemetry/instrumentation": "0.219.0", + "@opentelemetry/instrumentation": "0.221.0", "@opentelemetry/semantic-conventions": "^1.29.0" }, "engines": { @@ -684,13 +674,13 @@ } }, "node_modules/@opentelemetry/instrumentation-hapi": { - "version": "0.65.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-hapi/-/instrumentation-hapi-0.65.0.tgz", - "integrity": "sha512-Whhas9iU0SfK/7XBcgCwfW5c9AaxjxtZpzW21t3Ml8XZ6Irc3hF36JDolMmFa7nUjML9n9bSUAZjwCgrK+2UdQ==", + "version": "0.67.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-hapi/-/instrumentation-hapi-0.67.0.tgz", + "integrity": "sha512-cIXIN4vZXm6aI4yz+4oUIRnkiAxCIpONrMhnGTI+ILKKEsIXP8Uselfr9663+TnisrgRLB7kKp+SoOuGJRHGtw==", "license": "Apache-2.0", "dependencies": { - "@opentelemetry/core": "^2.0.0", - "@opentelemetry/instrumentation": "^0.219.0", + "@opentelemetry/core": "^2.9.0", + "@opentelemetry/instrumentation": "^0.221.0", "@opentelemetry/semantic-conventions": "^1.27.0" }, "engines": { @@ -701,12 +691,12 @@ } }, "node_modules/@opentelemetry/instrumentation-host-metrics": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-host-metrics/-/instrumentation-host-metrics-0.2.0.tgz", - "integrity": "sha512-NIttCEOLdg1ebbDiJpCf0Ly1OGIa10isesik+K2dnXy2P99q4muUFjpaLtTnhkENrt9SmR0Zrxzq7B+W/VNWyw==", + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-host-metrics/-/instrumentation-host-metrics-0.4.0.tgz", + "integrity": "sha512-jnFyX2sTn2B+9mjsL3qgAHzpxdaia4/FV2GSlf9QrpaiMi6O0M0lA9JZTsf4FTvuOwrIngCk+MeVXjsxgBXXyg==", "license": "Apache-2.0", "dependencies": { - "@opentelemetry/instrumentation": "^0.219.0", + "@opentelemetry/instrumentation": "^0.221.0", "systeminformation": "^5.31.6" }, "engines": { @@ -717,13 +707,13 @@ } }, "node_modules/@opentelemetry/instrumentation-http": { - "version": "0.219.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-http/-/instrumentation-http-0.219.0.tgz", - "integrity": "sha512-nNt1fqpyah/OKjNHdEOu8xLwISppRU2qJuF8aR+fCcftVwdFkPgtworBLA+TI1HU2iF508jcQBF2gerWczJAXg==", + "version": "0.221.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-http/-/instrumentation-http-0.221.0.tgz", + "integrity": "sha512-oIP91CPIANuYr09tGFElPFKAh6JUar+awJf1kBRYlaeo9b0gDwZHEB2zBfFlvdNFHm0wAVutMZODVi5smKT30g==", "license": "Apache-2.0", "dependencies": { - "@opentelemetry/core": "2.8.0", - "@opentelemetry/instrumentation": "0.219.0", + "@opentelemetry/core": "2.10.0", + "@opentelemetry/instrumentation": "0.221.0", "@opentelemetry/semantic-conventions": "^1.29.0", "forwarded-parse": "2.1.2" }, @@ -735,12 +725,12 @@ } }, "node_modules/@opentelemetry/instrumentation-ioredis": { - "version": "0.67.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-ioredis/-/instrumentation-ioredis-0.67.0.tgz", - "integrity": "sha512-dv64vQ4aXbJvRMMAFrMUSzDeJrNv/uQMLjfaav4LHAOar7Xn08W3pkoYoYEnzq/n2+fGgG96rp9S0gQ+VnLDiw==", + "version": "0.69.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-ioredis/-/instrumentation-ioredis-0.69.0.tgz", + "integrity": "sha512-I9sZtxXWZ1tRXtRNTEVxpokGtXy6RL1SZhtPVh7zxH78t8ar71V5Dx4bnQiUjKTDzItpC73krD8c0/cEWA9oLg==", "license": "Apache-2.0", "dependencies": { - "@opentelemetry/instrumentation": "^0.219.0", + "@opentelemetry/instrumentation": "^0.221.0", "@opentelemetry/redis-common": "^0.38.3", "@opentelemetry/semantic-conventions": "^1.33.0" }, @@ -752,12 +742,12 @@ } }, "node_modules/@opentelemetry/instrumentation-kafkajs": { - "version": "0.28.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-kafkajs/-/instrumentation-kafkajs-0.28.0.tgz", - "integrity": "sha512-dztkg70nJds3Uc0Xo3NFlRqL5iYgGYWh8myuuGfRC6NnXJchY0Kw9QnBjTZxBSldXU+P6nv2snDVMmlxuy6fEw==", + "version": "0.30.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-kafkajs/-/instrumentation-kafkajs-0.30.0.tgz", + "integrity": "sha512-/p/D4etxJpJGB0VrS+kqF8WfVAMFWf5ybhY0mjzIEd/d/T68+nxTWJaI5MJXFiyOnBUFuMlun12VJt+QJDCSZA==", "license": "Apache-2.0", "dependencies": { - "@opentelemetry/instrumentation": "^0.219.0", + "@opentelemetry/instrumentation": "^0.221.0", "@opentelemetry/semantic-conventions": "^1.30.0" }, "engines": { @@ -768,12 +758,12 @@ } }, "node_modules/@opentelemetry/instrumentation-knex": { - "version": "0.63.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-knex/-/instrumentation-knex-0.63.0.tgz", - "integrity": "sha512-XrpRahI/9vTrfSUfkhy8jGX8KMRKecQIPU9GyEZ8gkR030iJwQYsMmKGO5TK9R80cQGUopXwDvV55zmVNEkcPg==", + "version": "0.65.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-knex/-/instrumentation-knex-0.65.0.tgz", + "integrity": "sha512-rJTT12VlDnL6wOWfxnBvkTUIzW2ju+7nqlToMy1tlqL0j6ohlVPxryMCS5h6UqE3CFpq7HtHqVObQHZgrA37KA==", "license": "Apache-2.0", "dependencies": { - "@opentelemetry/instrumentation": "^0.219.0", + "@opentelemetry/instrumentation": "^0.221.0", "@opentelemetry/semantic-conventions": "^1.33.1" }, "engines": { @@ -784,13 +774,13 @@ } }, "node_modules/@opentelemetry/instrumentation-koa": { - "version": "0.67.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-koa/-/instrumentation-koa-0.67.0.tgz", - "integrity": "sha512-QOGY4mjqvF85LDcrzwrQXMcsu1wMTALeL1OHyTkLpN/7cnoDtv0W/qMBjHVq4IKYK6yDH4ZDNdwlonJPhwCGcw==", + "version": "0.69.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-koa/-/instrumentation-koa-0.69.0.tgz", + "integrity": "sha512-fxuA8jFOdqQzJV9Sitd0dk+zns7RQCFe19ia3LHex5oLiQPaaQovBv37jndX/zAZw6EBORATePHE8OQUwraPCQ==", "license": "Apache-2.0", "dependencies": { - "@opentelemetry/core": "^2.0.0", - "@opentelemetry/instrumentation": "^0.219.0", + "@opentelemetry/core": "^2.9.0", + "@opentelemetry/instrumentation": "^0.221.0", "@opentelemetry/semantic-conventions": "^1.36.0" }, "engines": { @@ -801,12 +791,12 @@ } }, "node_modules/@opentelemetry/instrumentation-lru-memoizer": { - "version": "0.63.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-lru-memoizer/-/instrumentation-lru-memoizer-0.63.0.tgz", - "integrity": "sha512-DlZRNXfiosmREoLbEGbYuxF70cYXjrYqoaO1sJE167i1+ARWXTq0YMPJ97i53Ws/xkZWllJNYU4mpY2LM2yTlA==", + "version": "0.65.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-lru-memoizer/-/instrumentation-lru-memoizer-0.65.0.tgz", + "integrity": "sha512-s2KisLZ82iDvCF2QbsV1k1wrz3DMSBP9OiMfmNn5oSyaNT7jcNphR4uxr7WjwH+ssucuvhKWqKzJ2vdx7KRMVA==", "license": "Apache-2.0", "dependencies": { - "@opentelemetry/instrumentation": "^0.219.0" + "@opentelemetry/instrumentation": "^0.221.0" }, "engines": { "node": "^18.19.0 || >=20.6.0" @@ -816,12 +806,12 @@ } }, "node_modules/@opentelemetry/instrumentation-memcached": { - "version": "0.62.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-memcached/-/instrumentation-memcached-0.62.0.tgz", - "integrity": "sha512-kAajd/MtdRBh9PCrMM4fnusFRNPJDU1dv5w9cgnKtMfutRE6K03wBbGSeT3FD1M56sMYWVqPhiKUhfSZCMZrLg==", + "version": "0.64.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-memcached/-/instrumentation-memcached-0.64.0.tgz", + "integrity": "sha512-ek34tp7Qjci4CLahXybJ3aaixU1d2j28X5JSXSXbp6/rIiFGjMCAXJWw3FeiYmA82D/gV/wzPhL7r7m/p4gUzw==", "license": "Apache-2.0", "dependencies": { - "@opentelemetry/instrumentation": "^0.219.0", + "@opentelemetry/instrumentation": "^0.221.0", "@opentelemetry/semantic-conventions": "^1.33.0", "@types/memcached": "^2.2.6" }, @@ -833,12 +823,12 @@ } }, "node_modules/@opentelemetry/instrumentation-mongodb": { - "version": "0.72.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-mongodb/-/instrumentation-mongodb-0.72.0.tgz", - "integrity": "sha512-WYgGzvlHzdoxHlrhysYtjxE4RC23j/iFZ66hdMJuAoczOWoD/xb6LhRwaz4CM+LKyKtcSIadAjSUyGv2B+SNwg==", + "version": "0.74.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-mongodb/-/instrumentation-mongodb-0.74.0.tgz", + "integrity": "sha512-GRnHu69YLQUYgguuYkKi6wpizMY4r7gLC08rSq8cg41p6t7+1YIy5nXoGC61NA7KCZUE9jbcDnzd45i32IblZg==", "license": "Apache-2.0", "dependencies": { - "@opentelemetry/instrumentation": "^0.219.0", + "@opentelemetry/instrumentation": "^0.221.0", "@opentelemetry/semantic-conventions": "^1.33.0" }, "engines": { @@ -849,13 +839,13 @@ } }, "node_modules/@opentelemetry/instrumentation-mongoose": { - "version": "0.65.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-mongoose/-/instrumentation-mongoose-0.65.0.tgz", - "integrity": "sha512-P0iT4oKuinEFZlTIKPJC5hhnmiV9De9lEiLkGzSwnNLdkyHIWug8BfRp5ZROrYAQ9mm47H+WLB/ozvUvgzEvEA==", + "version": "0.67.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-mongoose/-/instrumentation-mongoose-0.67.0.tgz", + "integrity": "sha512-iEBgNrychD36qI16X/V8WZb2JabjQPE+pyrkLU320ApZyhGVsYU9LH3Nqt4Mkg1nFsa9qWhRQrCfhoANCgT6EA==", "license": "Apache-2.0", "dependencies": { - "@opentelemetry/core": "^2.0.0", - "@opentelemetry/instrumentation": "^0.219.0", + "@opentelemetry/core": "^2.9.0", + "@opentelemetry/instrumentation": "^0.221.0", "@opentelemetry/semantic-conventions": "^1.33.0" }, "engines": { @@ -866,12 +856,12 @@ } }, "node_modules/@opentelemetry/instrumentation-mysql": { - "version": "0.65.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-mysql/-/instrumentation-mysql-0.65.0.tgz", - "integrity": "sha512-sh1wRjFaTt+8DOhJ0134rFtJoHVUXKI8faIWTbj4zZNw847dzUgmkO8xOluJ9Css0JzT4vCeZofJmq9mQmmESA==", + "version": "0.67.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-mysql/-/instrumentation-mysql-0.67.0.tgz", + "integrity": "sha512-G4aRrVKcd2Aodqi7WzRZ3LQJNKrM8BpsXEVMHrOb9s8Lg0jZXNvlLY0NWL1yVJrjnqI/unwEsp2aVHkVImPMeA==", "license": "Apache-2.0", "dependencies": { - "@opentelemetry/instrumentation": "^0.219.0", + "@opentelemetry/instrumentation": "^0.221.0", "@opentelemetry/semantic-conventions": "^1.33.0", "@types/mysql": "2.15.27" }, @@ -883,12 +873,12 @@ } }, "node_modules/@opentelemetry/instrumentation-mysql2": { - "version": "0.65.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-mysql2/-/instrumentation-mysql2-0.65.0.tgz", - "integrity": "sha512-Om6BJ/bmFBzNkGbAzj/UV5sCKX6jCGzhTl1Gqgtim/O0dnPE7F2zN65u16Fq3JgyypGAwT2iwh13tYdWkc8/RA==", + "version": "0.67.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-mysql2/-/instrumentation-mysql2-0.67.0.tgz", + "integrity": "sha512-AmviR7l0xMxhC83scY3u+NkkT6blhD/xK9tPi9nYtjNG1gwPtMgZjYOa3f9lGOwpXs/EwN7wiyAgxiO4KTcENA==", "license": "Apache-2.0", "dependencies": { - "@opentelemetry/instrumentation": "^0.219.0", + "@opentelemetry/instrumentation": "^0.221.0", "@opentelemetry/semantic-conventions": "^1.33.0", "@opentelemetry/sql-common": "^0.42.0" }, @@ -900,12 +890,12 @@ } }, "node_modules/@opentelemetry/instrumentation-nestjs-core": { - "version": "0.65.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-nestjs-core/-/instrumentation-nestjs-core-0.65.0.tgz", - "integrity": "sha512-/q8fN2M2zGl+gQRaF79dzqvyvVqHAI11c7xAQZy9W1eAtQScNwaQtPm0EUo5+aOgakaTksBrsiHUF0rQS24NsQ==", + "version": "0.67.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-nestjs-core/-/instrumentation-nestjs-core-0.67.0.tgz", + "integrity": "sha512-lXb7pjobd2i/9Gmihf9wrOM0MgnDYBxOJq7uWEhZOqULodNFLyPCRUnWxSIbPQdUzlU36QtHjuAeaEBUJnqXkA==", "license": "Apache-2.0", "dependencies": { - "@opentelemetry/instrumentation": "^0.219.0", + "@opentelemetry/instrumentation": "^0.221.0", "@opentelemetry/semantic-conventions": "^1.30.0" }, "engines": { @@ -916,12 +906,12 @@ } }, "node_modules/@opentelemetry/instrumentation-net": { - "version": "0.63.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-net/-/instrumentation-net-0.63.0.tgz", - "integrity": "sha512-fvmVdL4SlsYZf74mq6iLBPd6JJHRAe5utzN7Wt9e4nwa2S3pgExA9poOEmBL1CvIR47MceTA/A8vgVCF23ebbw==", + "version": "0.65.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-net/-/instrumentation-net-0.65.0.tgz", + "integrity": "sha512-W82H8UvaSrWynpI510CNJbq2Aq6L4/zuR/dAvoCZVYeRiChi0LHMI8i3rPe3Tmau2WBwE0jimaWgOs0GCfIHbQ==", "license": "Apache-2.0", "dependencies": { - "@opentelemetry/instrumentation": "^0.219.0", + "@opentelemetry/instrumentation": "^0.221.0", "@opentelemetry/semantic-conventions": "^1.33.0" }, "engines": { @@ -932,13 +922,13 @@ } }, "node_modules/@opentelemetry/instrumentation-openai": { - "version": "0.17.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-openai/-/instrumentation-openai-0.17.0.tgz", - "integrity": "sha512-X3aEZnzj7SJkn1nmqEoD9IljmqENnGrd0vcJngcEApT8uqhFaeimONqKeIzKYvUgC7k4OkAUxC7yfXzxIK1KlA==", + "version": "0.19.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-openai/-/instrumentation-openai-0.19.0.tgz", + "integrity": "sha512-zHz7m/aUMDyAap7UMzaaxDkbmxEyUOfMBfh+7KICNwTBmIOypMnuUWwXWAfIJLgl+2dnZlNzxZRknULeS4YIhg==", "license": "Apache-2.0", "dependencies": { - "@opentelemetry/api-logs": "^0.219.0", - "@opentelemetry/instrumentation": "^0.219.0", + "@opentelemetry/api-logs": "^0.221.0", + "@opentelemetry/instrumentation": "^0.221.0", "@opentelemetry/semantic-conventions": "^1.36.0" }, "engines": { @@ -948,13 +938,25 @@ "@opentelemetry/api": "^1.3.0" } }, + "node_modules/@opentelemetry/instrumentation-openai/node_modules/@opentelemetry/api-logs": { + "version": "0.221.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/api-logs/-/api-logs-0.221.0.tgz", + "integrity": "sha512-OlanaW1vv7ufTqQ3/fPLI4arGt5ZoM+P8abOMki6uEYnpRazepSWDwDnnw+la7kE26SHVC18//SMccrDvLKOXQ==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/api": "^1.3.0" + }, + "engines": { + "node": ">=8.0.0" + } + }, "node_modules/@opentelemetry/instrumentation-oracledb": { - "version": "0.44.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-oracledb/-/instrumentation-oracledb-0.44.0.tgz", - "integrity": "sha512-ncEfP4rzuZXBHJJtzWLYctK4Pq/FvZRiASxlFY9CJG9kGjaFTfzBUys0NiP27alYPvpjj0uDAMheDk9nihO8ZA==", + "version": "0.46.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-oracledb/-/instrumentation-oracledb-0.46.0.tgz", + "integrity": "sha512-nqxQvbp7HvsVPyDdgiZADPQX6B6ZUtLfm+XPuJHtQ3anxIcqU6qhJlrDIEM2LrZMShqf7bs84RHfDt2rgsp7hg==", "license": "Apache-2.0", "dependencies": { - "@opentelemetry/instrumentation": "^0.219.0", + "@opentelemetry/instrumentation": "^0.221.0", "@opentelemetry/semantic-conventions": "^1.34.0", "@types/oracledb": "6.5.2" }, @@ -966,13 +968,13 @@ } }, "node_modules/@opentelemetry/instrumentation-pg": { - "version": "0.71.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-pg/-/instrumentation-pg-0.71.0.tgz", - "integrity": "sha512-jAhfyZeOkEKh3cQ5nm1tNWqHg7HFARyAe+p4BSoDHnB79c1woyEvDKqS11Hj/DjtceP+vrurIfcDs7Fqiy11mQ==", + "version": "0.73.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-pg/-/instrumentation-pg-0.73.0.tgz", + "integrity": "sha512-yf3tBVwLHB9cZNNPSToNrthx36ouPe4FctFxy7ya6vSJ6gaiKjNfA/IgFFeuBpZflEQhy6aesPqzZo8ZjFkvNg==", "license": "Apache-2.0", "dependencies": { - "@opentelemetry/core": "^2.0.0", - "@opentelemetry/instrumentation": "^0.219.0", + "@opentelemetry/core": "^2.9.0", + "@opentelemetry/instrumentation": "^0.221.0", "@opentelemetry/semantic-conventions": "^1.34.0", "@opentelemetry/sql-common": "^0.42.0", "@types/pg": "8.15.6", @@ -986,14 +988,15 @@ } }, "node_modules/@opentelemetry/instrumentation-pino": { - "version": "0.65.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-pino/-/instrumentation-pino-0.65.0.tgz", - "integrity": "sha512-p6eh+NRmzi1F+/4QG7XDqRk4ICdCNTsM5vdcwUPnpMie2MddgY1/ENmWvCF9r0Kh6QQRA2kkpnhoJFaiRQ5kVw==", + "version": "0.67.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-pino/-/instrumentation-pino-0.67.0.tgz", + "integrity": "sha512-Hb6phi2x1bq23OIiesj4imQvXs9Y5MMLtpgXH9hOuMm9LpBYz2cxPNgpgZ/XATuRclP1eRPoSl399o5XKwfoIA==", "license": "Apache-2.0", "dependencies": { - "@opentelemetry/api-logs": "^0.219.0", - "@opentelemetry/core": "^2.0.0", - "@opentelemetry/instrumentation": "^0.219.0" + "@opentelemetry/api-logs": "^0.221.0", + "@opentelemetry/core": "^2.9.0", + "@opentelemetry/instrumentation": "^0.221.0", + "@opentelemetry/semantic-conventions": "^1.41.1" }, "engines": { "node": "^18.19.0 || >=20.6.0" @@ -1002,13 +1005,25 @@ "@opentelemetry/api": "^1.3.0" } }, + "node_modules/@opentelemetry/instrumentation-pino/node_modules/@opentelemetry/api-logs": { + "version": "0.221.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/api-logs/-/api-logs-0.221.0.tgz", + "integrity": "sha512-OlanaW1vv7ufTqQ3/fPLI4arGt5ZoM+P8abOMki6uEYnpRazepSWDwDnnw+la7kE26SHVC18//SMccrDvLKOXQ==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/api": "^1.3.0" + }, + "engines": { + "node": ">=8.0.0" + } + }, "node_modules/@opentelemetry/instrumentation-redis": { - "version": "0.67.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-redis/-/instrumentation-redis-0.67.0.tgz", - "integrity": "sha512-TBjO4bPvfGH6bRjJJ+KrJhEqpHg3SWCFZ84MqsTWF639RQZmvkMhT2/DJsBAqqkq33IvHoDJysserqAfZN1s+Q==", + "version": "0.69.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-redis/-/instrumentation-redis-0.69.0.tgz", + "integrity": "sha512-lyCIEW89cYhMwaUSMBzsKHdwH2wOoqmuwXOARJneo9UL55govLIUCbYYAJ457oM8kdKADymlY4+SUW0DKQeIHw==", "license": "Apache-2.0", "dependencies": { - "@opentelemetry/instrumentation": "^0.219.0", + "@opentelemetry/instrumentation": "^0.221.0", "@opentelemetry/redis-common": "^0.38.3", "@opentelemetry/semantic-conventions": "^1.27.0" }, @@ -1020,13 +1035,13 @@ } }, "node_modules/@opentelemetry/instrumentation-restify": { - "version": "0.64.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-restify/-/instrumentation-restify-0.64.0.tgz", - "integrity": "sha512-X+gL4KpfPAx7Y07zKQVtyJPckhCcYJdSlEz0Kq0iR5nkQ8/AVWJ05/txl4voZbZdCuNdn+uLZTtJ60bAQwputQ==", + "version": "0.66.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-restify/-/instrumentation-restify-0.66.0.tgz", + "integrity": "sha512-9zbnL0ML2jFgJmmG1XPQTqwopCogC8eAtUQ0SXvYb+Ux2yuOBOvSg9XvRk/hcQf6WsRMll47cELCMC+IaI9I+g==", "license": "Apache-2.0", "dependencies": { - "@opentelemetry/core": "^2.0.0", - "@opentelemetry/instrumentation": "^0.219.0", + "@opentelemetry/core": "^2.9.0", + "@opentelemetry/instrumentation": "^0.221.0", "@opentelemetry/semantic-conventions": "^1.27.0" }, "engines": { @@ -1037,12 +1052,12 @@ } }, "node_modules/@opentelemetry/instrumentation-router": { - "version": "0.63.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-router/-/instrumentation-router-0.63.0.tgz", - "integrity": "sha512-zIpsZSHGvbaqiEazwUm1X0FkPnLXIwZcL/llu/UplkeGNU58bsw70l2uMqNqTb7J+tzsBC09CLVPty5BhW3X9Q==", + "version": "0.65.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-router/-/instrumentation-router-0.65.0.tgz", + "integrity": "sha512-ti9tDLFLhLoev5U/cGeQpFTlOjFFwrFbieZmwTFql9z8EijXDzFE7a3+3mgnxGl9CZR0luKCwFD/o51NPU+Ngg==", "license": "Apache-2.0", "dependencies": { - "@opentelemetry/instrumentation": "^0.219.0", + "@opentelemetry/instrumentation": "^0.221.0", "@opentelemetry/semantic-conventions": "^1.27.0" }, "engines": { @@ -1053,14 +1068,14 @@ } }, "node_modules/@opentelemetry/instrumentation-runtime-node": { - "version": "0.32.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-runtime-node/-/instrumentation-runtime-node-0.32.0.tgz", - "integrity": "sha512-Jo1jSgrHlah3lPpGNPsIpF0q52D5uSLRJrztWUoPc1/Tli2ZWZ+cArgNtcdmiLuKhW21MwYbbcrNw1fbOPeR3A==", + "version": "0.34.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-runtime-node/-/instrumentation-runtime-node-0.34.0.tgz", + "integrity": "sha512-Yb3PcmuK/iIOWY49GSEcSGl7fR4r6UqhZnuy5EWvFaqKqqs9SYnQeyQUEAbnBnSougRpwFBDbdN1SSGcvMhbtQ==", "license": "Apache-2.0", "dependencies": { - "@opentelemetry/api-logs": "^0.219.0", + "@opentelemetry/api-logs": "^0.221.0", "@opentelemetry/core": "^2.0.0", - "@opentelemetry/instrumentation": "^0.219.0" + "@opentelemetry/instrumentation": "^0.221.0" }, "engines": { "node": "^18.19.0 || >=20.6.0" @@ -1069,13 +1084,25 @@ "@opentelemetry/api": "^1.3.0" } }, + "node_modules/@opentelemetry/instrumentation-runtime-node/node_modules/@opentelemetry/api-logs": { + "version": "0.221.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/api-logs/-/api-logs-0.221.0.tgz", + "integrity": "sha512-OlanaW1vv7ufTqQ3/fPLI4arGt5ZoM+P8abOMki6uEYnpRazepSWDwDnnw+la7kE26SHVC18//SMccrDvLKOXQ==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/api": "^1.3.0" + }, + "engines": { + "node": ">=8.0.0" + } + }, "node_modules/@opentelemetry/instrumentation-socket.io": { - "version": "0.66.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-socket.io/-/instrumentation-socket.io-0.66.0.tgz", - "integrity": "sha512-XrZmLkFJktVLd3biQiP8BAhupRwPWLHGIiDCfyDAnWI6borIL0wD6BpwFKKPT/etpu4/5OaeAQ7qs/S+FY9nhQ==", + "version": "0.68.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-socket.io/-/instrumentation-socket.io-0.68.0.tgz", + "integrity": "sha512-Bhd0KApVBYV4WQMZZbKRYfvev7SudvCtSn6b36uyUbKowOMEoGpnVoIvm0rlrrBR0KmkcV3Y37SngCGUrTm3lg==", "license": "Apache-2.0", "dependencies": { - "@opentelemetry/instrumentation": "^0.219.0" + "@opentelemetry/instrumentation": "^0.221.0" }, "engines": { "node": "^18.19.0 || >=20.6.0" @@ -1085,12 +1112,12 @@ } }, "node_modules/@opentelemetry/instrumentation-tedious": { - "version": "0.38.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-tedious/-/instrumentation-tedious-0.38.0.tgz", - "integrity": "sha512-9sRWyIMBHDqJvxRVZ+eQ7jHJ9Iu+DapO27WLZbQF1nyD8xIvEMuDonQ/HnlQiaRdwnqFknXSQTFusJUT3mNVyQ==", + "version": "0.40.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-tedious/-/instrumentation-tedious-0.40.0.tgz", + "integrity": "sha512-zTNNxs+KUJf1J+lHzeTDxAIZdVJYvQ8mvGUfyiWcVFgVdl7+4XV+wOBMSd1tZcRRlopfcVODDCOVMx/N7+zvcA==", "license": "Apache-2.0", "dependencies": { - "@opentelemetry/instrumentation": "^0.219.0", + "@opentelemetry/instrumentation": "^0.221.0", "@opentelemetry/semantic-conventions": "^1.33.0", "@types/tedious": "^4.0.14" }, @@ -1102,13 +1129,13 @@ } }, "node_modules/@opentelemetry/instrumentation-undici": { - "version": "0.29.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-undici/-/instrumentation-undici-0.29.0.tgz", - "integrity": "sha512-SnA+0XgGc595jtnwFVfWy7Vgfr5hle4D5YKIlm0U4z8aK9YoCZVUn1xAkVZ2evaJyykiDF50FBzr1XZ0uj8CPA==", + "version": "0.31.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-undici/-/instrumentation-undici-0.31.0.tgz", + "integrity": "sha512-qunCfgSFV+bjRdAYkWIjVX38jIN/Xj80CiERXXdzYAmdigCFvJPB3AY3j43bjJlkhgbJJSCGdbvQUSut8QIiyQ==", "license": "Apache-2.0", "dependencies": { - "@opentelemetry/core": "^2.0.0", - "@opentelemetry/instrumentation": "^0.219.0", + "@opentelemetry/core": "^2.9.0", + "@opentelemetry/instrumentation": "^0.221.0", "@opentelemetry/semantic-conventions": "^1.24.0" }, "engines": { @@ -1119,13 +1146,13 @@ } }, "node_modules/@opentelemetry/instrumentation-winston": { - "version": "0.63.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-winston/-/instrumentation-winston-0.63.0.tgz", - "integrity": "sha512-NFMHLYODph0rWGfT/QLv75hCsu1sxVAV79L8HduBCMo181jOTSRZHaqxfrvDtFOsvgYBaiUBa7Ga50w47wM3FA==", + "version": "0.65.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-winston/-/instrumentation-winston-0.65.0.tgz", + "integrity": "sha512-hWSPnS530deRa+ttzY+QiGmgsK7aQHpqxbQRm56yO1j4qnIXuYYHaoSJ8/4lLOEcXB+hZs0mAAJ6QiMl1aaiGA==", "license": "Apache-2.0", "dependencies": { - "@opentelemetry/api-logs": "^0.219.0", - "@opentelemetry/instrumentation": "^0.219.0" + "@opentelemetry/api-logs": "^0.221.0", + "@opentelemetry/instrumentation": "^0.221.0" }, "engines": { "node": "^18.19.0 || >=20.6.0" @@ -1134,14 +1161,38 @@ "@opentelemetry/api": "^1.3.0" } }, + "node_modules/@opentelemetry/instrumentation-winston/node_modules/@opentelemetry/api-logs": { + "version": "0.221.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/api-logs/-/api-logs-0.221.0.tgz", + "integrity": "sha512-OlanaW1vv7ufTqQ3/fPLI4arGt5ZoM+P8abOMki6uEYnpRazepSWDwDnnw+la7kE26SHVC18//SMccrDvLKOXQ==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/api": "^1.3.0" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/@opentelemetry/instrumentation/node_modules/@opentelemetry/api-logs": { + "version": "0.221.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/api-logs/-/api-logs-0.221.0.tgz", + "integrity": "sha512-OlanaW1vv7ufTqQ3/fPLI4arGt5ZoM+P8abOMki6uEYnpRazepSWDwDnnw+la7kE26SHVC18//SMccrDvLKOXQ==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/api": "^1.3.0" + }, + "engines": { + "node": ">=8.0.0" + } + }, "node_modules/@opentelemetry/otlp-exporter-base": { - "version": "0.219.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/otlp-exporter-base/-/otlp-exporter-base-0.219.0.tgz", - "integrity": "sha512-zvIxQX/AZUVKDU+hCuYx+7UkiP7GRdnk1ZbFQRYzHvYp47cAWR4j3IhoPhV9KaeXEv2xdGq3IA6PnpzDmLcmSA==", + "version": "0.221.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/otlp-exporter-base/-/otlp-exporter-base-0.221.0.tgz", + "integrity": "sha512-UFPIq80OH3Ns/oPFHRj14d4DTOxUo+MUFU8hUiCq5jTqFhdeJnfVSANHT+xp92409cA+oxzvlZCe6NM1wvCuBA==", "license": "Apache-2.0", "dependencies": { - "@opentelemetry/core": "2.8.0", - "@opentelemetry/otlp-transformer": "0.219.0" + "@opentelemetry/core": "2.10.0", + "@opentelemetry/otlp-transformer": "0.221.0" }, "engines": { "node": "^18.19.0 || >=20.6.0" @@ -1151,15 +1202,15 @@ } }, "node_modules/@opentelemetry/otlp-grpc-exporter-base": { - "version": "0.219.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/otlp-grpc-exporter-base/-/otlp-grpc-exporter-base-0.219.0.tgz", - "integrity": "sha512-iIk/s8QQu39zpTrRRmsW/Eg3SE2+Hg8tLWepr2FLRgmwUpNd0IpCTLJEHJ77hpt4hgIS8MAh44UYI4xQPZwWlw==", + "version": "0.221.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/otlp-grpc-exporter-base/-/otlp-grpc-exporter-base-0.221.0.tgz", + "integrity": "sha512-rQDmNgyiGCTrescjnzH2ntVyUKVIq6I2UjuK8+stT/Xg0ZOT71FVJqwjFdspQl6Yol/Yqsut9bDo+ame8oTmDQ==", "license": "Apache-2.0", "dependencies": { "@grpc/grpc-js": "^1.14.3", - "@opentelemetry/core": "2.8.0", - "@opentelemetry/otlp-exporter-base": "0.219.0", - "@opentelemetry/otlp-transformer": "0.219.0" + "@opentelemetry/core": "2.10.0", + "@opentelemetry/otlp-exporter-base": "0.221.0", + "@opentelemetry/otlp-transformer": "0.221.0" }, "engines": { "node": "^18.19.0 || >=20.6.0" @@ -1169,17 +1220,17 @@ } }, "node_modules/@opentelemetry/otlp-transformer": { - "version": "0.219.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/otlp-transformer/-/otlp-transformer-0.219.0.tgz", - "integrity": "sha512-aaYKAyXhw9VchKZVGOopD3Gw/kPsyrX2c6IQ0AW32mTjqmZOh5Y6Gf5OYqTNqVktAeBjmFinhyFaCwW6GYK9YQ==", + "version": "0.221.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/otlp-transformer/-/otlp-transformer-0.221.0.tgz", + "integrity": "sha512-lg6lkOU08Az23jVcn/0Els9HP+V8PnR4Km6p0KgpTggS0n/WuhnmY64rSh83Of9iR9nD+dpWr6adlcX8KzAwjg==", "license": "Apache-2.0", "dependencies": { - "@opentelemetry/api-logs": "0.219.0", - "@opentelemetry/core": "2.8.0", - "@opentelemetry/resources": "2.8.0", - "@opentelemetry/sdk-logs": "0.219.0", - "@opentelemetry/sdk-metrics": "2.8.0", - "@opentelemetry/sdk-trace-base": "2.8.0" + "@opentelemetry/api-logs": "0.221.0", + "@opentelemetry/core": "2.10.0", + "@opentelemetry/resources": "2.10.0", + "@opentelemetry/sdk-logs": "0.221.0", + "@opentelemetry/sdk-metrics": "2.10.0", + "@opentelemetry/sdk-trace": "2.10.0" }, "engines": { "node": "^18.19.0 || >=20.6.0" @@ -1188,6 +1239,18 @@ "@opentelemetry/api": "^1.3.0" } }, + "node_modules/@opentelemetry/otlp-transformer/node_modules/@opentelemetry/api-logs": { + "version": "0.221.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/api-logs/-/api-logs-0.221.0.tgz", + "integrity": "sha512-OlanaW1vv7ufTqQ3/fPLI4arGt5ZoM+P8abOMki6uEYnpRazepSWDwDnnw+la7kE26SHVC18//SMccrDvLKOXQ==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/api": "^1.3.0" + }, + "engines": { + "node": ">=8.0.0" + } + }, "node_modules/@opentelemetry/propagator-aws-xray": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/@opentelemetry/propagator-aws-xray/-/propagator-aws-xray-2.2.0.tgz", @@ -1201,12 +1264,12 @@ } }, "node_modules/@opentelemetry/propagator-b3": { - "version": "2.8.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/propagator-b3/-/propagator-b3-2.8.0.tgz", - "integrity": "sha512-SazlvuSKi5533rPHTW2TwBwdMakhjZST4SYs0YauuvfGDkT13KbG1gJS75hV0uWVeevhtVP9sAIlaZLTHdSbMg==", + "version": "2.10.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/propagator-b3/-/propagator-b3-2.10.0.tgz", + "integrity": "sha512-GnA5B24H+1w8BO21J0q+IWNB0z1v+AGbcquTdIt/dufibhnhgxaA8YKvz0I3akRZhB1jHT+/tlzK+qlAjEDybQ==", "license": "Apache-2.0", "dependencies": { - "@opentelemetry/core": "2.8.0" + "@opentelemetry/core": "2.10.0" }, "engines": { "node": "^18.19.0 || >=20.6.0" @@ -1216,12 +1279,12 @@ } }, "node_modules/@opentelemetry/propagator-jaeger": { - "version": "2.8.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/propagator-jaeger/-/propagator-jaeger-2.8.0.tgz", - "integrity": "sha512-Xnz9zZvvQzUw+9DrOn0MomR7BxFCkA2pcfXBQuHC28ndJpSbjLs7knzYb05kw5SyCjSsEWombkZMgGcJSk8JVg==", + "version": "2.10.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/propagator-jaeger/-/propagator-jaeger-2.10.0.tgz", + "integrity": "sha512-yw/IX8DL470dSMZJoE82ScfYGp7JWZ/G8kFJo35ZILUVTB2jFPTOaioN+8s09pH0RHsWNhweVZb+ZnjJJpCChg==", "license": "Apache-2.0", "dependencies": { - "@opentelemetry/core": "2.8.0" + "@opentelemetry/core": "2.10.0" }, "engines": { "node": "^18.19.0 || >=20.6.0" @@ -1240,9 +1303,9 @@ } }, "node_modules/@opentelemetry/resource-detector-alibaba-cloud": { - "version": "0.34.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/resource-detector-alibaba-cloud/-/resource-detector-alibaba-cloud-0.34.0.tgz", - "integrity": "sha512-hUs4CK7MbRfffw8y5zR4Mo37MJRR3Zt8Ub4rgMkIk7gL8jozjV7k+zwIk9grz5kGAuD406BDDIghaY8090K4Zw==", + "version": "0.36.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/resource-detector-alibaba-cloud/-/resource-detector-alibaba-cloud-0.36.0.tgz", + "integrity": "sha512-s75zJV1ShpYL5nk2cODfZY05Haw2hGxcfEFMu3ymvh2QU3HrhXaCW+rmNkhXhRrO8YophMFTyVdb7iCDleC/JQ==", "license": "Apache-2.0", "dependencies": { "@opentelemetry/core": "^2.0.0", @@ -1256,9 +1319,9 @@ } }, "node_modules/@opentelemetry/resource-detector-aws": { - "version": "2.19.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/resource-detector-aws/-/resource-detector-aws-2.19.0.tgz", - "integrity": "sha512-ELKQCtbc7g2ghbteLftKzXvI3MkIfENrRjAaYd2h9cWNj8g/Dx3oDzpxfcv9mBigtwwWCZwQRr8R//uxREpdrg==", + "version": "2.21.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/resource-detector-aws/-/resource-detector-aws-2.21.0.tgz", + "integrity": "sha512-Veavy+khoywR+Hv065SU5jucFTGTiW1KXo39CsJ+8wqdYYz8jiRJPnQ20Kd+X9HbV2+Abb0l5CrJIdxK1ZOqBg==", "license": "Apache-2.0", "dependencies": { "@opentelemetry/core": "^2.0.0", @@ -1273,9 +1336,9 @@ } }, "node_modules/@opentelemetry/resource-detector-azure": { - "version": "0.27.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/resource-detector-azure/-/resource-detector-azure-0.27.0.tgz", - "integrity": "sha512-m6HCEmK12QpEcWbKtvGpQtoDVceXtpB14AaaW/t5f6gHeLuGq1QivkuohkvKMkxgAdwIHxEQ8qof3whWBpd8JA==", + "version": "0.29.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/resource-detector-azure/-/resource-detector-azure-0.29.0.tgz", + "integrity": "sha512-lWm0vjjlQMoc4Xvvd+dW/OZWT/SI4w+cIN7kbm8KimIZCr1EpAyvyQ7WEOrGoBoXCpQCrsZ18uKTooDgiBCGIw==", "license": "Apache-2.0", "dependencies": { "@opentelemetry/core": "^2.0.0", @@ -1290,9 +1353,9 @@ } }, "node_modules/@opentelemetry/resource-detector-container": { - "version": "0.8.10", - "resolved": "https://registry.npmjs.org/@opentelemetry/resource-detector-container/-/resource-detector-container-0.8.10.tgz", - "integrity": "sha512-aMU2wG4ktcqy6zEotdbIkkX5ACKRxEZLX1ZlMH0dea0M2+2KfabJc1lHPqAIFaa+CD37fCION55e69QmYwqX4A==", + "version": "0.8.12", + "resolved": "https://registry.npmjs.org/@opentelemetry/resource-detector-container/-/resource-detector-container-0.8.12.tgz", + "integrity": "sha512-EJRFfIY26whY0w5RDxMRXlfBDgDS001JYMHuOVuDBBsRrV4MBqoVajR9B0L9Vy728+w/HNVnSQkpJFacFr+klg==", "license": "Apache-2.0", "dependencies": { "@opentelemetry/core": "^2.0.0", @@ -1306,9 +1369,9 @@ } }, "node_modules/@opentelemetry/resource-detector-gcp": { - "version": "0.54.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/resource-detector-gcp/-/resource-detector-gcp-0.54.0.tgz", - "integrity": "sha512-u+6sBzQO03QQGFhxjzFa7uNbH6iQpWTcrpWyomxuppH3AN/+1mm3DRVseS1CiRq9VBKrFO0UosWAdD7fWVUrrg==", + "version": "0.56.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/resource-detector-gcp/-/resource-detector-gcp-0.56.0.tgz", + "integrity": "sha512-H8yNeqTsuapbXs6MLZTtelfUCk+5D8jD3+KosCJaXOyx5gl3EWWvs70HbNXTUO4VYLxccySFYJYVCd8YMM0NJw==", "license": "Apache-2.0", "dependencies": { "@opentelemetry/core": "^2.0.0", @@ -1323,12 +1386,12 @@ } }, "node_modules/@opentelemetry/resources": { - "version": "2.8.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/resources/-/resources-2.8.0.tgz", - "integrity": "sha512-qmXQ27ilDbUK/vGMqwL8D4/rhn76C+sherM4wTbjlfknR8Nvfc/hCxjRJPhkzZzUsPiNg16SA31NxMabwttRjg==", + "version": "2.10.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/resources/-/resources-2.10.0.tgz", + "integrity": "sha512-q6MMm2zhggzsHVNbabYwut+a6nbuQQe3URUoxaojM/8K1IBfwwPzvxIjNi2/lI1TFe+fMHMW9MWhrtDLEXEnkA==", "license": "Apache-2.0", "dependencies": { - "@opentelemetry/core": "2.8.0", + "@opentelemetry/core": "2.10.0", "@opentelemetry/semantic-conventions": "^1.29.0" }, "engines": { @@ -1339,14 +1402,14 @@ } }, "node_modules/@opentelemetry/sdk-logs": { - "version": "0.219.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-logs/-/sdk-logs-0.219.0.tgz", - "integrity": "sha512-s6lTKRakaPClvKoWHRChxnXjDMkM/TQ30ff78jN6EBGf7MI7VzANE5PU3f4z9qDUudWjvZjOLHG0rBnBKYvoXA==", + "version": "0.221.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-logs/-/sdk-logs-0.221.0.tgz", + "integrity": "sha512-FaDcazjyMp7TZZZAsqbo4IkovP0UegoCu0EBkiNt+qCqvUf7FPAsfcrZ3+ZEkKgXZ/jHafop+JoGPDk3A0SmLg==", "license": "Apache-2.0", "dependencies": { - "@opentelemetry/api-logs": "0.219.0", - "@opentelemetry/core": "2.8.0", - "@opentelemetry/resources": "2.8.0", + "@opentelemetry/api-logs": "0.221.0", + "@opentelemetry/core": "2.10.0", + "@opentelemetry/resources": "2.10.0", "@opentelemetry/semantic-conventions": "^1.29.0" }, "engines": { @@ -1356,14 +1419,26 @@ "@opentelemetry/api": ">=1.4.0 <1.10.0" } }, + "node_modules/@opentelemetry/sdk-logs/node_modules/@opentelemetry/api-logs": { + "version": "0.221.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/api-logs/-/api-logs-0.221.0.tgz", + "integrity": "sha512-OlanaW1vv7ufTqQ3/fPLI4arGt5ZoM+P8abOMki6uEYnpRazepSWDwDnnw+la7kE26SHVC18//SMccrDvLKOXQ==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/api": "^1.3.0" + }, + "engines": { + "node": ">=8.0.0" + } + }, "node_modules/@opentelemetry/sdk-metrics": { - "version": "2.8.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-metrics/-/sdk-metrics-2.8.0.tgz", - "integrity": "sha512-UDBGaj6W0Rgy5rTTaoxs8gVGF/aGkAKyjurJv7se6wjRxJu7FoquTLT/vt54DZfo4crbprYfhX/SOK9+BPw1qg==", + "version": "2.10.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-metrics/-/sdk-metrics-2.10.0.tgz", + "integrity": "sha512-t6r1VSvXNtSDnPXU1FbZeetJb7yyovHmgu0wRSoftxtE0g2rSNhQZQUy69sRUCL+iioJpX8SN/S6wq6ZtvLySQ==", "license": "Apache-2.0", "dependencies": { - "@opentelemetry/core": "2.8.0", - "@opentelemetry/resources": "2.8.0" + "@opentelemetry/core": "2.10.0", + "@opentelemetry/resources": "2.10.0" }, "engines": { "node": "^18.19.0 || >=20.6.0" @@ -1373,36 +1448,66 @@ } }, "node_modules/@opentelemetry/sdk-node": { - "version": "0.219.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-node/-/sdk-node-0.219.0.tgz", - "integrity": "sha512-NWLpWLEb8gV3+JBHYoIrktbM385wyHpRJoh3J/4Q52d4PR+AlPMNGJT3DzBUrDSUEVbKAXoHR+EDAPxtiNcj8g==", - "license": "Apache-2.0", - "dependencies": { - "@opentelemetry/api-logs": "0.219.0", - "@opentelemetry/configuration": "0.219.0", - "@opentelemetry/context-async-hooks": "2.8.0", - "@opentelemetry/core": "2.8.0", - "@opentelemetry/exporter-logs-otlp-grpc": "0.219.0", - "@opentelemetry/exporter-logs-otlp-http": "0.219.0", - "@opentelemetry/exporter-logs-otlp-proto": "0.219.0", - "@opentelemetry/exporter-metrics-otlp-grpc": "0.219.0", - "@opentelemetry/exporter-metrics-otlp-http": "0.219.0", - "@opentelemetry/exporter-metrics-otlp-proto": "0.219.0", - "@opentelemetry/exporter-prometheus": "0.219.0", - "@opentelemetry/exporter-trace-otlp-grpc": "0.219.0", - "@opentelemetry/exporter-trace-otlp-http": "0.219.0", - "@opentelemetry/exporter-trace-otlp-proto": "0.219.0", - "@opentelemetry/exporter-zipkin": "2.8.0", - "@opentelemetry/instrumentation": "0.219.0", - "@opentelemetry/otlp-exporter-base": "0.219.0", - "@opentelemetry/otlp-grpc-exporter-base": "0.219.0", - "@opentelemetry/propagator-b3": "2.8.0", - "@opentelemetry/propagator-jaeger": "2.8.0", - "@opentelemetry/resources": "2.8.0", - "@opentelemetry/sdk-logs": "0.219.0", - "@opentelemetry/sdk-metrics": "2.8.0", - "@opentelemetry/sdk-trace-base": "2.8.0", - "@opentelemetry/sdk-trace-node": "2.8.0", + "version": "0.221.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-node/-/sdk-node-0.221.0.tgz", + "integrity": "sha512-UbYuvtBrQQB5Prsh9KOKy4kxzexFxfMs5MkteHeWMoswsEB7kiNhyUVkAOFW/qsEzNHtrkgyghrD2ilZJa+5YA==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/api-logs": "0.221.0", + "@opentelemetry/configuration": "0.221.0", + "@opentelemetry/context-async-hooks": "2.10.0", + "@opentelemetry/core": "2.10.0", + "@opentelemetry/exporter-logs-otlp-grpc": "0.221.0", + "@opentelemetry/exporter-logs-otlp-http": "0.221.0", + "@opentelemetry/exporter-logs-otlp-proto": "0.221.0", + "@opentelemetry/exporter-metrics-otlp-grpc": "0.221.0", + "@opentelemetry/exporter-metrics-otlp-http": "0.221.0", + "@opentelemetry/exporter-metrics-otlp-proto": "0.221.0", + "@opentelemetry/exporter-prometheus": "0.221.0", + "@opentelemetry/exporter-trace-otlp-grpc": "0.221.0", + "@opentelemetry/exporter-trace-otlp-http": "0.221.0", + "@opentelemetry/exporter-trace-otlp-proto": "0.221.0", + "@opentelemetry/exporter-zipkin": "2.10.0", + "@opentelemetry/instrumentation": "0.221.0", + "@opentelemetry/otlp-exporter-base": "0.221.0", + "@opentelemetry/otlp-grpc-exporter-base": "0.221.0", + "@opentelemetry/propagator-b3": "2.10.0", + "@opentelemetry/propagator-jaeger": "2.10.0", + "@opentelemetry/resources": "2.10.0", + "@opentelemetry/sdk-logs": "0.221.0", + "@opentelemetry/sdk-metrics": "2.10.0", + "@opentelemetry/sdk-trace": "2.10.0", + "@opentelemetry/sdk-trace-base": "2.10.0", + "@opentelemetry/sdk-trace-node": "2.10.0", + "@opentelemetry/semantic-conventions": "^1.29.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.3.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/sdk-node/node_modules/@opentelemetry/api-logs": { + "version": "0.221.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/api-logs/-/api-logs-0.221.0.tgz", + "integrity": "sha512-OlanaW1vv7ufTqQ3/fPLI4arGt5ZoM+P8abOMki6uEYnpRazepSWDwDnnw+la7kE26SHVC18//SMccrDvLKOXQ==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/api": "^1.3.0" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/@opentelemetry/sdk-trace": { + "version": "2.10.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-trace/-/sdk-trace-2.10.0.tgz", + "integrity": "sha512-MfQGq3GRmTh5fM/y+OjaO0vj6+luCB1XO2gfXCalKCfgKw0eHL++sm75DNweC6ohlp+aFvACqeE0fYayqdRaoQ==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/core": "2.10.0", + "@opentelemetry/resources": "2.10.0", "@opentelemetry/semantic-conventions": "^1.29.0" }, "engines": { @@ -1413,13 +1518,14 @@ } }, "node_modules/@opentelemetry/sdk-trace-base": { - "version": "2.8.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-trace-base/-/sdk-trace-base-2.8.0.tgz", - "integrity": "sha512-mhU4jp+vW0mGbFRd+GeXHvmfA4aDqWjBjLC3pE5XMpLs0IE2ryYb019Ts2AQrOq67gaTF25D91+fgvEHDZEnuQ==", + "version": "2.10.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-trace-base/-/sdk-trace-base-2.10.0.tgz", + "integrity": "sha512-GuYQQT7QD2EeO8lcZLRQzcbOyhqAzL+6WWTKTU9mSUBYBazkEDl+VrQcXQhbB08OWM9anD1aHleVadzulpOaUQ==", "license": "Apache-2.0", "dependencies": { - "@opentelemetry/core": "2.8.0", - "@opentelemetry/resources": "2.8.0", + "@opentelemetry/core": "2.10.0", + "@opentelemetry/resources": "2.10.0", + "@opentelemetry/sdk-trace": "2.10.0", "@opentelemetry/semantic-conventions": "^1.29.0" }, "engines": { @@ -1430,14 +1536,14 @@ } }, "node_modules/@opentelemetry/sdk-trace-node": { - "version": "2.8.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-trace-node/-/sdk-trace-node-2.8.0.tgz", - "integrity": "sha512-nZt9OGufioAc3AfoLTqA9bsAeaMJAictYDdI2VcNQ+PmT+3rfKjAZDZvgPfd8VPX0O5Bw1hdQF6kDK8VSpZiWg==", + "version": "2.10.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-trace-node/-/sdk-trace-node-2.10.0.tgz", + "integrity": "sha512-GZK/G6oZyBLGlH1pUgeDch7D91KoHd2uotUGIkWCPi9GI5T9X0p4L7nNAMDR1BQjkRYoDqo+ddfVx9t5Uhys+Q==", "license": "Apache-2.0", "dependencies": { - "@opentelemetry/context-async-hooks": "2.8.0", - "@opentelemetry/core": "2.8.0", - "@opentelemetry/sdk-trace-base": "2.8.0" + "@opentelemetry/context-async-hooks": "2.10.0", + "@opentelemetry/core": "2.10.0", + "@opentelemetry/sdk-trace-base": "2.10.0" }, "engines": { "node": "^18.19.0 || >=20.6.0" @@ -1447,9 +1553,9 @@ } }, "node_modules/@opentelemetry/semantic-conventions": { - "version": "1.41.1", - "resolved": "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-1.41.1.tgz", - "integrity": "sha512-/UhIkaZgPutTFmQ7RnIJGgDXZmtEJ7Dvi86xNTFWcnRxVRNk/aotsqDJYeEvDP+FSMB2SdW+pQzNMcWP0rwuNA==", + "version": "1.43.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-1.43.0.tgz", + "integrity": "sha512-eSYWTm620tTk45EKSedaUL8MFYI8hW164hIXsgIHyxu3VobUB3fFCu5t0hQby6OoWRPsG1KkKUG2M5UadiLiVg==", "license": "Apache-2.0", "engines": { "node": ">=14" @@ -1471,12 +1577,13 @@ } }, "node_modules/@opentelemetry/winston-transport": { - "version": "0.29.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/winston-transport/-/winston-transport-0.29.0.tgz", - "integrity": "sha512-/VX3xYkWZIFWLasxqPSj/3JFu8Ea8zP22zFsiiSlM8cHQ9ciqmbuX8k2N2CTTezBknteCMeOxt255s59RN3Wkg==", + "version": "0.30.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/winston-transport/-/winston-transport-0.30.0.tgz", + "integrity": "sha512-sBuqGxyOBIRmzVtzI7K67W2KZwmjHr8nVZPCi6A3uwei8hGlgnjqVWdgX9wIjcvGxg5qs3wkOeaLWuFz/MsOYA==", "license": "Apache-2.0", "dependencies": { - "@opentelemetry/api-logs": "^0.219.0", + "@opentelemetry/api-logs": "^0.220.0", + "@opentelemetry/semantic-conventions": "^1.41.1", "winston-transport": "4.*" }, "engines": { @@ -1536,9 +1643,9 @@ } }, "node_modules/@types/node": { - "version": "26.0.0", - "resolved": "https://registry.npmjs.org/@types/node/-/node-26.0.0.tgz", - "integrity": "sha512-vf2YFi1iY9lHGwNJMs01biZFbKJkrZR1T6/MlzjhJLPdntOHLhTrDSnSVcdtvjihi4VQNlrFRIxLsDBlQpAipA==", + "version": "26.1.1", + "resolved": "https://registry.npmjs.org/@types/node/-/node-26.1.1.tgz", + "integrity": "sha512-nxAkRSVkN1Y0JC1W8ky/fTfkGsMmcrRsbx+3XoZE+rMOX71kLYTV7fLXpqud1GpbpP5TuffXFqfX7fH2GgZREw==", "license": "MIT", "dependencies": { "undici-types": "~8.3.0" @@ -1588,27 +1695,6 @@ "integrity": "sha512-6WaYesThRMCl19iryMYP7/x2OVgCtbIVflDGFpWnb9irXI3UjYE4AzmYuiUKY1AJstGijoY+MgUszMgRxIYTYw==", "license": "MIT" }, - "node_modules/acorn": { - "version": "8.17.0", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.17.0.tgz", - "integrity": "sha512-xRQbDb9BnwDafYNn6Vwl839DYVjqXYb1XVGtWAZ1kcDc6iwAL4hg3B1dZlRiuENFeO2H53gFG3in621AdERVAg==", - "license": "MIT", - "bin": { - "acorn": "bin/acorn" - }, - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/acorn-import-attributes": { - "version": "1.9.5", - "resolved": "https://registry.npmjs.org/acorn-import-attributes/-/acorn-import-attributes-1.9.5.tgz", - "integrity": "sha512-n02Vykv5uA3eHGM/Z2dQrcD56kL8TyDb2p1+0P83PClMnC/nc+anbQRhIOWnSq4Ke/KvDPrY3C9hDtC/A3eHnQ==", - "license": "MIT", - "peerDependencies": { - "acorn": "^8" - } - }, "node_modules/agent-base": { "version": "7.1.4", "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.4.tgz", @@ -1658,9 +1744,9 @@ } }, "node_modules/brace-expansion": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.1.1.tgz", - "integrity": "sha512-WR1cURNjuvBLMZBMbqM0UoE+WAfdUcEV1ccD8PVBVOI+Z3ND4+SZbN8RsfT2bMuG1qwz5RFvPukSZm5fF2D5eA==", + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.1.2.tgz", + "integrity": "sha512-w5JZcKgdhDOgOwm8H+KgbosopHMuGcl6qbulwjtz3SM7I7P3yW1eAjzMPLrIE+NQ9vjgANKHWeMHnrT0OXW1oA==", "license": "MIT", "dependencies": { "balanced-match": "^1.0.0" @@ -1829,6 +1915,12 @@ "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", "license": "MIT" }, + "node_modules/es-module-lexer": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-2.3.1.tgz", + "integrity": "sha512-shc1dbU90Yl/xq1QrC7QRtfcwURZuVRfPhZbDoldJ1cn1gzDvBaBWlv0eFolj5+0znnPJz5TXLxsN77X/12KTA==", + "license": "MIT" + }, "node_modules/escalade": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", @@ -1923,9 +2015,9 @@ } }, "node_modules/gcp-metadata": { - "version": "8.1.3", - "resolved": "https://registry.npmjs.org/gcp-metadata/-/gcp-metadata-8.1.3.tgz", - "integrity": "sha512-ziTrzUhhpL9Zk5k0HHzgP/KIpWDJT0VMBC/ynt/QIBvTW+UUcSivQRl6VlwTf/EilDxtSWklHoRsKy1c4k+59w==", + "version": "8.1.4", + "resolved": "https://registry.npmjs.org/gcp-metadata/-/gcp-metadata-8.1.4.tgz", + "integrity": "sha512-iJ9KMsiu+xKtNRX0PmGLSaIU3bUBAyzWTyqKemKPzNPsmmsBCQYmlNg+brEbES7IHSXtdVwzBPzx1vz3FAaipw==", "license": "Apache-2.0", "dependencies": { "gaxios": "7.1.3", @@ -1989,14 +2081,13 @@ } }, "node_modules/import-in-the-middle": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/import-in-the-middle/-/import-in-the-middle-3.1.0.tgz", - "integrity": "sha512-c0AeAV8VcwZzfYE7euTZY3H+VXUPMVugiovdosq80lqEXJmOekg3zGUAYg6KImHMaMuBoTUfTv7xNpUFdy0hJA==", + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/import-in-the-middle/-/import-in-the-middle-3.3.2.tgz", + "integrity": "sha512-jTd2FfOgOWOdgjkHuk/1Ms8VKFXkPs15ymYBETw1sAOrO/dY3XeGVRWir9qBbw7pXr0T2eTFwfCZ+N02HmiNGA==", "license": "Apache-2.0", "dependencies": { - "acorn": "^8.15.0", - "acorn-import-attributes": "^1.9.5", "cjs-module-lexer": "^2.2.0", + "es-module-lexer": "^2.2.0", "module-details-from-path": "^1.0.4" }, "engines": { @@ -2259,9 +2350,9 @@ } }, "node_modules/protobufjs": { - "version": "8.6.4", - "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-8.6.4.tgz", - "integrity": "sha512-/+XMv9JalknuncEJSwsyEVlwcxVLKx2iaoSUXFZA86MJkdqyOdfrlB1sB7S6aKyUk9tl20YY+SgQe5J2sJHTcg==", + "version": "8.7.1", + "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-8.7.1.tgz", + "integrity": "sha512-agdGHrXNTv0IrYscJPDou/PlEJk1c/hBZ9o/B5NH2i/nSPtPqacNxzgwf1CebXxFMjMrZH5sqv9uQuw96aGt/A==", "license": "BSD-3-Clause", "dependencies": { "long": "^5.3.2" @@ -2489,9 +2580,9 @@ } }, "node_modules/systeminformation": { - "version": "5.31.7", - "resolved": "https://registry.npmjs.org/systeminformation/-/systeminformation-5.31.7.tgz", - "integrity": "sha512-/8NC53e5nP9nmhn42/ncdOkyJnOoue/Vy+tJOyUGd1Yv66G069wK4rrziwhrqDETgk78CudTQupw5z19S5uoZw==", + "version": "5.33.1", + "resolved": "https://registry.npmjs.org/systeminformation/-/systeminformation-5.33.1.tgz", + "integrity": "sha512-DEN6ICHk3Tk0Uf/hrAHh7xlt7iL5CJFBtPZinA0H62DrGG/KPKqq/Nzj6lCXPS4Ay/sf/14zNnk9LpqKzBIc+w==", "license": "MIT", "os": [ "darwin", @@ -2507,7 +2598,7 @@ "systeminformation": "lib/cli.js" }, "engines": { - "node": ">=8.0.0" + "node": ">=10.0.0" }, "funding": { "type": "Buy me a coffee", diff --git a/nodejs-instrumentation/package.json b/nodejs-instrumentation/package.json index 217bc96c1..a53a74b8c 100644 --- a/nodejs-instrumentation/package.json +++ b/nodejs-instrumentation/package.json @@ -11,18 +11,18 @@ "license": "ISC", "dependencies": { "@opentelemetry/api": "^1.9.0", - "@opentelemetry/auto-instrumentations-node": "^0.77.0", - "@opentelemetry/exporter-metrics-otlp-grpc": "^0.219.0", - "@opentelemetry/exporter-trace-otlp-grpc": "^0.219.0", + "@opentelemetry/auto-instrumentations-node": "^0.79.0", + "@opentelemetry/exporter-metrics-otlp-grpc": "^0.221.0", + "@opentelemetry/exporter-trace-otlp-grpc": "^0.221.0", "@opentelemetry/propagator-aws-xray": "^2.2.0", "@opentelemetry/resource-detector-aws": "^2.19.0", "@opentelemetry/resource-detector-container": "^0.8.10", "@opentelemetry/resources": "^2.8.0", "@opentelemetry/sdk-metrics": "^2.8.0", - "@opentelemetry/sdk-node": "^0.219.0", + "@opentelemetry/sdk-node": "^0.221.0", "@opentelemetry/sdk-trace-base": "^2.8.0", - "@opentelemetry/semantic-conventions": "^1.41.1", - "@opentelemetry/winston-transport": "^0.29.0" + "@opentelemetry/semantic-conventions": "^1.43.0", + "@opentelemetry/winston-transport": "^0.30.0" }, "devDependencies": { "typescript": "^6.0.3" diff --git a/package-lock.json b/package-lock.json index 60c2763ab..992042da5 100644 --- a/package-lock.json +++ b/package-lock.json @@ -17,16 +17,16 @@ "@graphql-codegen/typescript-react-apollo": "^4.4.2", "@graphql-codegen/typescript-resolvers": "^5.1.8", "@ianvs/prettier-plugin-sort-imports": "^4.7.1", - "@parcel/watcher": "^2.5.6", + "@parcel/watcher": "^2.6.0", "@types/express": "^5.0.6", "@types/jest": "^30.0.0", - "@types/node": "^24.0.0", + "@types/node": "^24.13.3", "@types/passport": "^1.0.17", "@types/validator": "^13.15.10", "concurrently": "^10.0.3", "husky": "^9.1.7", "lint-staged": "^16.4.0", - "prettier": "^3.8.3", + "prettier": "^3.9.6", "ts-node": "^10.9.2", "typescript": "^5.9.0" }, @@ -1858,9 +1858,9 @@ } }, "node_modules/@parcel/watcher": { - "version": "2.5.6", - "resolved": "https://registry.npmjs.org/@parcel/watcher/-/watcher-2.5.6.tgz", - "integrity": "sha512-tmmZ3lQxAe/k/+rNnXQRawJ4NjxO2hqiOLTHvWchtGZULp4RyFeh6aU4XdOYBFe2KE1oShQTv4AblOs2iOrNnQ==", + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/@parcel/watcher/-/watcher-2.6.0.tgz", + "integrity": "sha512-7FNeNl8NCE7aINx7WXiKQrPYZWC/hvrTsmk6zmxbI7LTXE7hVek/n8AfVgpe2y82zl3w0HvCHN0bVKMBoJcC0w==", "dev": true, "hasInstallScript": true, "license": "MIT", @@ -1868,7 +1868,7 @@ "detect-libc": "^2.0.3", "is-glob": "^4.0.3", "node-addon-api": "^7.0.0", - "picomatch": "^4.0.3" + "picomatch": "^4.0.4" }, "engines": { "node": ">= 10.0.0" @@ -1878,25 +1878,24 @@ "url": "https://opencollective.com/parcel" }, "optionalDependencies": { - "@parcel/watcher-android-arm64": "2.5.6", - "@parcel/watcher-darwin-arm64": "2.5.6", - "@parcel/watcher-darwin-x64": "2.5.6", - "@parcel/watcher-freebsd-x64": "2.5.6", - "@parcel/watcher-linux-arm-glibc": "2.5.6", - "@parcel/watcher-linux-arm-musl": "2.5.6", - "@parcel/watcher-linux-arm64-glibc": "2.5.6", - "@parcel/watcher-linux-arm64-musl": "2.5.6", - "@parcel/watcher-linux-x64-glibc": "2.5.6", - "@parcel/watcher-linux-x64-musl": "2.5.6", - "@parcel/watcher-win32-arm64": "2.5.6", - "@parcel/watcher-win32-ia32": "2.5.6", - "@parcel/watcher-win32-x64": "2.5.6" + "@parcel/watcher-android-arm64": "2.6.0", + "@parcel/watcher-darwin-arm64": "2.6.0", + "@parcel/watcher-darwin-x64": "2.6.0", + "@parcel/watcher-freebsd-x64": "2.6.0", + "@parcel/watcher-linux-arm-glibc": "2.6.0", + "@parcel/watcher-linux-arm-musl": "2.6.0", + "@parcel/watcher-linux-arm64-glibc": "2.6.0", + "@parcel/watcher-linux-arm64-musl": "2.6.0", + "@parcel/watcher-linux-x64-glibc": "2.6.0", + "@parcel/watcher-linux-x64-musl": "2.6.0", + "@parcel/watcher-win32-arm64": "2.6.0", + "@parcel/watcher-win32-x64": "2.6.0" } }, "node_modules/@parcel/watcher-android-arm64": { - "version": "2.5.6", - "resolved": "https://registry.npmjs.org/@parcel/watcher-android-arm64/-/watcher-android-arm64-2.5.6.tgz", - "integrity": "sha512-YQxSS34tPF/6ZG7r/Ih9xy+kP/WwediEUsqmtf0cuCV5TPPKw/PQHRhueUo6JdeFJaqV3pyjm0GdYjZotbRt/A==", + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/@parcel/watcher-android-arm64/-/watcher-android-arm64-2.6.0.tgz", + "integrity": "sha512-trgpLSCKRC/huFjXX/Smh+0sWe4+YtKfktIToiMl59ghz7z+qkH6kMvNnUbLyRs9N11t8l4svSCs1+5B3rOAhA==", "cpu": [ "arm64" ], @@ -1915,9 +1914,9 @@ } }, "node_modules/@parcel/watcher-darwin-arm64": { - "version": "2.5.6", - "resolved": "https://registry.npmjs.org/@parcel/watcher-darwin-arm64/-/watcher-darwin-arm64-2.5.6.tgz", - "integrity": "sha512-Z2ZdrnwyXvvvdtRHLmM4knydIdU9adO3D4n/0cVipF3rRiwP+3/sfzpAwA/qKFL6i1ModaabkU7IbpeMBgiVEA==", + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/@parcel/watcher-darwin-arm64/-/watcher-darwin-arm64-2.6.0.tgz", + "integrity": "sha512-Y3QV0gl7Q1zbfueunkWIERICbEojQFCgpyG7YqOGNFLsckXyI1xu9mAIUpKY9QBYzBtSkN8dBPwd3yiAO9ovMw==", "cpu": [ "arm64" ], @@ -1936,9 +1935,9 @@ } }, "node_modules/@parcel/watcher-darwin-x64": { - "version": "2.5.6", - "resolved": "https://registry.npmjs.org/@parcel/watcher-darwin-x64/-/watcher-darwin-x64-2.5.6.tgz", - "integrity": "sha512-HgvOf3W9dhithcwOWX9uDZyn1lW9R+7tPZ4sug+NGrGIo4Rk1hAXLEbcH1TQSqxts0NYXXlOWqVpvS1SFS4fRg==", + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/@parcel/watcher-darwin-x64/-/watcher-darwin-x64-2.6.0.tgz", + "integrity": "sha512-Ohv6OpzhUfKYD7Beb8kDvG0jbIxORCYY1JRdZnaBtnjjkJxgD7ZVL0nw2sCYd0yTMKTvz3nnTnOF3cDifK+kvw==", "cpu": [ "x64" ], @@ -1957,9 +1956,9 @@ } }, "node_modules/@parcel/watcher-freebsd-x64": { - "version": "2.5.6", - "resolved": "https://registry.npmjs.org/@parcel/watcher-freebsd-x64/-/watcher-freebsd-x64-2.5.6.tgz", - "integrity": "sha512-vJVi8yd/qzJxEKHkeemh7w3YAn6RJCtYlE4HPMoVnCpIXEzSrxErBW5SJBgKLbXU3WdIpkjBTeUNtyBVn8TRng==", + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/@parcel/watcher-freebsd-x64/-/watcher-freebsd-x64-2.6.0.tgz", + "integrity": "sha512-5HmXvDgs8VK+74jF9y9/2FE3/OnlcKmc56tjmSrEuZjpSZOGL+fvAu+HKJBdPs9uwoP2hE6TlSUpXZ/C5jUFmQ==", "cpu": [ "x64" ], @@ -1978,9 +1977,9 @@ } }, "node_modules/@parcel/watcher-linux-arm-glibc": { - "version": "2.5.6", - "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm-glibc/-/watcher-linux-arm-glibc-2.5.6.tgz", - "integrity": "sha512-9JiYfB6h6BgV50CCfasfLf/uvOcJskMSwcdH1PHH9rvS1IrNy8zad6IUVPVUfmXr+u+Km9IxcfMLzgdOudz9EQ==", + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm-glibc/-/watcher-linux-arm-glibc-2.6.0.tgz", + "integrity": "sha512-Ps/hui3A+vMbjdqlqAowK2ZL8+BO8dBjxeWXj6npTBs3jx4wWmbPpaLuqwrQrSqIVMCnpWo238bJ1U37GhQOYg==", "cpu": [ "arm" ], @@ -2002,9 +2001,9 @@ } }, "node_modules/@parcel/watcher-linux-arm-musl": { - "version": "2.5.6", - "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm-musl/-/watcher-linux-arm-musl-2.5.6.tgz", - "integrity": "sha512-Ve3gUCG57nuUUSyjBq/MAM0CzArtuIOxsBdQ+ftz6ho8n7s1i9E1Nmk/xmP323r2YL0SONs1EuwqBp2u1k5fxg==", + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm-musl/-/watcher-linux-arm-musl-2.6.0.tgz", + "integrity": "sha512-9c6AUHgHoG+IY88MRIHupztQiQnrbqHYQjkM2btA+Bf/wQnQMuiD0Wfk1EVv3TlNT3x41uU71rn6E4xh/+zvkw==", "cpu": [ "arm" ], @@ -2026,9 +2025,9 @@ } }, "node_modules/@parcel/watcher-linux-arm64-glibc": { - "version": "2.5.6", - "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm64-glibc/-/watcher-linux-arm64-glibc-2.5.6.tgz", - "integrity": "sha512-f2g/DT3NhGPdBmMWYoxixqYr3v/UXcmLOYy16Bx0TM20Tchduwr4EaCbmxh1321TABqPGDpS8D/ggOTaljijOA==", + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm64-glibc/-/watcher-linux-arm64-glibc-2.6.0.tgz", + "integrity": "sha512-yHRqS2owEXe6Hic9z6Mh1ECsCd+ODVOGvZDyciqRd21+v+o+DnXMOrw50DSpIG2sb8GPEaPPmfeCAWKPJdq46g==", "cpu": [ "arm64" ], @@ -2050,9 +2049,9 @@ } }, "node_modules/@parcel/watcher-linux-arm64-musl": { - "version": "2.5.6", - "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm64-musl/-/watcher-linux-arm64-musl-2.5.6.tgz", - "integrity": "sha512-qb6naMDGlbCwdhLj6hgoVKJl2odL34z2sqkC7Z6kzir8b5W65WYDpLB6R06KabvZdgoHI/zxke4b3zR0wAbDTA==", + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm64-musl/-/watcher-linux-arm64-musl-2.6.0.tgz", + "integrity": "sha512-WhB2e/V7rqdHHWZusBSPuy5Ei8S6lSz6FE5TKKQz5h3a0O+C+mhY7vxU9b/stqvMb8beLnPY82ZrFTLKs+SrKA==", "cpu": [ "arm64" ], @@ -2074,9 +2073,9 @@ } }, "node_modules/@parcel/watcher-linux-x64-glibc": { - "version": "2.5.6", - "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-x64-glibc/-/watcher-linux-x64-glibc-2.5.6.tgz", - "integrity": "sha512-kbT5wvNQlx7NaGjzPFu8nVIW1rWqV780O7ZtkjuWaPUgpv2NMFpjYERVi0UYj1msZNyCzGlaCWEtzc+exjMGbQ==", + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-x64-glibc/-/watcher-linux-x64-glibc-2.6.0.tgz", + "integrity": "sha512-ulGE6x6Oz6iAwg75T8YQSoguBWasniIbX+QWpaYPcCnDOpdWX3k+4xbEYPZVLxOuoJI+svJJPD3sEj8G7lrQ3A==", "cpu": [ "x64" ], @@ -2098,9 +2097,9 @@ } }, "node_modules/@parcel/watcher-linux-x64-musl": { - "version": "2.5.6", - "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-x64-musl/-/watcher-linux-x64-musl-2.5.6.tgz", - "integrity": "sha512-1JRFeC+h7RdXwldHzTsmdtYR/Ku8SylLgTU/reMuqdVD7CtLwf0VR1FqeprZ0eHQkO0vqsbvFLXUmYm/uNKJBg==", + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-x64-musl/-/watcher-linux-x64-musl-2.6.0.tgz", + "integrity": "sha512-tkBYKt7YQrjIJWYDnto2YgO8MRkjlMTSNoRHzsXinBqbLdeOM3L32wPZJvIZxqaLMfSlS/4sUjH/6STVP/XDLw==", "cpu": [ "x64" ], @@ -2122,9 +2121,9 @@ } }, "node_modules/@parcel/watcher-win32-arm64": { - "version": "2.5.6", - "resolved": "https://registry.npmjs.org/@parcel/watcher-win32-arm64/-/watcher-win32-arm64-2.5.6.tgz", - "integrity": "sha512-3ukyebjc6eGlw9yRt678DxVF7rjXatWiHvTXqphZLvo7aC5NdEgFufVwjFfY51ijYEWpXbqF5jtrK275z52D4Q==", + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/@parcel/watcher-win32-arm64/-/watcher-win32-arm64-2.6.0.tgz", + "integrity": "sha512-gIZAP23jaHjGWasY/TY6yL7NHFClf0Ga7FN+iINvk+KN94rhm94lYZhFsbYFNcA04/onvGD9kKmiJLJB2HbNwQ==", "cpu": [ "arm64" ], @@ -2142,31 +2141,10 @@ "url": "https://opencollective.com/parcel" } }, - "node_modules/@parcel/watcher-win32-ia32": { - "version": "2.5.6", - "resolved": "https://registry.npmjs.org/@parcel/watcher-win32-ia32/-/watcher-win32-ia32-2.5.6.tgz", - "integrity": "sha512-k35yLp1ZMwwee3Ez/pxBi5cf4AoBKYXj00CZ80jUz5h8prpiaQsiRPKQMxoLstNuqe2vR4RNPEAEcjEFzhEz/g==", - "cpu": [ - "ia32" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">= 10.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, "node_modules/@parcel/watcher-win32-x64": { - "version": "2.5.6", - "resolved": "https://registry.npmjs.org/@parcel/watcher-win32-x64/-/watcher-win32-x64-2.5.6.tgz", - "integrity": "sha512-hbQlYcCq5dlAX9Qx+kFb0FHue6vbjlf0FrNzSKdYK2APUf7tGfGxQCk2ihEREmbR6ZMc0MVAD5RIX/41gpUzTw==", + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/@parcel/watcher-win32-x64/-/watcher-win32-x64-2.6.0.tgz", + "integrity": "sha512-cA+/pXV2YkfxlIcXOQ5fSWqAzzPyD78/x5qbK/I0vUkrlYHA8TIz+MXjAbGouguKVSI4bOmkTSJ1/poVSsgt+A==", "cpu": [ "x64" ], @@ -2319,13 +2297,13 @@ } }, "node_modules/@types/node": { - "version": "24.12.2", - "resolved": "https://registry.npmjs.org/@types/node/-/node-24.12.2.tgz", - "integrity": "sha512-A1sre26ke7HDIuY/M23nd9gfB+nrmhtYyMINbjI1zHJxYteKR6qSMX56FsmjMcDb3SMcjJg5BiRRgOCC/yBD0g==", + "version": "24.13.3", + "resolved": "https://registry.npmjs.org/@types/node/-/node-24.13.3.tgz", + "integrity": "sha512-Dh8vAsV36ig5wa9OX4pXvMc9D3Veibfw2wix0CUwYODLD8nkj9UsLjASr49nPg+2eKzxhBV+v7L8pXvT4e639Q==", "dev": true, "license": "MIT", "dependencies": { - "undici-types": "~7.16.0" + "undici-types": "~7.18.0" } }, "node_modules/@types/passport": { @@ -2567,16 +2545,16 @@ } }, "node_modules/brace-expansion": { - "version": "5.0.6", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.6.tgz", - "integrity": "sha512-kLpxurY4Z4r9sgMsyG0Z9uzsBlgiU/EFKhj/h91/8yHu0edo7XuixOIH3VcJ8kkxs6/jPzoI6U9Vj3WqbMQ94g==", + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.8.tgz", + "integrity": "sha512-JZyDyq3D4AUifKTPOB7DELf6XsB3WdPuNxCtob1vFXPsSXhdAiHBWJ/tJ8HAc9aH84BK+5JFZLNkJKx3G9kzQg==", "dev": true, "license": "MIT", "dependencies": { "balanced-match": "^4.0.2" }, "engines": { - "node": "18 || 20 || >=22" + "node": "20 || >=22" } }, "node_modules/braces": { @@ -3552,9 +3530,9 @@ } }, "node_modules/immutable": { - "version": "5.1.5", - "resolved": "https://registry.npmjs.org/immutable/-/immutable-5.1.5.tgz", - "integrity": "sha512-t7xcm2siw+hlUM68I+UEOK+z84RzmN59as9DZ7P1l0994DKUWV7UXBMQZVxaoMSRQ+PBZbHCOoBt7a2wxOMt+A==", + "version": "5.1.9", + "resolved": "https://registry.npmjs.org/immutable/-/immutable-5.1.9.tgz", + "integrity": "sha512-m8nVez3rwrgmWxtLMt1ZYXB2Lv7OKYn/disyxAlSDYAlKSlFoPPfIAmAM/M5xqL4m4C/wAPw7S2/CNaUii1Hxg==", "dev": true, "license": "MIT" }, @@ -4723,9 +4701,9 @@ } }, "node_modules/prettier": { - "version": "3.8.3", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.8.3.tgz", - "integrity": "sha512-7igPTM53cGHMW8xWuVTydi2KO233VFiTNyF5hLJqpilHfmn8C8gPf+PS7dUT64YcXFbiMGZxS9pCSxL/Dxm/Jw==", + "version": "3.9.6", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.9.6.tgz", + "integrity": "sha512-OpN0zzVdiaiAhxpuuj5efpIS4sY9j7bY6uR5mnj5yPzGkdkjNKSJeUThPb60Jw29QuAZgA4o+/iB49kFiaBX6g==", "dev": true, "license": "MIT", "bin": { @@ -5181,9 +5159,9 @@ } }, "node_modules/undici-types": { - "version": "7.16.0", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.16.0.tgz", - "integrity": "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw==", + "version": "7.18.2", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.18.2.tgz", + "integrity": "sha512-AsuCzffGHJybSaRrmr5eHr81mwJU3kjw6M+uprWvCXiNeN9SOGwQ3Jn8jb8m3Z6izVgknn1R0FTCEAP2QrLY/w==", "dev": true, "license": "MIT" }, diff --git a/package.json b/package.json index 2faf51920..ef32517b7 100644 --- a/package.json +++ b/package.json @@ -9,11 +9,11 @@ "client:start": "cd client && npm start", "server:start": "cd server && npm start", "create-org": "cd server && npm run create-org --", - "db:add": "cd db && npm i && NODE_OPTIONS=\"--loader ts-node/esm --require dotenv/config\" node src/index.ts add", - "db:clean": "cd db && npm i && NODE_OPTIONS=\"--loader ts-node/esm --require dotenv/config\" node src/index.ts clean", - "db:update": "cd db && npm i && NODE_OPTIONS=\"--loader ts-node/esm --require dotenv/config\" node src/index.ts apply", - "db:create": "cd db && npm i && NODE_OPTIONS=\"--loader ts-node/esm --require dotenv/config\" node src/index.ts create", - "db:drop": "cd db && npm i && NODE_OPTIONS=\"--loader ts-node/esm --require dotenv/config\" node src/index.ts drop", + "db:add": "cd db && npm i && NODE_OPTIONS=\"--loader ts-node/esm\" node --env-file-if-exists=.env src/index.ts add", + "db:clean": "cd db && npm i && NODE_OPTIONS=\"--loader ts-node/esm\" node --env-file-if-exists=.env src/index.ts clean", + "db:update": "cd db && npm i && NODE_OPTIONS=\"--loader ts-node/esm\" node --env-file-if-exists=.env src/index.ts apply", + "db:create": "cd db && npm i && NODE_OPTIONS=\"--loader ts-node/esm\" node --env-file-if-exists=.env src/index.ts create", + "db:drop": "cd db && npm i && NODE_OPTIONS=\"--loader ts-node/esm\" node --env-file-if-exists=.env src/index.ts drop", "check:prepush": "cd server && npm run check:prepush && cd ../client && npm run check:prepush", "typecheck": "cd server && npm run typecheck && cd ../client && npx tsc --noEmit", "prettier": "prettier --check \"./**/*.{ts,tsx,js,jsx,mjs,cjs,json,md,yaml,yml}\"", @@ -41,13 +41,13 @@ "lint-staged": "^16.4.0", "@types/express": "^5.0.6", "@types/jest": "^30.0.0", - "@types/node": "^24.0.0", + "@types/node": "^24.13.3", "@types/passport": "^1.0.17", "@types/validator": "^13.15.10", "concurrently": "^10.0.3", "husky": "^9.1.7", - "prettier": "^3.8.3", - "@parcel/watcher": "^2.5.6", + "prettier": "^3.9.6", + "@parcel/watcher": "^2.6.0", "ts-node": "^10.9.2", "typescript": "^5.9.0" } diff --git a/server/.env.example b/server/.env.example index 774142aae..27c6117fa 100644 --- a/server/.env.example +++ b/server/.env.example @@ -33,7 +33,7 @@ DATABASE_IDLE_IN_TRANSACTION_TIMEOUT_MS=300000 # Delay (ms) before sending the first keepalive probe. Unset => OS default. # DATABASE_KEEPALIVE_INITIAL_DELAY_MS=0 # Log every Kysely query (SQL, params, duration). Errors always log regardless. -DATABASE_PRINT_LOGS=true +# DATABASE_PRINT_LOGS=true # Used to sign session ids in cookies. @@ -76,6 +76,13 @@ CLICKHOUSE_PROTOCOL=http HMA_SERVICE_URL=http://localhost:9876 # Scylla Cluster Details +# Set to "false" to run Coop without a Scylla cluster. This disables the two +# Scylla-backed features — Item Investigation (item/user history views) and +# User Strikes (repeat-offender strike counts) — which then no-op: reads return +# empty (strike counts read as 0) and writes are dropped. When "false" (or +# "0"/"no"), the SCYLLA_* connection settings below are not required. +# Defaults to enabled. +ITEM_INVESTIGATION_AND_STRIKES_ENABLED=true SCYLLA_USERNAME=cassandra SCYLLA_PASSWORD=cassandra SCYLLA_HOSTS='127.0.0.1:9042' diff --git a/server/.eslintrc.cjs b/server/.eslintrc.cjs index b51c42f5f..1e7230047 100644 --- a/server/.eslintrc.cjs +++ b/server/.eslintrc.cjs @@ -694,6 +694,7 @@ module.exports = { }, ], 'no-console': 'off', + 'max-lines': 'off', // Allow `typeof import('...')` annotations; needed for E2E tests. '@typescript-eslint/consistent-type-imports': [ 'error', diff --git a/server/.gitignore b/server/.gitignore index 060dfba3e..bb3551c5d 100644 --- a/server/.gitignore +++ b/server/.gitignore @@ -3,3 +3,7 @@ build/ .eslintcache transpiled/ reports + +# Playwright e2e artifacts +e2e/test-results/ +e2e/playwright-report/ diff --git a/server/e2e/fixtures/coop.ts b/server/e2e/fixtures/coop.ts index b038f8438..04500136d 100644 --- a/server/e2e/fixtures/coop.ts +++ b/server/e2e/fixtures/coop.ts @@ -1,10 +1,10 @@ -// Load .env before anything reads process.env (the DI container does, heavily). -import 'dotenv/config'; - -import { test as base } from '@playwright/test'; +import { test as base, type APIRequestContext } from '@playwright/test'; import { uid } from 'uid'; import { type Dependencies } from '../../iocContainer/index.js'; +import { jsonStringify } from '../../utils/encoding.js'; + +export { jsonStringify }; /** * Server runtime is loaded from the COMPILED output (`transpiled/`), not the TS @@ -19,7 +19,14 @@ async function importIocContainer() { } async function importSeedHelpers() { - const [createOrg, ums, userPersistence] = await Promise.all([ + const [ + createOrg, + ums, + userPersistence, + createRule, + createMrtQueue, + itemSubmissionQueue, + ] = await Promise.all([ import(`${TRANSPILED}/test/fixtureHelpers/createOrg.js`) as Promise< typeof import('../../test/fixtureHelpers/createOrg.js') >, @@ -31,12 +38,24 @@ async function importSeedHelpers() { ) as Promise< typeof import('../../graphql/datasources/userKyselyPersistence.js') >, + import(`${TRANSPILED}/test/fixtureHelpers/createRule.js`) as Promise< + typeof import('../../test/fixtureHelpers/createRule.js') + >, + import(`${TRANSPILED}/test/fixtureHelpers/createMrtQueue.js`) as Promise< + typeof import('../../test/fixtureHelpers/createMrtQueue.js') + >, + import(`${TRANSPILED}/queues/itemSubmissionQueue.js`) as Promise< + typeof import('../../queues/itemSubmissionQueue.js') + >, ]); return { createOrg: createOrg.default, hashPassword: ums.hashPassword, UserRole: ums.UserRole, kyselyUserInsert: userPersistence.kyselyUserInsert, + createRule: createRule.default, + createMrtQueue: createMrtQueue.default, + ITEM_SUBMISSION_QUEUE_NAME: itemSubmissionQueue.ITEM_SUBMISSION_QUEUE_NAME, }; } @@ -49,6 +68,8 @@ export type SeededAdmin = { email: string; /** Plaintext password to log in with. */ password: string; + /** API key for the org's ingest endpoint (POST /api/v1/items/async). */ + apiKey: string; }; /** @@ -87,7 +108,144 @@ class Seeder { loginMethods: ['password'], }); - return { orgId: org.org.id, userId: user.id, email, password }; + return { + orgId: org.org.id, + userId: user.id, + email, + password, + apiKey: org.apiKey, + }; + } + + /** + * Create an MRT queue for the org. The first queue created for an org becomes + * the default queue (the destination for ENQUEUE_TO_MRT when no routing rule + * matches), so create exactly one queue before submitting if you rely on the + * default. The admin is assigned as a reviewer so the queue's jobs are + * visible to them via `reviewableQueues`. + */ + async createMrtQueue( + admin: SeededAdmin, + ): Promise<{ id: string; name: string }> { + const { createMrtQueue } = await importSeedHelpers(); + const { queue } = await createMrtQueue({ + orgId: admin.orgId, + mrtService: this.deps.ManualReviewToolService, + userId: admin.userId, + }); + return { id: queue.id, name: queue.name }; + } + + /** + * Create a LIVE content rule scoped to `itemTypeId` with the given + * `conditionSet` and `actionIds`. The conditionSet (what the rule matches) + * is owned by the caller; this factory just persists it via the + * `createRule` fixture helper. + */ + async createRule( + admin: SeededAdmin, + itemTypeId: string, + rule: { + conditionSet: unknown; + actionIds?: readonly string[]; + }, + ): Promise<{ id: string; name: string }> { + const { createRule } = await importSeedHelpers(); + const created = await createRule(this.deps.KyselyPg, admin.orgId, { + actionIds: rule.actionIds ?? [], + contentTypeIds: [itemTypeId], + conditionSet: rule.conditionSet as never, + }); + return { id: created.id, name: created.name }; + } + + /** + * Submit a content item via the real ingest endpoint (POST /api/v1/items/async), + * routed through the same origin the browser uses. The endpoint is async + * (202), so callers should waitForQueueDrained before reading the item. + */ + async submitContentItem( + request: APIRequestContext, + admin: SeededAdmin, + itemTypeId: string, + data: Record, + ): Promise<{ itemId: string }> { + const itemId = uid(); + const res = await request.post('/api/v1/items/async', { + headers: { 'x-api-key': admin.apiKey }, + data: { items: [{ id: itemId, typeId: itemTypeId, data }] }, + }); + if (res.status() !== 202) { + throw new Error( + `submitContentItem expected 202, got ${res.status()}: ${await res.text()}`, + ); + } + return { itemId }; + } + + /** + * Block until the item-submission BullMQ queue has no waiting or active jobs — + * i.e. every submitted item has been fully processed (written to Scylla, + * run through the rule engine, and any MRT jobs enqueued). Call this after + * `submitContentItem` and before navigating to a page that reads the + * processed item, so the read sees the data without the test having to + * poll the UI itself. + */ + async waitForQueueDrained(timeoutMs = 30_000): Promise { + const { ITEM_SUBMISSION_QUEUE_NAME } = await importSeedHelpers(); + const waitKey = `bull:${ITEM_SUBMISSION_QUEUE_NAME}:wait`; + const activeKey = `bull:${ITEM_SUBMISSION_QUEUE_NAME}:active`; + const redis = this.deps.IORedis; + const llen = async (key: string) => Number(await redis.llen(key)); + const deadline = Date.now() + timeoutMs; + while (true) { + if ((await llen(waitKey)) + (await llen(activeKey)) === 0) return; + if (Date.now() >= deadline) { + throw new Error('item-submission queue did not drain in time'); + } + await new Promise((resolve) => setTimeout(resolve, 100)); + } + } + + /** + * Authenticate as `admin` by calling the real `login` GraphQL mutation via + * `page.request`, which shares the page's cookie jar — so the session cookie + * the server sets lands on the browser automatically. Skips the UI login + * form, which other tests don't need to exercise (login.spec.ts does). After + * this, `page.goto('/dashboard/...')` works without re-authenticating. + */ + async login( + page: import('@playwright/test').Page, + admin: SeededAdmin, + ): Promise { + const res = await page.request.post('/api/v1/graphql', { + data: { + query: `mutation Login($input: LoginInput!) { + login(input: $input) { + __typename + ... on LoginSuccessResponse { user { id } } + ... on LoginUserDoesNotExistError { title } + ... on LoginIncorrectPasswordError { title } + } +}`, + variables: { input: { email: admin.email, password: admin.password } }, + }, + }); + if (!res.ok()) { + throw new Error( + `login mutation HTTP ${res.status()}: ${await res.text()}`, + ); + } + const body = (await res.json()) as { + data?: { login: { __typename: string } }; + errors?: unknown; + }; + const typename = body.data?.login.__typename; + if (typename !== 'LoginSuccessResponse') { + throw new Error( + `login failed: expected LoginSuccessResponse, got ${typename ?? 'no data'}`, + ); + } } } @@ -107,7 +265,26 @@ export const test = base.extend({ const { default: getBottle } = await importIocContainer(); const bottle = await getBottle(); const deps = bottle.container as Dependencies; + + // Start the item-processing worker inline so that content submitted via + // POST /api/v1/items/async gets drained from the Redis queue, run + // through the rule engine, and indexed — without a separate worker + // process. + const workerAbort = new AbortController(); + const workerRun = deps.ItemProcessingWorker.run(workerAbort.signal); + workerRun.catch((err) => { + console.error('ItemProcessingWorker exited with error', err); + }); + await use(deps); + + workerAbort.abort(); + try { + await deps.ItemProcessingWorker.shutdown(); + } catch { + // BullMQ's Worker.close() closes the shared ioredis connection, which + // can make closeSharedResourcesForShutdown throw on its own quit(). + } await deps.closeSharedResourcesForShutdown(); }, { scope: 'worker' }, diff --git a/server/e2e/fixtures/media.ts b/server/e2e/fixtures/media.ts new file mode 100644 index 000000000..6b1b02953 --- /dev/null +++ b/server/e2e/fixtures/media.ts @@ -0,0 +1,15 @@ +/** + * Media URLs for e2e tests. The server's item-field validator only accepts + * http(s) URLs (no data URIs), and the audio/video must actually load to be + * played, so we serve tiny fixtures from the client dev server's `public/` dir + * (localhost is allowed via ALLOW_USER_INPUT_LOCALHOST_URIS=true). The image + * reuses the existing client logo. + */ + +const mediaBaseUrl = process.env.PLAYWRIGHT_BASE_URL ?? 'http://localhost:3000'; + +export const IMAGE_URL = `${mediaBaseUrl}/logo192.png`; + +export const AUDIO_URL = `${mediaBaseUrl}/e2e/tone-3s.wav`; + +export const VIDEO_URL = `${mediaBaseUrl}/e2e/test-video-2s.mp4`; diff --git a/server/e2e/playwright.config.ts b/server/e2e/playwright.config.ts index be509c5e6..8ba67d868 100644 --- a/server/e2e/playwright.config.ts +++ b/server/e2e/playwright.config.ts @@ -10,9 +10,12 @@ export default defineConfig({ // Configured paths resolve relative to this config file's dir (server/e2e/), // so these land at server/e2e/{test-results,playwright-report}. outputDir: 'test-results', - // Run every test concurrently. This helps ensure that we do - // not get implicit dependencies between tests. - fullyParallel: true, + // Run the suite serially. The tests share a single in-process BullMQ worker + // (the e2e `deps` fixture) and a content-type fixture path that races a + // global `REFRESH MATERIALIZED VIEW` trigger under concurrent inserts — so + // parallel `createContentType` calls intermittently return undefined. + fullyParallel: false, + workers: 1, // Fail the build on CI if test.only was left in the source. forbidOnly: Boolean(process.env.CI), // Retry flaky flows on CI; fail fast locally. diff --git a/server/e2e/tests/investigation.spec.ts b/server/e2e/tests/investigation.spec.ts new file mode 100644 index 000000000..465ff3444 --- /dev/null +++ b/server/e2e/tests/investigation.spec.ts @@ -0,0 +1,38 @@ +import { ScalarTypes, type Field } from '@roostorg/coop-types'; +import { uid } from 'uid'; + +import { expect, test } from '../fixtures/coop.js'; + +test('a submitted item can be found in the investigation tool', async ({ + page, + request, + deps, + seed, +}) => { + const admin = await seed.orgWithAdmin(); + const itemType = await deps.ModerationConfigService.createContentType( + admin.orgId, + { + name: `type-${uid()}`, + schema: [ + { + name: 'text', + type: ScalarTypes.STRING, + required: true, + container: null, + }, + ] as [Field, ...Field[]], + schemaFieldRoles: {}, + }, + ); + const { itemId } = await seed.submitContentItem(request, admin, itemType.id, { + text: 'hello from e2e', + }); + await seed.waitForQueueDrained(); + + await seed.login(page, admin); + await page.goto('/dashboard/manual_review/investigation'); + await page.getByPlaceholder('Enter an item ID').fill(itemId); + await page.getByRole('button', { name: 'Search' }).click(); + await expect(page.getByText(itemType.name).first()).toBeVisible(); +}); diff --git a/server/e2e/tests/item-signals.spec.ts b/server/e2e/tests/item-signals.spec.ts new file mode 100644 index 000000000..624558fe8 --- /dev/null +++ b/server/e2e/tests/item-signals.spec.ts @@ -0,0 +1,60 @@ +import { ScalarTypes, type Field } from '@roostorg/coop-types'; +import { uid } from 'uid'; + +import { expect, jsonStringify, test } from '../fixtures/coop.js'; + +test('an item shows its rule execution / signal results in the investigation tool', async ({ + page, + request, + deps, + seed, +}) => { + const admin = await seed.orgWithAdmin(); + const itemType = await deps.ModerationConfigService.createContentType( + admin.orgId, + { + name: `type-${uid()}`, + schema: [ + { + name: 'text', + type: ScalarTypes.STRING, + required: true, + container: null, + }, + ] as [Field, ...Field[]], + schemaFieldRoles: {}, + }, + ); + const rule = await seed.createRule(admin, itemType.id, { + conditionSet: { + conjunction: 'AND', + conditions: [ + { + input: { + type: 'CONTENT_FIELD', + name: 'text', + contentTypeId: itemType.id, + }, + signal: { + id: jsonStringify({ type: 'TEXT_MATCHING_CONTAINS_TEXT' }), + type: 'TEXT_MATCHING_CONTAINS_TEXT', + }, + matchingValues: { strings: ['test'] }, + }, + ], + }, + }); + const { itemId } = await seed.submitContentItem(request, admin, itemType.id, { + text: 'this is a test', + }); + await seed.waitForQueueDrained(); + + await seed.login(page, admin); + await page.goto('/dashboard/manual_review/investigation'); + await page.getByPlaceholder('Enter an item ID').fill(itemId); + await page.getByRole('button', { name: 'Search' }).click(); + await expect(page.getByText(rule.name).first()).toBeVisible(); + await expect( + page.getByText('Matched', { exact: true }).first(), + ).toBeVisible(); +}); diff --git a/server/e2e/tests/item-type-creation.spec.ts b/server/e2e/tests/item-type-creation.spec.ts new file mode 100644 index 000000000..67a4dd746 --- /dev/null +++ b/server/e2e/tests/item-type-creation.spec.ts @@ -0,0 +1,48 @@ +import { uid } from 'uid'; + +import { expect, test } from '../fixtures/coop.js'; + +test('an admin creates an item type with mixed field types via the UI', async ({ + page, + seed, +}) => { + const admin = await seed.orgWithAdmin(); + await seed.login(page, admin); + + await page.goto('/dashboard/settings/item_types/form?kind=CONTENT'); + await expect(page.getByText('Create Item Type')).toBeVisible(); + + const typeName = `e2e-type-${uid()}`; + await page.locator('input[placeholder="Name"]').fill(typeName); + + const fields = [ + { name: 'text', type: 'String' }, + { name: 'image', type: 'Image' }, + { name: 'audio', type: 'Audio' }, + ]; + for (let i = 0; i < fields.length; i++) { + if (i > 0) { + await page.getByRole('button', { name: 'Add Field' }).click(); + } + await page + .locator('input[placeholder="Field Name"]') + .nth(i) + .fill(fields[i].name); + if (fields[i].type !== 'String') { + await page + .locator('.ant-select') + .nth(2 + i * 2) + .click(); + await page + .locator( + `.ant-select-dropdown:not(.ant-select-dropdown-hidden) .ant-select-item-option[title="${fields[i].type}"]`, + ) + .last() + .click(); + } + } + + await page.getByRole('button', { name: 'Create Content Type' }).click(); + await page.goto('/dashboard/settings/item_types'); + await expect(page.getByText(typeName)).toBeVisible(); +}); diff --git a/server/e2e/tests/login.spec.ts b/server/e2e/tests/login.spec.ts index acc5b9f2c..5373ee75c 100644 --- a/server/e2e/tests/login.spec.ts +++ b/server/e2e/tests/login.spec.ts @@ -1,6 +1,9 @@ import { expect, test } from '../fixtures/coop.js'; -test('a user can log in', async ({ page, seed }) => { +test('a user can log in and their session persists until logout', async ({ + page, + seed, +}) => { const admin = await seed.orgWithAdmin(); await page.goto('/login'); @@ -9,4 +12,17 @@ test('a user can log in', async ({ page, seed }) => { await page.getByRole('button', { name: 'Sign In' }).click(); await expect(page).toHaveURL(/\/dashboard/); + await page.goto('/dashboard/overview'); + await expect(page).toHaveURL(/\/dashboard\/overview/); + + await page.reload(); + await expect(page).toHaveURL(/\/dashboard\/overview/); + + const res = await page.request.post('/api/v1/graphql', { + data: { query: 'mutation { logout }' }, + }); + expect(res.ok()).toBeTruthy(); + + await page.goto('/dashboard/overview'); + await expect(page).toHaveURL(/\/login/); }); diff --git a/server/e2e/tests/mrt-job-review.spec.ts b/server/e2e/tests/mrt-job-review.spec.ts new file mode 100644 index 000000000..1433fbde7 --- /dev/null +++ b/server/e2e/tests/mrt-job-review.spec.ts @@ -0,0 +1,121 @@ +import { ScalarTypes, type Field } from '@roostorg/coop-types'; +import { uid } from 'uid'; + +import { expect, jsonStringify, test } from '../fixtures/coop.js'; +import { AUDIO_URL, IMAGE_URL, VIDEO_URL } from '../fixtures/media.js'; + +const FIELDS: Field[] = [ + { name: 'text', type: ScalarTypes.STRING, required: true, container: null }, + { name: 'image', type: ScalarTypes.IMAGE, required: false, container: null }, + { name: 'audio', type: ScalarTypes.AUDIO, required: false, container: null }, + { name: 'video', type: ScalarTypes.VIDEO, required: false, container: null }, +]; + +test('an MRT job renders text/image/audio/video, plays media, and records a decision', async ({ + page, + request, + deps, + seed, +}) => { + const admin = await seed.orgWithAdmin(); + const itemType = await deps.ModerationConfigService.createContentType( + admin.orgId, + { + name: `type-${uid()}`, + schema: FIELDS as [Field, ...Field[]], + schemaFieldRoles: {}, + }, + ); + const queue = await seed.createMrtQueue(admin); + const actions = await deps.ModerationConfigService.getActions({ + orgId: admin.orgId, + }); + const enqueueToMrt = actions.find((a) => a.actionType === 'ENQUEUE_TO_MRT'); + if (enqueueToMrt == null) { + throw new Error('ENQUEUE_TO_MRT built-in action not found for org'); + } + await seed.createRule(admin, itemType.id, { + actionIds: [enqueueToMrt.id], + conditionSet: { + conjunction: 'AND', + conditions: [ + { + input: { + type: 'CONTENT_FIELD', + name: 'text', + contentTypeId: itemType.id, + }, + signal: { + id: jsonStringify({ type: 'TEXT_MATCHING_CONTAINS_TEXT' }), + type: 'TEXT_MATCHING_CONTAINS_TEXT', + }, + matchingValues: { strings: ['test'] }, + }, + ], + }, + }); + + const uniqueText = `test media ${uid()}`; + await seed.submitContentItem(request, admin, itemType.id, { + text: uniqueText, + image: IMAGE_URL, + audio: AUDIO_URL, + video: VIDEO_URL, + }); + await seed.waitForQueueDrained(); + + await seed.login(page, admin); + await page.goto(`/dashboard/manual_review/queues/review/${queue.id}`); + await expect(page).toHaveURL(/\/review\/[^/]+\/[^/]+\/[^/]+/); + const jobId = new URL(page.url()).pathname.split('/').at(-2)!; + + await expect(page.getByText('Text', { exact: true })).toBeVisible(); + await expect(page.getByText('Image', { exact: true })).toBeVisible(); + await expect(page.getByText('Audio', { exact: true })).toBeVisible(); + await expect(page.getByText('Video', { exact: true })).toBeVisible(); + + const audio = page.locator('audio').first(); + await expect(audio).toBeVisible(); + await audio.evaluate(async (el: HTMLAudioElement) => { + // eslint-disable-next-line functional/immutable-data -- DOM elements are mutable by nature. + el.muted = true; + await el.play(); + }); + + const video = page.locator('video').first(); + await expect(video).toBeVisible(); + await video.evaluate(async (el: HTMLVideoElement) => { + // eslint-disable-next-line functional/immutable-data -- DOM elements are mutable by nature. + el.muted = true; + await el.play(); + }); + await expect + .poll(async () => video.evaluate((el: HTMLVideoElement) => el.currentTime)) + .toBeGreaterThan(0); + + const submitResponse = page.waitForResponse( + (resp) => + resp.url().includes('/api/v1/graphql') && + resp.request().postData()?.includes('submitManualReviewDecision') === + true, + ); + await page + .getByTestId('manual-review-decision-action-list') + .getByText('Ignore', { exact: true }) + .click(); + await page.getByRole('button', { name: 'Submit' }).click(); + await submitResponse; + + const res = await page.request.post('/api/v1/graphql', { + data: { + query: `query GetDecidedJobFromJobId($id: String!) { + getDecidedJobFromJobId(id: $id) { decision { id } } +}`, + variables: { id: jobId }, + }, + }); + const body = (await res.json()) as { + data?: { getDecidedJobFromJobId?: { decision?: { id: string } } }; + }; + expect(body.data?.getDecidedJobFromJobId?.decision).not.toBeNull(); +}); diff --git a/server/e2e/tests/rule-creation.spec.ts b/server/e2e/tests/rule-creation.spec.ts new file mode 100644 index 000000000..da746f617 --- /dev/null +++ b/server/e2e/tests/rule-creation.spec.ts @@ -0,0 +1,72 @@ +import { ScalarTypes, type Field } from '@roostorg/coop-types'; +import { uid } from 'uid'; + +import { expect, test } from '../fixtures/coop.js'; + +test('an admin creates a content rule with a condition and an MRT action via the UI', async ({ + page, + deps, + seed, +}) => { + const admin = await seed.orgWithAdmin(); + const itemType = await deps.ModerationConfigService.createContentType( + admin.orgId, + { + name: `e2e-type-${uid()}`, + schema: [ + { + name: 'text', + type: ScalarTypes.STRING, + required: true, + container: null, + }, + ] as [Field, ...Field[]], + schemaFieldRoles: {}, + }, + ); + + await seed.login(page, admin); + await page.goto('/dashboard/rules/proactive/form'); + await expect(page.getByText('Create Rule').first()).toBeVisible(); + + const ruleName = `e2e-rule-${uid()}`; + await page.locator('input').first().fill(ruleName); + await page.locator('.ant-select').first().click(); + await page + .locator( + `.ant-select-dropdown:not(.ant-select-dropdown-hidden) .ant-select-item-option[title="${itemType.name}"]`, + ) + .click(); + await page.getByRole('button', { name: 'Continue' }).click(); + + await page.locator('.ant-select').nth(1).click(); + await page + .locator( + '.ant-select-dropdown:not(.ant-select-dropdown-hidden) .ant-select-item-option', + ) + .filter({ hasText: /^text$/ }) + .last() + .click(); + await page.getByRole('button', { name: 'Select Signal' }).click(); + await page.getByPlaceholder('Search').fill('Contains text'); + await page.getByText('Contains text', { exact: true }).click(); + await page.getByPlaceholder('Input Strings').fill('test'); + await page.getByPlaceholder('Input Strings').press('Enter'); + await page.getByRole('button', { name: 'Continue' }).click(); + + await page.locator('.ant-select').nth(2).click(); + await page + .locator( + '.ant-select-dropdown:not(.ant-select-dropdown-hidden) .ant-select-item-option', + ) + .filter({ hasText: 'Enqueue Item to Manual Review' }) + .last() + .click(); + await page.getByText('Live', { exact: true }).click(); + await page.getByRole('button', { name: 'Create Rule' }).click(); + + await expect(page.getByText('Rule Created')).toBeVisible(); + await page.getByRole('button', { name: 'OK' }).click(); + await expect(page).toHaveURL(/\/dashboard\/rules\/proactive/); + await expect(page.getByText(ruleName)).toBeVisible(); +}); diff --git a/server/e2e/tests/rule-routing.spec.ts b/server/e2e/tests/rule-routing.spec.ts new file mode 100644 index 000000000..e21e68e8f --- /dev/null +++ b/server/e2e/tests/rule-routing.spec.ts @@ -0,0 +1,67 @@ +import { ScalarTypes, type Field } from '@roostorg/coop-types'; +import { uid } from 'uid'; + +import { expect, jsonStringify, test } from '../fixtures/coop.js'; + +test('a rule routes a submitted item into a manual review queue', async ({ + page, + request, + deps, + seed, +}) => { + const admin = await seed.orgWithAdmin(); + const itemType = await deps.ModerationConfigService.createContentType( + admin.orgId, + { + name: `type-${uid()}`, + schema: [ + { + name: 'text', + type: ScalarTypes.STRING, + required: true, + container: null, + }, + ] as [Field, ...Field[]], + schemaFieldRoles: {}, + }, + ); + const queue = await seed.createMrtQueue(admin); + const actions = await deps.ModerationConfigService.getActions({ + orgId: admin.orgId, + }); + const enqueueToMrt = actions.find((a) => a.actionType === 'ENQUEUE_TO_MRT'); + if (enqueueToMrt == null) { + throw new Error('ENQUEUE_TO_MRT built-in action not found for org'); + } + await seed.createRule(admin, itemType.id, { + actionIds: [enqueueToMrt.id], + conditionSet: { + conjunction: 'AND', + conditions: [ + { + input: { + type: 'CONTENT_FIELD', + name: 'text', + contentTypeId: itemType.id, + }, + signal: { + id: jsonStringify({ type: 'TEXT_MATCHING_CONTAINS_TEXT' }), + type: 'TEXT_MATCHING_CONTAINS_TEXT', + }, + matchingValues: { strings: ['test'] }, + }, + ], + }, + }); + + const uniqueText = `test-${uid()}`; + await seed.submitContentItem(request, admin, itemType.id, { + text: uniqueText, + }); + await seed.waitForQueueDrained(); + + await seed.login(page, admin); + await page.goto(`/dashboard/manual_review/queues/jobs/${queue.id}`); + await expect(page.getByText(`Jobs in ${queue.name}`)).toBeVisible(); + await expect(page.getByText(uniqueText)).toBeVisible(); +}); diff --git a/server/graphql/datasources/OrgApi.ts b/server/graphql/datasources/OrgApi.ts index ce9a1876c..d4e0bdb07 100644 --- a/server/graphql/datasources/OrgApi.ts +++ b/server/graphql/datasources/OrgApi.ts @@ -195,8 +195,7 @@ class OrgAPI { } export type OrgErrorType = - | 'InviteUserTokenExpiredError' - | 'InviteUserTokenMissingError'; + 'InviteUserTokenExpiredError' | 'InviteUserTokenMissingError'; function orgValidationFailureToBadRequestError(failure: OrgValidationFailure) { return makeBadRequestError(failure.message, { diff --git a/server/graphql/datasources/RuleApi.ts b/server/graphql/datasources/RuleApi.ts index a80cf9587..a80e88b56 100644 --- a/server/graphql/datasources/RuleApi.ts +++ b/server/graphql/datasources/RuleApi.ts @@ -380,9 +380,7 @@ class RuleAPI { orgId: string, actionIds: readonly string[], actionParameters: - | readonly { actionId: string; parameters: unknown }[] - | null - | undefined, + readonly { actionId: string; parameters: unknown }[] | null | undefined, ): Promise | undefined> { if (actionIds.length === 0) { return undefined; @@ -990,8 +988,7 @@ function conditionInputIsValid( } type ValidatedGQLConditionInput = - | ValidatedGQLConditionSetInput - | ValidatedGQLLeafConditionInput; + ValidatedGQLConditionSetInput | ValidatedGQLLeafConditionInput; type ValidatedGQLConditionSetInput = RequiredWithoutNull< Pick diff --git a/server/graphql/datasources/UserApi.ts b/server/graphql/datasources/UserApi.ts index 3bd00b739..80b5cf2b6 100644 --- a/server/graphql/datasources/UserApi.ts +++ b/server/graphql/datasources/UserApi.ts @@ -159,15 +159,13 @@ class UserAPI { token: inviteUserToken, }); } - if ( - !( - token != null && - token.email === email && - token.orgId === orgId && - token.role === role && - Date.now() - new Date(token.createdAt).getTime() < 2 * WEEK_MS - ) - ) { + if (!( + token != null && + token.email === email && + token.orgId === orgId && + token.role === role && + Date.now() - new Date(token.createdAt).getTime() < 2 * WEEK_MS + )) { throw makeUnauthorizedError('Invalid invite token', { shouldErrorSpan: true, }); diff --git a/server/graphql/datasources/buildGraphqlRuleParent.ts b/server/graphql/datasources/buildGraphqlRuleParent.ts index 6107dc90c..844510f95 100644 --- a/server/graphql/datasources/buildGraphqlRuleParent.ts +++ b/server/graphql/datasources/buildGraphqlRuleParent.ts @@ -26,8 +26,7 @@ export function buildGraphqlRuleParent( // getActions and getActionParameters resolve from the same joined read, so // share one lazy promise to avoid querying the rule's actions twice. let actionsWithParameters: - | ReturnType - | undefined; + ReturnType | undefined; const getActionsWithParameters = async () => { actionsWithParameters ??= deps.moderationConfigService.getActionsForRuleId({ orgId: plain.orgId, diff --git a/server/graphql/datasources/orgValidation.ts b/server/graphql/datasources/orgValidation.ts index 8e1cd036f..2b3d6140a 100644 --- a/server/graphql/datasources/orgValidation.ts +++ b/server/graphql/datasources/orgValidation.ts @@ -31,8 +31,7 @@ export type OrgValidationFailure = { }; export type OrgValidationResult = - | { ok: true } - | { ok: false; failure: OrgValidationFailure }; + { ok: true } | { ok: false; failure: OrgValidationFailure }; function isEmailShape(value: string): boolean { return validator.isEmail(value); diff --git a/server/graphql/datasources/userKyselyPersistenceFindByEmailAndOrg.test.ts b/server/graphql/datasources/userKyselyPersistenceFindByEmailAndOrg.test.ts index 7fdcc690f..be771952c 100644 --- a/server/graphql/datasources/userKyselyPersistenceFindByEmailAndOrg.test.ts +++ b/server/graphql/datasources/userKyselyPersistenceFindByEmailAndOrg.test.ts @@ -3,10 +3,8 @@ import { uid } from 'uid'; import { UserRole } from '../../services/userManagementService/index.js'; import createOrg from '../../test/fixtureHelpers/createOrg.js'; -import { makeMockedServer } from '../../test/setupMockedServer.js'; -import { makeTestWithFixture } from '../../test/utils.js'; +import { makeTransactionalTestWithFixture } from '../../test/harness/transactionalTest.js'; import { - kyselyUserDeleteById, kyselyUserFindByEmailAndOrg, kyselyUserInsert, } from './userKyselyPersistence.js'; @@ -25,9 +23,8 @@ function samlUserInput(orgId: string) { } describe('kyselyUserFindByEmailAndOrg', () => { - const testWithFixture = makeTestWithFixture(async () => { - const { deps, shutdown } = await makeMockedServer(); - const { org, cleanup: orgCleanup } = await createOrg( + const testWithFixture = makeTransactionalTestWithFixture(async ({ deps }) => { + const { org } = await createOrg( { KyselyPg: deps.KyselyPg, ModerationConfigService: deps.ModerationConfigService, @@ -35,14 +32,7 @@ describe('kyselyUserFindByEmailAndOrg', () => { }, uid(), ); - return { - deps, - org, - async cleanup() { - await orgCleanup(); - await shutdown(); - }, - }; + return { org }; }); testWithFixture( @@ -50,15 +40,11 @@ describe('kyselyUserFindByEmailAndOrg', () => { async ({ deps, org }) => { const input = samlUserInput(org.id); await kyselyUserInsert({ db: deps.KyselyPg, ...input }); - try { - const result = await kyselyUserFindByEmailAndOrg(deps.KyselyPg, { - email: input.email, - orgId: org.id, - }); - expect(result).toMatchObject({ id: input.id, orgId: org.id }); - } finally { - await kyselyUserDeleteById(deps.KyselyPg, input.id); - } + const result = await kyselyUserFindByEmailAndOrg(deps.KyselyPg, { + email: input.email, + orgId: org.id, + }); + expect(result).toMatchObject({ id: input.id, orgId: org.id }); }, ); @@ -69,15 +55,11 @@ describe('kyselyUserFindByEmailAndOrg', () => { async ({ deps, org }) => { const input = samlUserInput(org.id); await kyselyUserInsert({ db: deps.KyselyPg, ...input }); - try { - const result = await kyselyUserFindByEmailAndOrg(deps.KyselyPg, { - email: input.email, - orgId: `different-org-${uid()}`, - }); - expect(result).toBeUndefined(); - } finally { - await kyselyUserDeleteById(deps.KyselyPg, input.id); - } + const result = await kyselyUserFindByEmailAndOrg(deps.KyselyPg, { + email: input.email, + orgId: `different-org-${uid()}`, + }); + expect(result).toBeUndefined(); }, ); diff --git a/server/graphql/datasources/userValidation.ts b/server/graphql/datasources/userValidation.ts index 92cc2fd4d..71493034e 100644 --- a/server/graphql/datasources/userValidation.ts +++ b/server/graphql/datasources/userValidation.ts @@ -26,18 +26,12 @@ const validator = createRequire(import.meta.url)('validator') as ValidatorLib; export type UserValidationFailure = { /** Kept stable for GraphQL JSON pointers. */ field: - | 'email' - | 'firstName' - | 'lastName' - | 'role' - | 'loginMethods' - | 'password'; + 'email' | 'firstName' | 'lastName' | 'role' | 'loginMethods' | 'password'; message: string; }; export type UserValidationResult = - | { ok: true } - | { ok: false; failure: UserValidationFailure }; + { ok: true } | { ok: false; failure: UserValidationFailure }; function isEmailShape(value: string): boolean { return validator.isEmail(value); diff --git a/server/graphql/generated.ts b/server/graphql/generated.ts index 1749e1ea9..772f35add 100644 --- a/server/graphql/generated.ts +++ b/server/graphql/generated.ts @@ -299,8 +299,7 @@ export type GQLAddFavoriteRuleSuccessResponse = { }; export type GQLAddManualReviewJobCommentResponse = - | GQLAddManualReviewJobCommentSuccessResponse - | GQLNotFoundError; + GQLAddManualReviewJobCommentSuccessResponse | GQLNotFoundError; export type GQLAddManualReviewJobCommentSuccessResponse = { readonly __typename?: 'AddManualReviewJobCommentSuccessResponse'; @@ -484,8 +483,7 @@ export type GQLChangePasswordInput = { }; export type GQLChangePasswordResponse = - | GQLChangePasswordError - | GQLChangePasswordSuccessResponse; + GQLChangePasswordError | GQLChangePasswordSuccessResponse; export type GQLChangePasswordSuccessResponse = { readonly __typename?: 'ChangePasswordSuccessResponse'; @@ -594,8 +592,7 @@ export type GQLConditionSetWithResult = { }; export type GQLConditionWithResult = - | GQLConditionSetWithResult - | GQLLeafConditionWithResult; + GQLConditionSetWithResult | GQLLeafConditionWithResult; export type GQLContainer = { readonly __typename?: 'Container'; @@ -831,8 +828,7 @@ export type GQLCreateContentRuleInput = { }; export type GQLCreateContentRuleResponse = - | GQLMutateContentRuleSuccessResponse - | GQLRuleNameExistsError; + GQLMutateContentRuleSuccessResponse | GQLRuleNameExistsError; export type GQLCreateHashBankInput = { readonly description?: InputMaybe; @@ -882,8 +878,7 @@ export type GQLCreateReportingRuleInput = { }; export type GQLCreateReportingRuleResponse = - | GQLMutateReportingRuleSuccessResponse - | GQLReportingRuleNameExistsError; + GQLMutateReportingRuleSuccessResponse | GQLReportingRuleNameExistsError; export type GQLCreateRoutingRuleInput = { readonly conditionSet: GQLConditionSetInput; @@ -941,8 +936,7 @@ export type GQLCreateUserRuleInput = { }; export type GQLCreateUserRuleResponse = - | GQLMutateUserRuleSuccessResponse - | GQLRuleNameExistsError; + GQLMutateUserRuleSuccessResponse | GQLRuleNameExistsError; export type GQLCustomAction = GQLActionBase & { readonly __typename?: 'CustomAction'; @@ -1060,8 +1054,7 @@ export const GQLDecisionsCountGroupBy = { export type GQLDecisionsCountGroupBy = (typeof GQLDecisionsCountGroupBy)[keyof typeof GQLDecisionsCountGroupBy]; export type GQLDeleteAllJobsFromQueueResponse = - | GQLDeleteAllJobsFromQueueSuccessResponse - | GQLDeleteAllJobsUnauthorizedError; + GQLDeleteAllJobsFromQueueSuccessResponse | GQLDeleteAllJobsUnauthorizedError; export type GQLDeleteAllJobsFromQueueSuccessResponse = { readonly __typename?: 'DeleteAllJobsFromQueueSuccessResponse'; @@ -1079,8 +1072,7 @@ export type GQLDeleteAllJobsUnauthorizedError = GQLError & { }; export type GQLDeleteItemTypeResponse = - | GQLCannotDeleteDefaultUserError - | GQLDeleteItemTypeSuccessResponse; + GQLCannotDeleteDefaultUserError | GQLDeleteItemTypeSuccessResponse; export type GQLDeleteItemTypeSuccessResponse = { readonly __typename?: 'DeleteItemTypeSuccessResponse'; @@ -1394,8 +1386,7 @@ export type GQLGetDecisionCountsTableInput = { }; export type GQLGetFullReportingRuleResultForItemResponse = - | GQLNotFoundError - | GQLReportingRuleExecutionResult; + GQLNotFoundError | GQLReportingRuleExecutionResult; export type GQLGetFullResultForItemInput = { readonly date?: InputMaybe; @@ -1405,8 +1396,7 @@ export type GQLGetFullResultForItemInput = { }; export type GQLGetFullResultForItemResponse = - | GQLNotFoundError - | GQLRuleExecutionResult; + GQLNotFoundError | GQLRuleExecutionResult; export type GQLGetJobCreationCountInput = { readonly filterBy: GQLJobCreationFilterByInput; @@ -1698,9 +1688,7 @@ export type GQLItemSubmissions = { }; export type GQLItemType = - | GQLContentItemType - | GQLThreadItemType - | GQLUserItemType; + GQLContentItemType | GQLThreadItemType | GQLUserItemType; export type GQLItemTypeBase = { readonly baseFields: ReadonlyArray; @@ -2213,8 +2201,7 @@ export const GQLManualReviewChartMetric = { export type GQLManualReviewChartMetric = (typeof GQLManualReviewChartMetric)[keyof typeof GQLManualReviewChartMetric]; export type GQLManualReviewChartSettings = - | GQLGetDecisionCountSettings - | GQLGetJobCreationCountSettings; + GQLGetDecisionCountSettings | GQLGetJobCreationCountSettings; export type GQLManualReviewChartSettingsInput = { readonly decisionCountSettings?: InputMaybe; @@ -2442,6 +2429,7 @@ export type GQLModeratorSafetySettingsInput = { readonly moderatorSafetyBlurLevel: Scalars['Int']['input']; readonly moderatorSafetyGrayscale: Scalars['Boolean']['input']; readonly moderatorSafetyMuteVideo: Scalars['Boolean']['input']; + readonly moderatorSafetySepia: Scalars['Boolean']['input']; }; export const GQLMrtClearReportsDisposition = { @@ -2476,8 +2464,7 @@ export const GQLMutateActionError = { export type GQLMutateActionError = (typeof GQLMutateActionError)[keyof typeof GQLMutateActionError]; export type GQLMutateActionResponse = - | GQLActionNameExistsError - | GQLMutateActionSuccessResponse; + GQLActionNameExistsError | GQLMutateActionSuccessResponse; export type GQLMutateActionSuccessResponse = { readonly __typename?: 'MutateActionSuccessResponse'; @@ -2491,8 +2478,7 @@ export type GQLMutateBankResponse = { }; export type GQLMutateContentItemTypeResponse = - | GQLItemTypeNameAlreadyExistsError - | GQLMutateContentTypeSuccessResponse; + GQLItemTypeNameAlreadyExistsError | GQLMutateContentTypeSuccessResponse; export type GQLMutateContentRuleSuccessResponse = { readonly __typename?: 'MutateContentRuleSuccessResponse'; @@ -2505,8 +2491,7 @@ export type GQLMutateContentTypeSuccessResponse = { }; export type GQLMutateHashBankResponse = - | GQLMatchingBankNameExistsError - | GQLMutateHashBankSuccessResponse; + GQLMatchingBankNameExistsError | GQLMutateHashBankSuccessResponse; export type GQLMutateHashBankSuccessResponse = { readonly __typename?: 'MutateHashBankSuccessResponse'; @@ -2515,8 +2500,7 @@ export type GQLMutateHashBankSuccessResponse = { }; export type GQLMutateLocationBankResponse = - | GQLLocationBankNameExistsError - | GQLMutateLocationBankSuccessResponse; + GQLLocationBankNameExistsError | GQLMutateLocationBankSuccessResponse; export type GQLMutateLocationBankSuccessResponse = { readonly __typename?: 'MutateLocationBankSuccessResponse'; @@ -2544,8 +2528,7 @@ export type GQLMutateRoutingRulesOrderSuccessResponse = { }; export type GQLMutateThreadItemTypeResponse = - | GQLItemTypeNameAlreadyExistsError - | GQLMutateThreadTypeSuccessResponse; + GQLItemTypeNameAlreadyExistsError | GQLMutateThreadTypeSuccessResponse; export type GQLMutateThreadTypeSuccessResponse = { readonly __typename?: 'MutateThreadTypeSuccessResponse'; @@ -2553,8 +2536,7 @@ export type GQLMutateThreadTypeSuccessResponse = { }; export type GQLMutateUserItemTypeResponse = - | GQLItemTypeNameAlreadyExistsError - | GQLMutateUserTypeSuccessResponse; + GQLItemTypeNameAlreadyExistsError | GQLMutateUserTypeSuccessResponse; export type GQLMutateUserRuleSuccessResponse = { readonly __typename?: 'MutateUserRuleSuccessResponse'; @@ -3961,8 +3943,7 @@ export type GQLRemoveAccessibleQueuesToUserInput = { }; export type GQLRemoveAccessibleQueuesToUserResponse = - | GQLMutateAccessibleQueuesForUserSuccessResponse - | GQLNotFoundError; + GQLMutateAccessibleQueuesForUserSuccessResponse | GQLNotFoundError; export type GQLRemoveFavoriteMrtQueueSuccessResponse = { readonly __typename?: 'RemoveFavoriteMRTQueueSuccessResponse'; @@ -4155,8 +4136,7 @@ export type GQLRotateApiKeyInput = { }; export type GQLRotateApiKeyResponse = - | GQLRotateApiKeyError - | GQLRotateApiKeySuccessResponse; + GQLRotateApiKeyError | GQLRotateApiKeySuccessResponse; export type GQLRotateApiKeySuccessResponse = { readonly __typename?: 'RotateApiKeySuccessResponse'; @@ -4175,8 +4155,7 @@ export type GQLRotateWebhookSigningKeyError = GQLError & { }; export type GQLRotateWebhookSigningKeyResponse = - | GQLRotateWebhookSigningKeyError - | GQLRotateWebhookSigningKeySuccessResponse; + GQLRotateWebhookSigningKeyError | GQLRotateWebhookSigningKeySuccessResponse; export type GQLRotateWebhookSigningKeySuccessResponse = { readonly __typename?: 'RotateWebhookSigningKeySuccessResponse'; @@ -4459,8 +4438,7 @@ export type GQLSignUpInput = { }; export type GQLSignUpResponse = - | GQLSignUpSuccessResponse - | GQLSignUpUserExistsError; + GQLSignUpSuccessResponse | GQLSignUpUserExistsError; export type GQLSignUpSuccessResponse = { readonly __typename?: 'SignUpSuccessResponse'; @@ -4538,8 +4516,7 @@ export const GQLSignalInputType = { export type GQLSignalInputType = (typeof GQLSignalInputType)[keyof typeof GQLSignalInputType]; export type GQLSignalOutputType = - | GQLEnumSignalOutputType - | GQLScalarSignalOutputType; + GQLEnumSignalOutputType | GQLScalarSignalOutputType; export type GQLSignalPricingStructure = { readonly __typename?: 'SignalPricingStructure'; @@ -4587,6 +4564,12 @@ export const GQLSignalType = { OpenAiHateTextModel: 'OPEN_AI_HATE_TEXT_MODEL', OpenAiHateThreateningTextModel: 'OPEN_AI_HATE_THREATENING_TEXT_MODEL', OpenAiSelfHarmImageModel: 'OPEN_AI_SELF_HARM_IMAGE_MODEL', + OpenAiSelfHarmInstructionsImageModel: + 'OPEN_AI_SELF_HARM_INSTRUCTIONS_IMAGE_MODEL', + OpenAiSelfHarmInstructionsTextModel: + 'OPEN_AI_SELF_HARM_INSTRUCTIONS_TEXT_MODEL', + OpenAiSelfHarmIntentImageModel: 'OPEN_AI_SELF_HARM_INTENT_IMAGE_MODEL', + OpenAiSelfHarmIntentTextModel: 'OPEN_AI_SELF_HARM_INTENT_TEXT_MODEL', OpenAiSelfHarmTextModel: 'OPEN_AI_SELF_HARM_TEXT_MODEL', OpenAiSexualImageModel: 'OPEN_AI_SEXUAL_IMAGE_MODEL', OpenAiSexualMinorsTextModel: 'OPEN_AI_SEXUAL_MINORS_TEXT_MODEL', @@ -5161,6 +5144,7 @@ export type GQLUserInterfacePreferences = { readonly moderatorSafetyBlurLevel: Scalars['Int']['output']; readonly moderatorSafetyGrayscale: Scalars['Boolean']['output']; readonly moderatorSafetyMuteVideo: Scalars['Boolean']['output']; + readonly moderatorSafetySepia: Scalars['Boolean']['output']; readonly mrtChartConfigurations: ReadonlyArray; }; @@ -5518,8 +5502,7 @@ export type GQLResolversUnionTypes<_RefType extends Record> = { }) | GQLNotFoundError; ChangePasswordResponse: - | GQLChangePasswordError - | GQLChangePasswordSuccessResponse; + GQLChangePasswordError | GQLChangePasswordSuccessResponse; Condition: ConditionSet | LeafCondition; ConditionWithResult: ConditionSetWithResult | LeafConditionWithResult; CreateContentRuleResponse: @@ -5552,8 +5535,7 @@ export type GQLResolversUnionTypes<_RefType extends Record> = { | GQLDeleteAllJobsFromQueueSuccessResponse | GQLDeleteAllJobsUnauthorizedError; DeleteItemTypeResponse: - | GQLCannotDeleteDefaultUserError - | GQLDeleteItemTypeSuccessResponse; + GQLCannotDeleteDefaultUserError | GQLDeleteItemTypeSuccessResponse; DequeueManualReviewJobResponse: Omit< GQLDequeueManualReviewJobSuccessResponse, 'job' @@ -5608,8 +5590,7 @@ export type GQLResolversUnionTypes<_RefType extends Record> = { | (Omit & { user: _RefType['User'] }) | GQLLoginUserDoesNotExistError; ManualReviewChartSettings: - | GQLGetDecisionCountSettings - | GQLGetJobCreationCountSettings; + GQLGetDecisionCountSettings | GQLGetJobCreationCountSettings; ManualReviewDecisionComponent: | GQLAcceptAppealDecisionComponent | GQLAutomaticCloseDecisionComponent @@ -5672,8 +5653,7 @@ export type GQLResolversUnionTypes<_RefType extends Record> = { items: ReadonlyArray<_RefType['Item']>; }); RemoveAccessibleQueuesToUserResponse: - | GQLMutateAccessibleQueuesForUserSuccessResponse - | GQLNotFoundError; + GQLMutateAccessibleQueuesForUserSuccessResponse | GQLNotFoundError; RemoveFavoriteRuleResponse: GQLRemoveFavoriteRuleSuccessResponse; ReorderRoutingRulesResponse: Omit< GQLMutateRoutingRulesOrderSuccessResponse, @@ -5681,8 +5661,7 @@ export type GQLResolversUnionTypes<_RefType extends Record> = { > & { data: ReadonlyArray<_RefType['RoutingRule']> }; RotateApiKeyResponse: GQLRotateApiKeyError | GQLRotateApiKeySuccessResponse; RotateWebhookSigningKeyResponse: - | GQLRotateWebhookSigningKeyError - | GQLRotateWebhookSigningKeySuccessResponse; + GQLRotateWebhookSigningKeyError | GQLRotateWebhookSigningKeySuccessResponse; RunRetroactionResponse: GQLRunRetroactionSuccessResponse; SchemaFieldRoles: | GQLContentSchemaFieldRoles @@ -14830,6 +14809,11 @@ export type GQLUserInterfacePreferencesResolvers< ParentType, ContextType >; + moderatorSafetySepia?: Resolver< + GQLResolversTypes['Boolean'], + ParentType, + ContextType + >; mrtChartConfigurations?: Resolver< ReadonlyArray, ParentType, diff --git a/server/graphql/modules/itemType.ts b/server/graphql/modules/itemType.ts index 021c52df6..f7994a624 100644 --- a/server/graphql/modules/itemType.ts +++ b/server/graphql/modules/itemType.ts @@ -40,12 +40,10 @@ import { gqlErrorResult, gqlSuccessResult } from '../utils/gqlResult.js'; export type ItemTypeResolversParentType = ItemTypeT | ItemTypeSelector; export type ThreadItemTypeResolversParentType = - | ThreadItemTypeT - | ItemTypeSelector; + ThreadItemTypeT | ItemTypeSelector; export type UserItemTypeResolversParentType = UserItemTypeT | ItemTypeSelector; export type ContentItemTypeResolversParentType = - | ContentItemTypeT - | ItemTypeSelector; + ContentItemTypeT | ItemTypeSelector; const typeDefs = /* GraphQL */ ` interface Field { @@ -1082,13 +1080,9 @@ function isValidField(it: GQLFieldInput): it is Field { // similar idea to the `satisfies` checks above. const containerType = it.container?.containerType satisfies - | ContainerType - | null - | undefined; + ContainerType | null | undefined; const keyScalarType = it.container?.keyScalarType satisfies - | ScalarType - | null - | undefined; + ScalarType | null | undefined; const isValidContainerType = containerTypes.includes(type) && diff --git a/server/graphql/modules/signal.ts b/server/graphql/modules/signal.ts index 060d0c35f..a6aae1cf0 100644 --- a/server/graphql/modules/signal.ts +++ b/server/graphql/modules/signal.ts @@ -104,6 +104,10 @@ const typeDefs = /* GraphQL */ ` OPEN_AI_HATE_TEXT_MODEL OPEN_AI_HATE_THREATENING_TEXT_MODEL OPEN_AI_SELF_HARM_IMAGE_MODEL + OPEN_AI_SELF_HARM_INSTRUCTIONS_IMAGE_MODEL + OPEN_AI_SELF_HARM_INSTRUCTIONS_TEXT_MODEL + OPEN_AI_SELF_HARM_INTENT_IMAGE_MODEL + OPEN_AI_SELF_HARM_INTENT_TEXT_MODEL OPEN_AI_SELF_HARM_TEXT_MODEL OPEN_AI_SEXUAL_IMAGE_MODEL OPEN_AI_SEXUAL_MINORS_TEXT_MODEL diff --git a/server/graphql/modules/user.ts b/server/graphql/modules/user.ts index 45d9febd8..edd51e9d0 100644 --- a/server/graphql/modules/user.ts +++ b/server/graphql/modules/user.ts @@ -79,6 +79,7 @@ const typeDefs = /* GraphQL */ ` type UserInterfacePreferences { moderatorSafetyMuteVideo: Boolean! moderatorSafetyGrayscale: Boolean! + moderatorSafetySepia: Boolean! moderatorSafetyBlurLevel: Int! mrtChartConfigurations: [ManualReviewChartSettings!]! } @@ -87,6 +88,7 @@ const typeDefs = /* GraphQL */ ` moderatorSafetyMuteVideo: Boolean! moderatorSafetyGrayscale: Boolean! moderatorSafetyBlurLevel: Int! + moderatorSafetySepia: Boolean! } input ManualReviewChartConfigurationsInput { diff --git a/server/graphql/utils/resolveSamlUser.test.ts b/server/graphql/utils/resolveSamlUser.test.ts index c28154c55..3c7d0174b 100644 --- a/server/graphql/utils/resolveSamlUser.test.ts +++ b/server/graphql/utils/resolveSamlUser.test.ts @@ -5,11 +5,9 @@ import { uid } from 'uid'; import { UserRole } from '../../services/userManagementService/index.js'; import createOrg from '../../test/fixtureHelpers/createOrg.js'; -import { makeMockedServer } from '../../test/setupMockedServer.js'; -import { makeTestWithFixture } from '../../test/utils.js'; +import { makeTransactionalTestWithFixture } from '../../test/harness/transactionalTest.js'; import { type default as SafeTracer } from '../../utils/SafeTracer.js'; import { - kyselyUserDeleteById, kyselyUserInsert, type UsersDb, } from '../datasources/userKyselyPersistence.js'; @@ -33,9 +31,8 @@ function samlUserInput(orgId: string) { } describe('resolveSamlUser', () => { - const testWithFixture = makeTestWithFixture(async () => { - const { deps, shutdown } = await makeMockedServer(); - const { org, cleanup: orgCleanup } = await createOrg( + const testWithFixture = makeTransactionalTestWithFixture(async ({ deps }) => { + const { org } = await createOrg( { KyselyPg: deps.KyselyPg, ModerationConfigService: deps.ModerationConfigService, @@ -43,14 +40,7 @@ describe('resolveSamlUser', () => { }, uid(), ); - return { - deps, - org, - async cleanup() { - await orgCleanup(); - await shutdown(); - }, - }; + return { org }; }); testWithFixture( @@ -59,21 +49,17 @@ describe('resolveSamlUser', () => { const input = samlUserInput(org.id); await kyselyUserInsert({ db: deps.KyselyPg, ...input }); const done = jest.fn(); - try { - await resolveSamlUser( - deps.KyselyPg, - deps.Tracer, - makeReq(org.id), - { email: input.email }, - done, - ); - expect(done).toHaveBeenCalledTimes(1); - const [err, user] = done.mock.calls[0]; - expect(err).toBeNull(); - expect(user).toMatchObject({ id: input.id, orgId: org.id }); - } finally { - await kyselyUserDeleteById(deps.KyselyPg, input.id); - } + await resolveSamlUser( + deps.KyselyPg, + deps.Tracer, + makeReq(org.id), + { email: input.email }, + done, + ); + expect(done).toHaveBeenCalledTimes(1); + const [err, user] = done.mock.calls[0]; + expect(err).toBeNull(); + expect(user).toMatchObject({ id: input.id, orgId: org.id }); }, ); @@ -85,20 +71,16 @@ describe('resolveSamlUser', () => { const input = samlUserInput(org.id); await kyselyUserInsert({ db: deps.KyselyPg, ...input }); const done = jest.fn(); - try { - await resolveSamlUser( - deps.KyselyPg, - deps.Tracer, - makeReq(`different-org-${uid()}`), - { email: input.email }, - done, - ); - const [err, user] = done.mock.calls[0]; - expect(err).toBeInstanceOf(Error); - expect(user).toBeUndefined(); - } finally { - await kyselyUserDeleteById(deps.KyselyPg, input.id); - } + await resolveSamlUser( + deps.KyselyPg, + deps.Tracer, + makeReq(`different-org-${uid()}`), + { email: input.email }, + done, + ); + const [err, user] = done.mock.calls[0]; + expect(err).toBeInstanceOf(Error); + expect(user).toBeUndefined(); }, ); diff --git a/server/iocContainer/index.ts b/server/iocContainer/index.ts index 5d4a7b71e..fe68e9f82 100644 --- a/server/iocContainer/index.ts +++ b/server/iocContainer/index.ts @@ -59,6 +59,9 @@ import makeRuleEvaluator, { type RuleEvaluator, } from '../rule_engine/RuleEvaluator.js'; import { Scylla } from '../scylla/index.js'; +import NoOpScylla, { + itemInvestigationAndStrikesEnabled, +} from '../scylla/noOpScylla.js'; import { makeActionStatisticsService, type ActionStatisticsService, @@ -772,6 +775,9 @@ export default async function getBottle() { executionContext, ); }, + itemInvestigationAndStrikesEnabled( + process.env.ITEM_INVESTIGATION_AND_STRIKES_ENABLED, + ), ), ); @@ -783,6 +789,23 @@ export default async function getBottle() { // keyspace aware and it's very annoying and likely error prone to be // switching keyspaces with `USE KEYSPACE` all the time. bottle.factory('Scylla', () => { + // Scylla backs the item-investigation and user-strike features. Operators + // who don't need those (and don't want to run a Scylla cluster) can set + // `ITEM_INVESTIGATION_AND_STRIKES_ENABLED=false` to swap in a no-op that + // drops writes and returns empty reads, so no `SCYLLA_*` connection env + // vars are required. Defaults to enabled to preserve existing behaviour. + if ( + !itemInvestigationAndStrikesEnabled( + process.env.ITEM_INVESTIGATION_AND_STRIKES_ENABLED, + ) + ) { + // eslint-disable-next-line no-restricted-syntax + logJson( + 'scylla.disabled ITEM_INVESTIGATION_AND_STRIKES_ENABLED=false; using no-op Scylla', + ); + return new NoOpScylla(); + } + const contactPoints = safeGetEnvVar('SCYLLA_HOSTS') .split(',') .map((it) => it.trim()) @@ -1684,7 +1707,9 @@ export default async function getBottle() { Dependencies, { [ServiceName in keyof Dependencies]: { - [Method in CloseMethodName]: Dependencies[ServiceName] extends { + [ + Method in CloseMethodName + ]: Dependencies[ServiceName] extends { [_ in Method]: unknown; } ? ServiceName diff --git a/server/iocContainer/utils.ts b/server/iocContainer/utils.ts index bc301aa46..db5baf205 100644 --- a/server/iocContainer/utils.ts +++ b/server/iocContainer/utils.ts @@ -10,8 +10,7 @@ const DEPENDENCIES = Symbol(); type DepName = keyof Deps; export type Factory = - | ((...args: D) => R) - | (new (...args: D) => R); + ((...args: D) => R) | (new (...args: D) => R); export type AnnotatedFactory = Factory & { [DEPENDENCIES]: DepName[]; diff --git a/server/lib/cache/types/utils.ts b/server/lib/cache/types/utils.ts index afffa3f82..90d8cec4f 100644 --- a/server/lib/cache/types/utils.ts +++ b/server/lib/cache/types/utils.ts @@ -2,12 +2,7 @@ * JSON-serializable values. */ export type JSON = - | null - | string - | boolean - | number - | JSON[] - | { [k: string]: JSON }; + null | string | boolean | number | JSON[] | { [k: string]: JSON }; export type Bind1< F extends (arg0: A0, ...args: never[]) => unknown, diff --git a/server/package-lock.json b/server/package-lock.json index 77d2fac80..0d47eef71 100644 --- a/server/package-lock.json +++ b/server/package-lock.json @@ -103,7 +103,6 @@ "@typescript-eslint/eslint-plugin": "^8.57.2", "@typescript-eslint/parser": "^8.57.2", "copyfiles": "^2.4.1", - "dotenv": "^10.0.0", "eslint": "^9.39.4", "eslint-import-resolver-typescript": "^3.6.0", "eslint-plugin-functional": "^9.0.4", @@ -112,17 +111,17 @@ "eslint-plugin-node": "^11.1.0", "eslint-plugin-promise": "^7.2.1", "eslint-plugin-security": "^1.7.1", - "eslint-plugin-switch-statement": "^0.0.11", + "eslint-plugin-switch-statement": "^0.0.12", "jest": "^29.3.1", "jest-junit": "^16.0.0", "jest-light-runner": "^0.4.1", - "js-yaml": "^4.2.0", + "js-yaml": "^5.2.2", "jsonpath-plus": "^10.4.0", "knip": "^6.16.1", "supertest": "^6.2.2", "ts-node": "^10.9.1", "tsc-watch": "^4.6.0", - "typescript": "^5.5.2", + "typescript": "5.5.2", "typescript-eslint": "^8.57.2" } }, @@ -479,7 +478,8 @@ "version": "0.10.1", "resolved": "https://registry.npmjs.org/@assemblyscript/loader/-/loader-0.10.1.tgz", "integrity": "sha512-H71nDOOL8Y7kWRLqf6Sums+01Q5msqBW2KhDUTemh1tvY04eSkSXrK0uj/4mmY0Xr16/3zyZmsrxN7CKuRbNRg==", - "dev": true + "dev": true, + "license": "Apache-2.0" }, "node_modules/@aws-crypto/crc32": { "version": "5.2.0", @@ -1737,10 +1737,11 @@ } }, "node_modules/@babel/helper-plugin-utils": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.22.5.tgz", - "integrity": "sha512-uLls06UVKgFG9QD4OeFYLEGteMIAa5kpTPcFL28yuCIIzsf6ZyKZMllKVOCZFhiZ5ptnwX4mtKdWCBE/uT4amg==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.29.7.tgz", + "integrity": "sha512-G7sHYigPY17oO5SYWnfD/0MTBwVR781S/JI643e/JhUYgVgWE/61SoW3NH9KWUKyKq5LVh3npif99Wkt6j86Jw==", "dev": true, + "license": "MIT", "engines": { "node": ">=6.9.0" } @@ -1841,6 +1842,38 @@ "@babel/core": "^7.0.0-0" } }, + "node_modules/@babel/plugin-syntax-class-static-block": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-static-block/-/plugin-syntax-class-static-block-7.14.5.tgz", + "integrity": "sha512-b+YyPmr6ldyNnM6sqYeMWE+bgJcJpO6yS4QD7ymxgH34GBPNDM/THBh8iunyvKIZztiwLH4CJZ0RxTk9emgpjw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-import-attributes": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-attributes/-/plugin-syntax-import-attributes-7.29.7.tgz", + "integrity": "sha512-zGYcYfq/WmZ4V+kBIXQon9dSSc8ircGZqw9ZaNhhGj9nZkeBu1jHLBDQqYYi5WA9uawvA2sIMbry2nCFhf5Djg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, "node_modules/@babel/plugin-syntax-import-meta": { "version": "7.10.4", "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-meta/-/plugin-syntax-import-meta-7.10.4.tgz", @@ -1866,12 +1899,13 @@ } }, "node_modules/@babel/plugin-syntax-jsx": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.22.5.tgz", - "integrity": "sha512-gvyP4hZrgrs/wWMaocvxZ44Hw0b3W8Pe+cMxc8V1ULQ07oh8VNbIRaoD1LRZVTvD+0nieDKjfgKg89sD7rrKrg==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.29.7.tgz", + "integrity": "sha512-TSu8+mHCoEaaCDEZ0I3+6mvTBYR4PCxQwf2z9/r5Tbztv6NaLR3B9thGTTxX2WGuGHJqRiAbKPeGTJ5XWXVg6A==", "dev": true, + "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5" + "@babel/helper-plugin-utils": "^7.29.7" }, "engines": { "node": ">=6.9.0" @@ -1952,6 +1986,22 @@ "@babel/core": "^7.0.0-0" } }, + "node_modules/@babel/plugin-syntax-private-property-in-object": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-private-property-in-object/-/plugin-syntax-private-property-in-object-7.14.5.tgz", + "integrity": "sha512-0wVnp9dxJ72ZUJDV27ZfbSj6iHLoytYZmh3rFcxNnvsJF3ktkzLDZPy/mA17HGsaQT3/DQsWYX1f1QGWkCoVUg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, "node_modules/@babel/plugin-syntax-top-level-await": { "version": "7.14.5", "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.14.5.tgz", @@ -1968,12 +2018,13 @@ } }, "node_modules/@babel/plugin-syntax-typescript": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.22.5.tgz", - "integrity": "sha512-1mS2o03i7t1c6VzH6fdQ3OA8tcEIxwG18zIPRp+UY1Ihv6W+XZzBCVxExF9upussPXJ0xE9XRHwMoNs1ep/nRQ==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.29.7.tgz", + "integrity": "sha512-ngr+82Sh0xMz25TPCZi+nC2iTzjfCdWS2ONXTp/PtSCHCgaCNBpdMqgvJ2ccdLlClVZ7sisIgB914j/JFe+RZA==", "dev": true, + "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5" + "@babel/helper-plugin-utils": "^7.29.7" }, "engines": { "node": ">=6.9.0" @@ -2126,26 +2177,26 @@ } }, "node_modules/@es-joy/jsdoccomment": { - "version": "0.84.0", - "resolved": "https://registry.npmjs.org/@es-joy/jsdoccomment/-/jsdoccomment-0.84.0.tgz", - "integrity": "sha512-0xew1CxOam0gV5OMjh2KjFQZsKL2bByX1+q4j3E73MpYIdyUxcZb/xQct9ccUb+ve5KGUYbCUxyPnYB7RbuP+w==", + "version": "0.86.0", + "resolved": "https://registry.npmjs.org/@es-joy/jsdoccomment/-/jsdoccomment-0.86.0.tgz", + "integrity": "sha512-ukZmRQ81WiTpDWO6D/cTBM7XbrNtutHKvAVnZN/8pldAwLoJArGOvkNyxPTBGsPjsoaQBJxlH+tE2TNA/92Qgw==", "dev": true, "license": "MIT", "dependencies": { "@types/estree": "^1.0.8", - "@typescript-eslint/types": "^8.54.0", - "comment-parser": "1.4.5", + "@typescript-eslint/types": "^8.58.0", + "comment-parser": "1.4.6", "esquery": "^1.7.0", - "jsdoc-type-pratt-parser": "~7.1.1" + "jsdoc-type-pratt-parser": "~7.2.0" }, "engines": { "node": "^20.19.0 || ^22.13.0 || >=24" } }, "node_modules/@es-joy/jsdoccomment/node_modules/@typescript-eslint/types": { - "version": "8.55.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.55.0.tgz", - "integrity": "sha512-ujT0Je8GI5BJWi+/mMoR0wxwVEQaxM+pi30xuMiJETlX80OPovb2p9E8ss87gnSVtYXtJoU9U1Cowcr6w2FE0w==", + "version": "8.65.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.65.0.tgz", + "integrity": "sha512-JSSwWNy+H0E/01jJEM+hrX6N0OFDzFzeIhHFSAS01tlVaevpG8cFyYRPhS5yjGOvBUx3sqQHVMjCL1CAZZMxBg==", "dev": true, "license": "MIT", "engines": { @@ -2196,13 +2247,13 @@ } }, "node_modules/@eslint/compat": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/@eslint/compat/-/compat-2.0.3.tgz", - "integrity": "sha512-SjIJhGigp8hmd1YGIBwh7Ovri7Kisl42GYFjrOyHhtfYGGoLW6teYi/5p8W50KSsawUPpuLOSmsq1bD0NGQLBw==", + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@eslint/compat/-/compat-2.1.0.tgz", + "integrity": "sha512-LgaSCymEpw7tF53xvDw9SNsraPb1IBHxpdABIOM0hW8UAlP8znrjYtuxfR58FSJ3L9BhwD+FaPRFQpZq84Nh6g==", "dev": true, "license": "Apache-2.0", "dependencies": { - "@eslint/core": "^1.1.1" + "@eslint/core": "^1.2.1" }, "engines": { "node": "^20.19.0 || ^22.13.0 || >=24" @@ -2217,9 +2268,9 @@ } }, "node_modules/@eslint/compat/node_modules/@eslint/core": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@eslint/core/-/core-1.1.1.tgz", - "integrity": "sha512-QUPblTtE51/7/Zhfv8BDwO0qkkzQL7P/aWWbqcf4xWLEYn1oKjdO0gglQBB4GAsu7u6wjijbCmzsUTy6mnk6oQ==", + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@eslint/core/-/core-1.2.1.tgz", + "integrity": "sha512-MwcE1P+AZ4C6DWlpin/OmOA54mmIZ/+xZuJiQd4SyB29oAJjN30UW9wkKNptW2ctp4cEsvhlLY/CsQ1uoHDloQ==", "dev": true, "license": "Apache-2.0", "dependencies": { @@ -2271,9 +2322,9 @@ } }, "node_modules/@eslint/eslintrc": { - "version": "3.3.5", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.3.5.tgz", - "integrity": "sha512-4IlJx0X0qftVsN5E+/vGujTRIFtwuLbNsVUe7TO6zYPDR1O6nFwvwhIKEKSrl6dZchmYBITazxKoUYOjdtjlRg==", + "version": "3.3.6", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.3.6.tgz", + "integrity": "sha512-l2Ul9PrHsPCKcEY/ac7VgFj9D80C7S68sOKc618SyHDPK36s1XcFebXY0iTzUVn4Yq+YbwvSnDmCz9yxjX+QrA==", "dev": true, "license": "MIT", "dependencies": { @@ -2283,7 +2334,7 @@ "globals": "^14.0.0", "ignore": "^5.2.0", "import-fresh": "^3.2.1", - "js-yaml": "^4.1.1", + "js-yaml": "^4.3.0", "minimatch": "^3.1.5", "strip-json-comments": "^3.1.1" }, @@ -2311,6 +2362,29 @@ "url": "https://github.com/sponsors/epoberezkin" } }, + "node_modules/@eslint/eslintrc/node_modules/js-yaml": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.3.0.tgz", + "integrity": "sha512-1td788aAnnZ5qs7V2QIRl1owjtYpbKt749Y3xauqQgwIIGF/xXWz1wMTEBx5O3LK3lXLVuqXPdPxj2BoFHaW9Q==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/puzrin" + }, + { + "type": "github", + "url": "https://github.com/sponsors/nodeca" + } + ], + "license": "MIT", + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, "node_modules/@eslint/eslintrc/node_modules/json-schema-traverse": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", @@ -2319,9 +2393,9 @@ "license": "MIT" }, "node_modules/@eslint/js": { - "version": "9.39.4", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.39.4.tgz", - "integrity": "sha512-nE7DEIchvtiFTwBw4Lfbu59PG+kCofhjsKaCWzxTpt4lfRjRMqG6uMBzKXuEcyXhOHoUp9riAm7/aWYGhXZ9cw==", + "version": "9.39.5", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.39.5.tgz", + "integrity": "sha512-QywQuszQh77pIXCsq998c8hbhSTI/azTty1Z6N53dmAudKHhy573j3yvRLsX2BSp8YpLtoCEG8E9DJe+8zUh4A==", "dev": true, "license": "MIT", "engines": { @@ -2780,9 +2854,9 @@ } }, "node_modules/@istanbuljs/load-nyc-config/node_modules/js-yaml": { - "version": "3.14.2", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.2.tgz", - "integrity": "sha512-PMSmkqxr106Xa156c2M265Z+FTrPl+oxd/rgOQy2tijQeK5TxQ43psO1ZCwhVOSdnn+RzkzlRz/eY4BgJBYVpg==", + "version": "3.15.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.15.0.tgz", + "integrity": "sha512-ttBQIIQPDeLjpPOohtUdXuXUVoA2uIB6fEH9HyJ7234s5mBJ5wTx20njxplLZQgLaOfpmPQA7X2t5AX6tIPbog==", "dev": true, "license": "MIT", "dependencies": { @@ -3480,6 +3554,16 @@ "node": ">= 8" } }, + "node_modules/@nolyfill/is-core-module": { + "version": "1.0.39", + "resolved": "https://registry.npmjs.org/@nolyfill/is-core-module/-/is-core-module-1.0.39.tgz", + "integrity": "sha512-nn5ozdjYQpUCZlWGuxcJY/KpxkWQs4DcbMCmKojjyrYDEAGy4Ce19NN4v5MduafTwJlbKc99UA8YhSVqq9yPZA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12.4.0" + } + }, "node_modules/@opentelemetry/api": { "version": "1.8.0", "resolved": "https://registry.npmjs.org/@opentelemetry/api/-/api-1.8.0.tgz", @@ -3498,9 +3582,9 @@ } }, "node_modules/@oxc-parser/binding-android-arm-eabi": { - "version": "0.133.0", - "resolved": "https://registry.npmjs.org/@oxc-parser/binding-android-arm-eabi/-/binding-android-arm-eabi-0.133.0.tgz", - "integrity": "sha512-l/44caGse+VpnY9gx0yvvc5QnnG3yG1FO3KZgYvNL1GZrfK86zIwAOgGEVlxDyRymzrU/KHiblPFpevKOmJmUA==", + "version": "0.137.0", + "resolved": "https://registry.npmjs.org/@oxc-parser/binding-android-arm-eabi/-/binding-android-arm-eabi-0.137.0.tgz", + "integrity": "sha512-KDs+0VPdEmasOkpuJHW9V5WCF+cvYdMQv2Jd+aJXt+cxIx12NToRQRbXaRwUEDsZw+/jMk81Ve8ZFbjUkJTOwA==", "cpu": [ "arm" ], @@ -3515,9 +3599,9 @@ } }, "node_modules/@oxc-parser/binding-android-arm64": { - "version": "0.133.0", - "resolved": "https://registry.npmjs.org/@oxc-parser/binding-android-arm64/-/binding-android-arm64-0.133.0.tgz", - "integrity": "sha512-KUHmPMziLBp4u+zbrLdB7iWS7KshuZe+RAp7ELnY9SI9nNXBZ+dp8fiBqWOxhXqn+FQg3a4UcQhwmsJOKV8Jjg==", + "version": "0.137.0", + "resolved": "https://registry.npmjs.org/@oxc-parser/binding-android-arm64/-/binding-android-arm64-0.137.0.tgz", + "integrity": "sha512-WhALNzfy3x/RfC6bsqX+csavuUY0yHHE7XfgPE5M542uhoBZUUoGTPG+nkMbGoG4+gcfss5s7urMyn5QBHu0sw==", "cpu": [ "arm64" ], @@ -3532,9 +3616,9 @@ } }, "node_modules/@oxc-parser/binding-darwin-arm64": { - "version": "0.133.0", - "resolved": "https://registry.npmjs.org/@oxc-parser/binding-darwin-arm64/-/binding-darwin-arm64-0.133.0.tgz", - "integrity": "sha512-q8dWmnU/8ea2tga9w2f1PinQ5rcMPDUGkF64T189b65YMjUomET4oy5oRldOr4AwOQkneOG/Zttnz1Dvrc62wg==", + "version": "0.137.0", + "resolved": "https://registry.npmjs.org/@oxc-parser/binding-darwin-arm64/-/binding-darwin-arm64-0.137.0.tgz", + "integrity": "sha512-bFPr5hgmNMOMoyPTGtdsK4Ug21RovIPojRMgDDhSp1LtCnc/DkLwGONKjgRjszg677RlGnkYSviQ8hHaUPOVYA==", "cpu": [ "arm64" ], @@ -3549,9 +3633,9 @@ } }, "node_modules/@oxc-parser/binding-darwin-x64": { - "version": "0.133.0", - "resolved": "https://registry.npmjs.org/@oxc-parser/binding-darwin-x64/-/binding-darwin-x64-0.133.0.tgz", - "integrity": "sha512-cOKeIELIB2bJnCKwqx4Rdj+1Lss/U6uCbLxRySZrhyOOQa1flKhwZFjEHRHxk8fU1NKmhK5OnTdPQ4CpjuFuVw==", + "version": "0.137.0", + "resolved": "https://registry.npmjs.org/@oxc-parser/binding-darwin-x64/-/binding-darwin-x64-0.137.0.tgz", + "integrity": "sha512-CL5dMm1asqXIDZHg14FLxj3Mc36w8PI7xCWh1uA4is6z8g2XrIILoTcQYOxDbwzuk34RDPX5IAGUxZr6LA9KAg==", "cpu": [ "x64" ], @@ -3566,9 +3650,9 @@ } }, "node_modules/@oxc-parser/binding-freebsd-x64": { - "version": "0.133.0", - "resolved": "https://registry.npmjs.org/@oxc-parser/binding-freebsd-x64/-/binding-freebsd-x64-0.133.0.tgz", - "integrity": "sha512-OpaSv4pW3KgFrMYQxTaS0aOE4T1DQF3qZE/4B6uqqv1KgPWWd4UQhJALi8PJPX1RRV5K7ThKXRfF7qGg2+3l1A==", + "version": "0.137.0", + "resolved": "https://registry.npmjs.org/@oxc-parser/binding-freebsd-x64/-/binding-freebsd-x64-0.137.0.tgz", + "integrity": "sha512-79h8rYGnSlKPGWo7mHr2ixO6ea7aW8B0CT965SZ8SLbNnCOH5aOYBTeVXUY6eMvEaiLyWr8Skuiugr5pDYgLGw==", "cpu": [ "x64" ], @@ -3583,9 +3667,9 @@ } }, "node_modules/@oxc-parser/binding-linux-arm-gnueabihf": { - "version": "0.133.0", - "resolved": "https://registry.npmjs.org/@oxc-parser/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-0.133.0.tgz", - "integrity": "sha512-JGK1wlGrGwxBIlVSF7KWTX1/ru6BEtf28fRROztDRkLfiW+Kxa4onnriezMIiogfn9hVw2KzYcKiLjkLR2ns8A==", + "version": "0.137.0", + "resolved": "https://registry.npmjs.org/@oxc-parser/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-0.137.0.tgz", + "integrity": "sha512-ASgmlSimhGyr0lksgVIo6hibz1obnDq4qJbiMX/AzltfgPnanRrzG1Q+23g8ljOHOjv6dsznkUuCYL3gg0sY1Q==", "cpu": [ "arm" ], @@ -3600,9 +3684,9 @@ } }, "node_modules/@oxc-parser/binding-linux-arm-musleabihf": { - "version": "0.133.0", - "resolved": "https://registry.npmjs.org/@oxc-parser/binding-linux-arm-musleabihf/-/binding-linux-arm-musleabihf-0.133.0.tgz", - "integrity": "sha512-yuZO533Ftonxn/iyoqQzURzLQHMspvsIyfiCSNi1t/ER4eIQaR0SsmUOUm5b/lmSig7IWIUa5/BrbEkAPwcilQ==", + "version": "0.137.0", + "resolved": "https://registry.npmjs.org/@oxc-parser/binding-linux-arm-musleabihf/-/binding-linux-arm-musleabihf-0.137.0.tgz", + "integrity": "sha512-AU2J9aa22Sx32wRGnDjybOU9TQXXQUud5sdUi+ZB0XxwM8aToWLweV+yA0wlQm0yIUVqljquqoHCYEq9II8gJQ==", "cpu": [ "arm" ], @@ -3617,13 +3701,16 @@ } }, "node_modules/@oxc-parser/binding-linux-arm64-gnu": { - "version": "0.133.0", - "resolved": "https://registry.npmjs.org/@oxc-parser/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-0.133.0.tgz", - "integrity": "sha512-hvpbqT5pN2rR+3+xtWeizwfR/aZ0vGceg6TqYMl+ToxMpk9/tmnX7kSvQnfEUkoua8mhogzvIKsAkn0wxgblBA==", + "version": "0.137.0", + "resolved": "https://registry.npmjs.org/@oxc-parser/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-0.137.0.tgz", + "integrity": "sha512-GdEtiG89yMr7XkUGxifgodXEEm2f+xW2f9CpDjlgAnBOwhTmrpQMvhOGobLVKUyzf/qHBXW16smk5zbF3nZU6w==", "cpu": [ "arm64" ], "dev": true, + "libc": [ + "glibc" + ], "license": "MIT", "optional": true, "os": [ @@ -3634,13 +3721,16 @@ } }, "node_modules/@oxc-parser/binding-linux-arm64-musl": { - "version": "0.133.0", - "resolved": "https://registry.npmjs.org/@oxc-parser/binding-linux-arm64-musl/-/binding-linux-arm64-musl-0.133.0.tgz", - "integrity": "sha512-wJQGamIosQBoJHW9+S5XxrtKRo3eyJxsnS1XCPrqN0LHi8uw1pTqqTfn3t/NVuvbBg7Pumn4ez9Eidgcn0xbEg==", + "version": "0.137.0", + "resolved": "https://registry.npmjs.org/@oxc-parser/binding-linux-arm64-musl/-/binding-linux-arm64-musl-0.137.0.tgz", + "integrity": "sha512-EGJ+Bs8iXx8KBH8DQ5BLoEm5lnHaYjlh4/8j8vFhrr/6z4tqONy5BZDzLpKmmNWlN6Hlc5r8YOuBVHqZ9vRFEQ==", "cpu": [ "arm64" ], "dev": true, + "libc": [ + "musl" + ], "license": "MIT", "optional": true, "os": [ @@ -3651,13 +3741,16 @@ } }, "node_modules/@oxc-parser/binding-linux-ppc64-gnu": { - "version": "0.133.0", - "resolved": "https://registry.npmjs.org/@oxc-parser/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-0.133.0.tgz", - "integrity": "sha512-Koaz32/O5+abIfrNGdyndgRvdOZ9jEf5/z3Ep9h3h2QWpdDiUQpVwgH0OcMXCs+l9aXxPLtkupqyVig9W6FDKw==", + "version": "0.137.0", + "resolved": "https://registry.npmjs.org/@oxc-parser/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-0.137.0.tgz", + "integrity": "sha512-vzFUQENy/fnbSe5DZWovq6tIBc1uhuMztanSW6rz1e9WdQE4gHwYuD7ZII6JnrJifd1R3RSoqiZbgRFlVL2tYQ==", "cpu": [ "ppc64" ], "dev": true, + "libc": [ + "glibc" + ], "license": "MIT", "optional": true, "os": [ @@ -3668,13 +3761,16 @@ } }, "node_modules/@oxc-parser/binding-linux-riscv64-gnu": { - "version": "0.133.0", - "resolved": "https://registry.npmjs.org/@oxc-parser/binding-linux-riscv64-gnu/-/binding-linux-riscv64-gnu-0.133.0.tgz", - "integrity": "sha512-R4vOjWzxhnNWHnVLeiB6jNuIifdy9vcMXZGPc7StXcxBovI+U2zg1QhZ9o8OjV80oGivs1lX5NfPLzk4IPqlRA==", + "version": "0.137.0", + "resolved": "https://registry.npmjs.org/@oxc-parser/binding-linux-riscv64-gnu/-/binding-linux-riscv64-gnu-0.137.0.tgz", + "integrity": "sha512-SfVI14HBQs9gtLcUD5hTt5hsNbdrqSUNg9S8muN+LhVQ5nf1WwH3hAoK6B9NKgdYgWAQSXFXGiiBedQ4r/BKuw==", "cpu": [ "riscv64" ], "dev": true, + "libc": [ + "glibc" + ], "license": "MIT", "optional": true, "os": [ @@ -3685,13 +3781,16 @@ } }, "node_modules/@oxc-parser/binding-linux-riscv64-musl": { - "version": "0.133.0", - "resolved": "https://registry.npmjs.org/@oxc-parser/binding-linux-riscv64-musl/-/binding-linux-riscv64-musl-0.133.0.tgz", - "integrity": "sha512-iwgBNUTHiMdxARLYuM0SBlnYeb19iw1Ea5M+4ERZupCsBMLArti6FyZ6UfFjJxIiTDr2oW2DGQFxlQVQ/dW9rA==", + "version": "0.137.0", + "resolved": "https://registry.npmjs.org/@oxc-parser/binding-linux-riscv64-musl/-/binding-linux-riscv64-musl-0.137.0.tgz", + "integrity": "sha512-e7Ppy4FCIFNQxT/ikSeIWFoQ0l+N9vgtRBtLcyZXeolTzApyVoPqEXsYPrcdM/9i0Bwk8knvYd37vaEMxHyi6g==", "cpu": [ "riscv64" ], "dev": true, + "libc": [ + "musl" + ], "license": "MIT", "optional": true, "os": [ @@ -3702,13 +3801,16 @@ } }, "node_modules/@oxc-parser/binding-linux-s390x-gnu": { - "version": "0.133.0", - "resolved": "https://registry.npmjs.org/@oxc-parser/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-0.133.0.tgz", - "integrity": "sha512-ZwZNo8FZmB/gVfboQl+wXilBigGl+6nQQs+nITOeAP/HcAOjiHl6XZJL9F/KXNEspODQcbjAiyjUbeCJd9a0fA==", + "version": "0.137.0", + "resolved": "https://registry.npmjs.org/@oxc-parser/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-0.137.0.tgz", + "integrity": "sha512-Bho5qFwdhqsIFR7gipYEUlqvi3SRrY8sugxXig380MIaakBB1PyU9+7dBiBVScfImTNWhijUxdBwqrprGdq5WA==", "cpu": [ "s390x" ], "dev": true, + "libc": [ + "glibc" + ], "license": "MIT", "optional": true, "os": [ @@ -3719,13 +3821,16 @@ } }, "node_modules/@oxc-parser/binding-linux-x64-gnu": { - "version": "0.133.0", - "resolved": "https://registry.npmjs.org/@oxc-parser/binding-linux-x64-gnu/-/binding-linux-x64-gnu-0.133.0.tgz", - "integrity": "sha512-govCvWx1dBlED3uu4qXctxpRcouu9I8Kn+DBktGCl760JtlGJzc9l/OmPJKlYWSbrRqKkMZehNeZ/4Wfma7uSA==", + "version": "0.137.0", + "resolved": "https://registry.npmjs.org/@oxc-parser/binding-linux-x64-gnu/-/binding-linux-x64-gnu-0.137.0.tgz", + "integrity": "sha512-36mGWtg7PyFzjJwGDkH6/F4o2nIDEoKXLPr/X/lwqklkomQwJJt1I5GJVmGhovUEmgPK5WAeAZMqlFCehwiy9Q==", "cpu": [ "x64" ], "dev": true, + "libc": [ + "glibc" + ], "license": "MIT", "optional": true, "os": [ @@ -3736,13 +3841,16 @@ } }, "node_modules/@oxc-parser/binding-linux-x64-musl": { - "version": "0.133.0", - "resolved": "https://registry.npmjs.org/@oxc-parser/binding-linux-x64-musl/-/binding-linux-x64-musl-0.133.0.tgz", - "integrity": "sha512-ssTlpXD5Mq9uCssDJPzlRWqBt4Y7Zzd9i+XZhWmK/9Y6KUIuAxVYTYiI8lxcGWi0+3/Cz4A8q9UrD4NK9Y2j7g==", + "version": "0.137.0", + "resolved": "https://registry.npmjs.org/@oxc-parser/binding-linux-x64-musl/-/binding-linux-x64-musl-0.137.0.tgz", + "integrity": "sha512-/Jqx6+N7A44n2BdvUr7pXhVr2vFjs6WGH3unZRczwrfiH0H1zY0QwKQMG/dtRiTlKGDKGukznPT8lx84/oEsZg==", "cpu": [ "x64" ], "dev": true, + "libc": [ + "musl" + ], "license": "MIT", "optional": true, "os": [ @@ -3753,9 +3861,9 @@ } }, "node_modules/@oxc-parser/binding-openharmony-arm64": { - "version": "0.133.0", - "resolved": "https://registry.npmjs.org/@oxc-parser/binding-openharmony-arm64/-/binding-openharmony-arm64-0.133.0.tgz", - "integrity": "sha512-51aByfXhPtLEdWG4a2Ihdw6cPWV1ei1AarALpFdDP8MLWDLE2NuUMgbo3DERR2Kt8fT/ok1GUvBiLxVGke9uUQ==", + "version": "0.137.0", + "resolved": "https://registry.npmjs.org/@oxc-parser/binding-openharmony-arm64/-/binding-openharmony-arm64-0.137.0.tgz", + "integrity": "sha512-9Uj0qHNNl+OgT1UTGwF7ixIXU6T1u2SbMidmgPy/h1h/fl2gRS6YpAxxY1gwHofcWjoTwkoMFd8xs5Vuj6GOFA==", "cpu": [ "arm64" ], @@ -3770,9 +3878,9 @@ } }, "node_modules/@oxc-parser/binding-wasm32-wasi": { - "version": "0.133.0", - "resolved": "https://registry.npmjs.org/@oxc-parser/binding-wasm32-wasi/-/binding-wasm32-wasi-0.133.0.tgz", - "integrity": "sha512-2e16tkKp+wDO2GTAmXfxbBcCmGEaFPIJEIRBBmVKNVXSc8/fJsSIaBGyFTPHM9ST5GNWgJcYIt94rDTks+PLwA==", + "version": "0.137.0", + "resolved": "https://registry.npmjs.org/@oxc-parser/binding-wasm32-wasi/-/binding-wasm32-wasi-0.137.0.tgz", + "integrity": "sha512-gW2vfkytNGgMVADiuzdvOfw0mWG9za20F/1fCJsif5aBMAvWJTSbpIXbIe0XkOe0VENk+PadpQ7cZgUy2sUJcA==", "cpu": [ "wasm32" ], @@ -3780,18 +3888,74 @@ "license": "MIT", "optional": true, "dependencies": { - "@emnapi/core": "1.10.0", - "@emnapi/runtime": "1.10.0", - "@napi-rs/wasm-runtime": "^1.1.4" + "@emnapi/core": "1.11.1", + "@emnapi/runtime": "1.11.1", + "@napi-rs/wasm-runtime": "^1.1.5" }, "engines": { "node": "^20.19.0 || >=22.12.0" } }, + "node_modules/@oxc-parser/binding-wasm32-wasi/node_modules/@emnapi/core": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.11.1.tgz", + "integrity": "sha512-RSvbQmHzdKzNsLYa/wHrbc3KN4sYLKAdPZxqiM2HATqv/SBk2/ENSHpvXGaLOMcsAyz0poEGqkmmKYG3OWiJEQ==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@emnapi/wasi-threads": "1.2.2", + "tslib": "^2.4.0" + } + }, + "node_modules/@oxc-parser/binding-wasm32-wasi/node_modules/@emnapi/runtime": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.11.1.tgz", + "integrity": "sha512-vgj7R3y3Wgx24IQaGPA/R6YFXLHVMOZ0uVEyIQPaWs+rd1AzfEMXlAC22FYwO1XkKR6NPsq7mUandH8oIRdZFw==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@oxc-parser/binding-wasm32-wasi/node_modules/@emnapi/wasi-threads": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.2.2.tgz", + "integrity": "sha512-c95qOXkHdydNKhscBTebqEC1CVAZpyqOfVfBzQ1qgzyl3gfeldUjIggDbIZgDKsHLgnsM+igH7TJ/eAasaVuMA==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@oxc-parser/binding-wasm32-wasi/node_modules/@napi-rs/wasm-runtime": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-1.2.0.tgz", + "integrity": "sha512-kDoONqMa+VnZ4vvvu/ZUurpJ4gkZU57e7g69qpNgWhYcZFPUHZM2CEMKm+cG6ufDVALbjMvfmMjFVqaK7uEMnA==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@tybys/wasm-util": "^0.10.3" + }, + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=23.5.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Brooooooklyn" + }, + "peerDependencies": { + "@emnapi/core": "^2.0.0-alpha.3", + "@emnapi/runtime": "^2.0.0-alpha.3" + } + }, "node_modules/@oxc-parser/binding-win32-arm64-msvc": { - "version": "0.133.0", - "resolved": "https://registry.npmjs.org/@oxc-parser/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-0.133.0.tgz", - "integrity": "sha512-KPTNDKbxH1cglrqTyVeXHb4Pk4oksz8EcE1/v8zqU7N4UXbiHfA/IwtXZ2U77fnRAWBbgVkl/lZbL7o3hRdejg==", + "version": "0.137.0", + "resolved": "https://registry.npmjs.org/@oxc-parser/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-0.137.0.tgz", + "integrity": "sha512-x+pFANF0yL5uK/6T7lu6SlR5qid6sp//eZXKLq5iNsIE+EQg6EaS8/wsW7E91nXXjpnPhSoMOHXShSVhGRdn8w==", "cpu": [ "arm64" ], @@ -3806,9 +3970,9 @@ } }, "node_modules/@oxc-parser/binding-win32-ia32-msvc": { - "version": "0.133.0", - "resolved": "https://registry.npmjs.org/@oxc-parser/binding-win32-ia32-msvc/-/binding-win32-ia32-msvc-0.133.0.tgz", - "integrity": "sha512-Una1bNYv9zCavQrfnDR9wuZVB3itLjCEH4Oz7i6CwAJN/Xq9b+zbbcxmvdkKvvJt4Ngc/MBmIYlbLo3zS4TQ0A==", + "version": "0.137.0", + "resolved": "https://registry.npmjs.org/@oxc-parser/binding-win32-ia32-msvc/-/binding-win32-ia32-msvc-0.137.0.tgz", + "integrity": "sha512-sQUqym80PFi6McRsIqfJrSu2JrSClEZIXXD+/FjAFoULEKzOPsldIdFBG96xdX8aVMzCNQ9792FPx3MfkEIrFA==", "cpu": [ "ia32" ], @@ -3823,9 +3987,9 @@ } }, "node_modules/@oxc-parser/binding-win32-x64-msvc": { - "version": "0.133.0", - "resolved": "https://registry.npmjs.org/@oxc-parser/binding-win32-x64-msvc/-/binding-win32-x64-msvc-0.133.0.tgz", - "integrity": "sha512-kjBhCiOGSYTwDJQuuZa7a94JbP8htWu7J0X1KwH74kV2K5eYf6eyJRYmkpCDvr0XEL8tMxYI4WU1VekblFCLgg==", + "version": "0.137.0", + "resolved": "https://registry.npmjs.org/@oxc-parser/binding-win32-x64-msvc/-/binding-win32-x64-msvc-0.137.0.tgz", + "integrity": "sha512-2AsevxlvNN4WKxpEn3RtqD5zbqMaXF+T7JXblsP4gVuY+vC9dXS4ED/PwfRCliFqoeisYS3Iro4DHzxr0TEvVA==", "cpu": [ "x64" ], @@ -3840,9 +4004,9 @@ } }, "node_modules/@oxc-project/types": { - "version": "0.133.0", - "resolved": "https://registry.npmjs.org/@oxc-project/types/-/types-0.133.0.tgz", - "integrity": "sha512-KzkdCd6Uxqnf6l3HOw1xfatAlUURA0g14cvBYFyJ5SaNOQbOUvBr9PKArcPcrNIeRsBdgcUzOGrhKveVpvOIGA==", + "version": "0.137.0", + "resolved": "https://registry.npmjs.org/@oxc-project/types/-/types-0.137.0.tgz", + "integrity": "sha512-WT+Gb24i8hmvo85AIv2oEYouEXkRlKAlT9WaCa3TfLgNCN+GhrJOGZuIlMouAh38Qe4QOx26eUOVsq70qXrywA==", "dev": true, "license": "MIT", "funding": { @@ -3850,9 +4014,9 @@ } }, "node_modules/@oxc-resolver/binding-android-arm-eabi": { - "version": "11.20.0", - "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-android-arm-eabi/-/binding-android-arm-eabi-11.20.0.tgz", - "integrity": "sha512-IjfWOXRgJFNdORDl+Uf1aibNgZY2guOD3zmOhx1BGVb/MIiqlFTdmjpQNplSN58lhWehnX4UNqC3QwpUo8pjJg==", + "version": "11.21.3", + "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-android-arm-eabi/-/binding-android-arm-eabi-11.21.3.tgz", + "integrity": "sha512-eNU11A2WNizh04v3uyaJCootrHIaS0B9aHYXvAvVnPNk4xYSjMUjHnhQ6dewPN2MRYDskV85d1N0Aw0WNWhcyg==", "cpu": [ "arm" ], @@ -3864,9 +4028,9 @@ ] }, "node_modules/@oxc-resolver/binding-android-arm64": { - "version": "11.20.0", - "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-android-arm64/-/binding-android-arm64-11.20.0.tgz", - "integrity": "sha512-QqslZAuFQG8Q9xm7JuIn8JUbvywhSBMVhuQHtYW+auirZJloS41oxUUaBXk7uUhZJgp44c5zQLeVvmFaDQB+2Q==", + "version": "11.21.3", + "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-android-arm64/-/binding-android-arm64-11.21.3.tgz", + "integrity": "sha512-8Q+ZjTLvn2dIcWsrmhdrEihm7q+ag/k+mkry7Z+t0QbbHaVxXQfvH9AewyVMh/WrpEKhQ3DDgx9fYbqeCpeOEw==", "cpu": [ "arm64" ], @@ -3878,9 +4042,9 @@ ] }, "node_modules/@oxc-resolver/binding-darwin-arm64": { - "version": "11.20.0", - "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-darwin-arm64/-/binding-darwin-arm64-11.20.0.tgz", - "integrity": "sha512-MUcavykj2ewlR+kc5arpg4tC2RvzJkUxWtNv74pf7lcNk00GpIpN43vXMj+j6r4eMmfZhlb8hueKoIb8e9kAGQ==", + "version": "11.21.3", + "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-darwin-arm64/-/binding-darwin-arm64-11.21.3.tgz", + "integrity": "sha512-wkh0qKZGHXVUDxFw3oA1TXnU2BDYY/r775oJflGeIr8uDPPoN2pk8gijQIzYRT6hoql/lg3+Tx/SaTn9e2/aGg==", "cpu": [ "arm64" ], @@ -3892,9 +4056,9 @@ ] }, "node_modules/@oxc-resolver/binding-darwin-x64": { - "version": "11.20.0", - "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-darwin-x64/-/binding-darwin-x64-11.20.0.tgz", - "integrity": "sha512-BGB16nRUK5Etiv//ihPyzj8Lj1px0mhh4YIfe0FDf045ywknfSm0GEbiRESpr6Q4K82AvnyaRIhhluHByvS4bg==", + "version": "11.21.3", + "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-darwin-x64/-/binding-darwin-x64-11.21.3.tgz", + "integrity": "sha512-HbNc23FAQYbuyDV2vBWMez4u4mrsm5RAkniGZAWqr6lYZ3N4beeqIb776jzwRl8qL2zRhHVXpUj97X0QgogVzg==", "cpu": [ "x64" ], @@ -3906,9 +4070,9 @@ ] }, "node_modules/@oxc-resolver/binding-freebsd-x64": { - "version": "11.20.0", - "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-freebsd-x64/-/binding-freebsd-x64-11.20.0.tgz", - "integrity": "sha512-JZgtePaqj3qmD5XFHJaSLWzHRxQu0LaPkdoM1KJXYADvAaa83ijXHclV3ej3CueeW0wxfIAbGCZVP45J0CA7uQ==", + "version": "11.21.3", + "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-freebsd-x64/-/binding-freebsd-x64-11.21.3.tgz", + "integrity": "sha512-K6xNsTUPEUdfrn0+kbMq5nOUB5w1C5pavPQngt4TM2FpN91lP0PBe2srSpamb4d69O7h86oAi/qWX/kZNRSjkw==", "cpu": [ "x64" ], @@ -3920,9 +4084,9 @@ ] }, "node_modules/@oxc-resolver/binding-linux-arm-gnueabihf": { - "version": "11.20.0", - "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-11.20.0.tgz", - "integrity": "sha512-hOQ/p3ry3v3SchUBXicrrnszaI/UmYzM4wtS4RGfwgVUX7a+HbyQSzJ5aOzu+o6XZkFkS3ZXN4PZAzhOb77OSg==", + "version": "11.21.3", + "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-11.21.3.tgz", + "integrity": "sha512-VcFmOpcpWX1zoEy8M58tR2M9YxM+Z9RuQhqAx5q0CTmrruaP7Gveejg75hzd/5sg5nk9G3aLALEa3hE2FsmmTQ==", "cpu": [ "arm" ], @@ -3934,9 +4098,9 @@ ] }, "node_modules/@oxc-resolver/binding-linux-arm-musleabihf": { - "version": "11.20.0", - "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-linux-arm-musleabihf/-/binding-linux-arm-musleabihf-11.20.0.tgz", - "integrity": "sha512-2ArPksaw0AqeuGBfoS715VF+JvJQAhD2niWgjE5hVO+L+nAfikVQopvngCMX9x4BD8itWoQ3dnikrQyl5Ho5Jg==", + "version": "11.21.3", + "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-linux-arm-musleabihf/-/binding-linux-arm-musleabihf-11.21.3.tgz", + "integrity": "sha512-quVoxFLBy43hWaQbbDtQNRwAX5vX76mv7n64icAtQcJ3eNgVeblqmkupF/hAneNthdqSlnd1sTjb3aQSaDPaCQ==", "cpu": [ "arm" ], @@ -3948,13 +4112,16 @@ ] }, "node_modules/@oxc-resolver/binding-linux-arm64-gnu": { - "version": "11.20.0", - "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-11.20.0.tgz", - "integrity": "sha512-0bJnmYFp62JdZ4nVMDUZ/C58BCZOCcqgKtnUlp7L9Ojf/czIN+3j72YlLPeWLkzlr6SlYvIQA4SGV/HyO0d+qg==", + "version": "11.21.3", + "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-11.21.3.tgz", + "integrity": "sha512-X0AqNZgcD07Q4V3RDK18/vYOj/HQT/FnmEFGYS2jTWqY7JO13ryE3TEs3eAIgUJhBnNkpEaiXqz3VK8M7qQhWQ==", "cpu": [ "arm64" ], "dev": true, + "libc": [ + "glibc" + ], "license": "MIT", "optional": true, "os": [ @@ -3962,13 +4129,16 @@ ] }, "node_modules/@oxc-resolver/binding-linux-arm64-musl": { - "version": "11.20.0", - "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-linux-arm64-musl/-/binding-linux-arm64-musl-11.20.0.tgz", - "integrity": "sha512-wKHHzPKZo7Ufhv/Bt6yxT7FOgnIgW4gwXcJUipkShGp68W3wGVqvr1Sr0fY65lN0Oy6y41+g2kIDvkgZaMMUkw==", + "version": "11.21.3", + "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-linux-arm64-musl/-/binding-linux-arm64-musl-11.21.3.tgz", + "integrity": "sha512-YkaQnaKYdbuaXvRt5Qd0GpbihzVnyfR6z1SpYfIUC6RTu4NF7lDKPjVkYb+jRI2gedVO2rVpN35Y6akG6ud4Lw==", "cpu": [ "arm64" ], "dev": true, + "libc": [ + "musl" + ], "license": "MIT", "optional": true, "os": [ @@ -3976,13 +4146,16 @@ ] }, "node_modules/@oxc-resolver/binding-linux-ppc64-gnu": { - "version": "11.20.0", - "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-11.20.0.tgz", - "integrity": "sha512-RN8goF7Ie0B79L4i4G6OeBocTgSC56vJbQ65VJje+oXnldVpLnOU7j/AQ/dP94TcCS+Yh6WG8u3Qt4ETteXFNQ==", + "version": "11.21.3", + "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-11.21.3.tgz", + "integrity": "sha512-gB9HwhrPiFqUzDeEq+y/CgAijz1YdI6BnXz5GaH2Pa9cWdutchlkGFAiAuGb/PjVQpiK6NFKzFuztxrweoit7A==", "cpu": [ "ppc64" ], "dev": true, + "libc": [ + "glibc" + ], "license": "MIT", "optional": true, "os": [ @@ -3990,13 +4163,16 @@ ] }, "node_modules/@oxc-resolver/binding-linux-riscv64-gnu": { - "version": "11.20.0", - "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-linux-riscv64-gnu/-/binding-linux-riscv64-gnu-11.20.0.tgz", - "integrity": "sha512-5l1yU6/xQEqLZRzxqmMxJfWPslpwCmBsdDGaBvABPehxquCXDC7dd7oraNdKSJUMDXSM7VvVj8H2D2FTjU7oWw==", + "version": "11.21.3", + "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-linux-riscv64-gnu/-/binding-linux-riscv64-gnu-11.21.3.tgz", + "integrity": "sha512-zjDWBlYk8QGv0H8dsPUWqkfjYIIjG2TvspGkzXL0eImbgxtZorA/klKeHyolevoT3Kvbi+1iMr9Lhrh7jf54Og==", "cpu": [ "riscv64" ], "dev": true, + "libc": [ + "glibc" + ], "license": "MIT", "optional": true, "os": [ @@ -4004,13 +4180,16 @@ ] }, "node_modules/@oxc-resolver/binding-linux-riscv64-musl": { - "version": "11.20.0", - "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-linux-riscv64-musl/-/binding-linux-riscv64-musl-11.20.0.tgz", - "integrity": "sha512-xHEvkbgz6UC+A3JOyDQy76LkUaxsNSfIr3/GV8slwZsnuooJiIB34gzJfsyvR4JdCYNUUPsRJc/w/oWkODu+hg==", + "version": "11.21.3", + "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-linux-riscv64-musl/-/binding-linux-riscv64-musl-11.21.3.tgz", + "integrity": "sha512-4UfsQvacV388y1zpXL7C1x1FNYaV52JtuNRiuzrfQA2z1z6ElVrsidkGsrvQ5EgeSq1Pj7kaKqrgGkvFuxJ/tw==", "cpu": [ "riscv64" ], "dev": true, + "libc": [ + "musl" + ], "license": "MIT", "optional": true, "os": [ @@ -4018,13 +4197,16 @@ ] }, "node_modules/@oxc-resolver/binding-linux-s390x-gnu": { - "version": "11.20.0", - "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-11.20.0.tgz", - "integrity": "sha512-aWPDUUmSeyHvlW+SoEUd+JIJsQhVhu6a5tBpDRMu058naPAchTgAVGCFy35zjbnFlt0i8hLWziff6HX0D3LU4g==", + "version": "11.21.3", + "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-11.21.3.tgz", + "integrity": "sha512-b5uH+HKH0MP5mNBYaK75SKsJbw52URqrx2LavYdq6wb0l3ExAG5niYRP9DWUNHdKilpaBVM2bXk9HNWrH3ew7Q==", "cpu": [ "s390x" ], "dev": true, + "libc": [ + "glibc" + ], "license": "MIT", "optional": true, "os": [ @@ -4032,13 +4214,16 @@ ] }, "node_modules/@oxc-resolver/binding-linux-x64-gnu": { - "version": "11.20.0", - "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-linux-x64-gnu/-/binding-linux-x64-gnu-11.20.0.tgz", - "integrity": "sha512-x2YeSimvhJjKLVD8KSu8f/rqU1potcdEMkApIPJqjZWN7c2Fpt4g2X32WDg1p+XDAmyT7nuQGe0vnhvXeLbH+g==", + "version": "11.21.3", + "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-linux-x64-gnu/-/binding-linux-x64-gnu-11.21.3.tgz", + "integrity": "sha512-PjYlmilBpNRh2ntXNYAK3Am5w/nPfEpnU/96iNx7CI8EzAn12J4JRiec63wHJTH31nLoCNxBg/829pN+3CfG3Q==", "cpu": [ "x64" ], "dev": true, + "libc": [ + "glibc" + ], "license": "MIT", "optional": true, "os": [ @@ -4046,13 +4231,16 @@ ] }, "node_modules/@oxc-resolver/binding-linux-x64-musl": { - "version": "11.20.0", - "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-linux-x64-musl/-/binding-linux-x64-musl-11.20.0.tgz", - "integrity": "sha512-kcRLEIxpZefeYfLChjpgFf3ilBzRDZ+yobMrpRsQlSrxuFGtm3U6PMU7AaEpMqo3NfDGVyJJseAjnRLzMFHjwQ==", + "version": "11.21.3", + "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-linux-x64-musl/-/binding-linux-x64-musl-11.21.3.tgz", + "integrity": "sha512-QTBAb7JuHlZ7JUEyM8UiQi2f7m/L4swBhP2TNpYIDc9Wp/wRw1G/8sl6i13aIzQAXH7LKIm294LeOHd0lQR8zA==", "cpu": [ "x64" ], "dev": true, + "libc": [ + "musl" + ], "license": "MIT", "optional": true, "os": [ @@ -4060,9 +4248,9 @@ ] }, "node_modules/@oxc-resolver/binding-openharmony-arm64": { - "version": "11.20.0", - "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-openharmony-arm64/-/binding-openharmony-arm64-11.20.0.tgz", - "integrity": "sha512-HHcfnApSZGtKhTiHqe8OZruOZe5XuFQH5/E0Yhj3u8fnFvzkM4/k6WjacUf4SvA0SPEAbfbgYmVPuo0VX/fIBQ==", + "version": "11.21.3", + "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-openharmony-arm64/-/binding-openharmony-arm64-11.21.3.tgz", + "integrity": "sha512-4j1DFwjwv36ec9kds0jU/ucQ5Ha4ERO/H95BxR5JFf0kqUUAJ1kwII7XhTc1vZrkdJkvLGC9Q2MbpObpum8RBg==", "cpu": [ "arm64" ], @@ -4074,9 +4262,9 @@ ] }, "node_modules/@oxc-resolver/binding-wasm32-wasi": { - "version": "11.20.0", - "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-wasm32-wasi/-/binding-wasm32-wasi-11.20.0.tgz", - "integrity": "sha512-Tn0y1XOFYHNfK1wp1Z5QK8Rcld/bsOwRISQXfqAZ5IBpv8Gz1IvV39fUWNprqNdRizgcvFhOzWwFun2zkJsyBg==", + "version": "11.21.3", + "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-wasm32-wasi/-/binding-wasm32-wasi-11.21.3.tgz", + "integrity": "sha512-i8oluoel5kru/j1WNrjmQSiA3GQ7wvIYVR1IwIoZtKogAhya2iub+ZKIeSIkcJOrnzQ18Tzl/F+kL3fYOxZLvA==", "cpu": [ "wasm32" ], @@ -4084,18 +4272,74 @@ "license": "MIT", "optional": true, "dependencies": { - "@emnapi/core": "1.10.0", - "@emnapi/runtime": "1.10.0", - "@napi-rs/wasm-runtime": "^1.1.4" + "@emnapi/core": "1.11.0", + "@emnapi/runtime": "1.11.0", + "@napi-rs/wasm-runtime": "^1.1.5" }, "engines": { "node": ">=14.0.0" } }, + "node_modules/@oxc-resolver/binding-wasm32-wasi/node_modules/@emnapi/core": { + "version": "1.11.0", + "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.11.0.tgz", + "integrity": "sha512-l9Oo58x0HOP5znGzVhYW9U3e5wVuA4LAZU2AGezTmkhO1CgQRFDhDg4nneHsu/t3WniXg9QrG2nIXL/ZS8ln8Q==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@emnapi/wasi-threads": "1.2.2", + "tslib": "^2.4.0" + } + }, + "node_modules/@oxc-resolver/binding-wasm32-wasi/node_modules/@emnapi/runtime": { + "version": "1.11.0", + "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.11.0.tgz", + "integrity": "sha512-55coeOFKHv1ywEcUXJtWU5f+Jr/W5tZDvZig8DLKSwUN1JpROQ4rk/SNOQiFWmaR/VKF4zuFyW1B8JduOSv6Pg==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@oxc-resolver/binding-wasm32-wasi/node_modules/@emnapi/wasi-threads": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.2.2.tgz", + "integrity": "sha512-c95qOXkHdydNKhscBTebqEC1CVAZpyqOfVfBzQ1qgzyl3gfeldUjIggDbIZgDKsHLgnsM+igH7TJ/eAasaVuMA==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@oxc-resolver/binding-wasm32-wasi/node_modules/@napi-rs/wasm-runtime": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-1.2.0.tgz", + "integrity": "sha512-kDoONqMa+VnZ4vvvu/ZUurpJ4gkZU57e7g69qpNgWhYcZFPUHZM2CEMKm+cG6ufDVALbjMvfmMjFVqaK7uEMnA==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@tybys/wasm-util": "^0.10.3" + }, + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=23.5.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Brooooooklyn" + }, + "peerDependencies": { + "@emnapi/core": "^2.0.0-alpha.3", + "@emnapi/runtime": "^2.0.0-alpha.3" + } + }, "node_modules/@oxc-resolver/binding-win32-arm64-msvc": { - "version": "11.20.0", - "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-11.20.0.tgz", - "integrity": "sha512-qPi25YNPe4YenS8MgsQU2+bIFHxxpLx1LVna2444cEHqNPhNjvWf9zqj4aWE43H9LpAsTmkkAlA3eL5ElBU3mA==", + "version": "11.21.3", + "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-11.21.3.tgz", + "integrity": "sha512-M/8dw8dD6aOs+NlPJax401CZB9I7Aut84isQLgALGGwke4Afvw+/7yYhZb94yXf6t2sPLhQLmSmtSV+2FhsOWg==", "cpu": [ "arm64" ], @@ -4107,9 +4351,9 @@ ] }, "node_modules/@oxc-resolver/binding-win32-x64-msvc": { - "version": "11.20.0", - "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-win32-x64-msvc/-/binding-win32-x64-msvc-11.20.0.tgz", - "integrity": "sha512-Wb14jWEW8huH6It9F6sXd9vrYmIS7pMrgkU6sxpLxkP+9z+wRgs71hUEhRpcn8FOXAFa27FVWfY2tRpbfTzfLw==", + "version": "11.21.3", + "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-win32-x64-msvc/-/binding-win32-x64-msvc-11.21.3.tgz", + "integrity": "sha512-H7BCt/VnS9hnmMp42eGhZ99izSCRvlnWwy/N71K1/J8QoExwY4262Z8QiEkMDtduRJrztayDxETTckmUuAVL9Q==", "cpu": [ "x64" ], @@ -4141,13 +4385,13 @@ } }, "node_modules/@playwright/test": { - "version": "1.61.0", - "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.61.0.tgz", - "integrity": "sha512-cKA5B6lpFEMyMGjxF54QihfYpB4FkEGH+qZhtArDEG+wezQAJY8Pq6C7T1SjWz+FFzt3TbyoXBQYk/0292TdJA==", + "version": "1.61.1", + "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.61.1.tgz", + "integrity": "sha512-8nKv6+0RJSL9FE4jYOEGXnPeM/Hg12qZpmqzZjRh3qM0Y7c3z1mrOTfFLids72RDQYVh9WpLEfR5WdpNX4fkig==", "dev": true, "license": "Apache-2.0", "dependencies": { - "playwright": "1.61.0" + "playwright": "1.61.1" }, "bin": { "playwright": "cli.js" @@ -11301,9 +11545,9 @@ "dev": true }, "node_modules/@tybys/wasm-util": { - "version": "0.10.2", - "resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.10.2.tgz", - "integrity": "sha512-RoBvJ2X0wuKlWFIjrwffGw1IqZHKQqzIchKaadZZfnNpsAYp2mM0h36JtPCjNDAHGgYez/15uMBpfGwchhiMgg==", + "version": "0.10.3", + "resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.10.3.tgz", + "integrity": "sha512-F3fo1MYrRJYL3zER0OUOmkutjr1Vp23m7OsSgp7nq4SP6OqX6C/56XFIPAl5bt3zaBRjmW7SGz3u/6LwFpYcOg==", "dev": true, "license": "MIT", "optional": true, @@ -11391,11 +11635,12 @@ } }, "node_modules/@types/cookie-parser": { - "version": "1.4.3", - "resolved": "https://registry.npmjs.org/@types/cookie-parser/-/cookie-parser-1.4.3.tgz", - "integrity": "sha512-CqSKwFwefj4PzZ5n/iwad/bow2hTCh0FlNAeWLtQM3JA/NX/iYagIpWG2cf1bQKQ2c9gU2log5VUCrn7LDOs0w==", + "version": "1.4.10", + "resolved": "https://registry.npmjs.org/@types/cookie-parser/-/cookie-parser-1.4.10.tgz", + "integrity": "sha512-B4xqkqfZ8Wek+rCOeRxsjMS9OgvzebEzzLYw7NHYuvzb7IdxOkI0ZHGgeEBX4PUM7QGVvNSK60T3OvWj3YfBRg==", "dev": true, - "dependencies": { + "license": "MIT", + "peerDependencies": { "@types/express": "*" } }, @@ -11416,9 +11661,9 @@ } }, "node_modules/@types/debug": { - "version": "4.1.12", - "resolved": "https://registry.npmjs.org/@types/debug/-/debug-4.1.12.tgz", - "integrity": "sha512-vIChWdVG3LG1SMxEvI/AK+FWJthlrqlTu7fbrlywTkkaONwk/UAGaULXRlf8vkzFBLVm0zkMdCquhL5aOjhXPQ==", + "version": "4.1.13", + "resolved": "https://registry.npmjs.org/@types/debug/-/debug-4.1.13.tgz", + "integrity": "sha512-KSVgmQmzMwPlmtljOomayoR89W4FynCAi3E8PPs7vmDVPe84hT+vGPKkJfThkmXs0x0jAaa9U8uW8bbfyS2fWw==", "license": "MIT", "dependencies": { "@types/ms": "*" @@ -11443,9 +11688,9 @@ } }, "node_modules/@types/express-serve-static-core": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-5.1.1.tgz", - "integrity": "sha512-v4zIMr/cX7/d2BpAEX3KNKL/JrT1s43s96lLvvdTmza1oEvDudCqK9aF/djc/SWgy8Yh0h30TZx5VpzqFCxk5A==", + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-5.1.2.tgz", + "integrity": "sha512-d3KvEXBSo/lOAMc2u6fkyDHBvetBHeqD7wm/AcXfLpSOQwlmG9D/aQ0SFswVjv05p7ullQS7Mjohj6/VdbZuTg==", "license": "MIT", "dependencies": { "@types/node": "*", @@ -11512,10 +11757,11 @@ "integrity": "sha512-/K3ds8TRAfBvi5vfjuz8y6+GiAYBZ0x4tXv1Av6CWBWn0IlADc+ZX9pMq7oU0fNQPnBwIZl3rmeLp6SBApbxSQ==" }, "node_modules/@types/istanbul-lib-coverage": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.4.tgz", - "integrity": "sha512-z/QT1XN4K4KYuslS23k62yDIDLwLFkzxOuMplDtObz0+y7VqJCaO2o+SPwHCvLFZh7xazvvoor2tA/hPz9ee7g==", - "dev": true + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.6.tgz", + "integrity": "sha512-2QF/t/auWm0lsy8XtKVPG19v3sSOQlJe/YHZgfjb/KBBHOGSV+J2q/S671rcq9uTBrLAXmZpqJiaQbMT+zNU1w==", + "dev": true, + "license": "MIT" }, "node_modules/@types/istanbul-lib-report": { "version": "3.0.0", @@ -11527,10 +11773,11 @@ } }, "node_modules/@types/istanbul-reports": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.1.tgz", - "integrity": "sha512-c3mAZEuK0lvBp8tmuL74XRKn1+y2dcwOUpH7x4WrF6gk1GIgiluDRgMYQtw2OFcBvAJWlt6ASU3tSqxp0Uu0Aw==", + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.4.tgz", + "integrity": "sha512-pk2B1NWalF9toCRu6gjBzR69syFjP4Od8WRAX+0mmf9lAjCRicLOWc+ZrxZHx/0XRjotgkF9t6iaMJ+aXcOdZQ==", "dev": true, + "license": "MIT", "dependencies": { "@types/istanbul-lib-report": "*" } @@ -11546,10 +11793,11 @@ } }, "node_modules/@types/js-yaml": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/@types/js-yaml/-/js-yaml-4.0.5.tgz", - "integrity": "sha512-FhpRzf927MNQdRZP0J5DLIdTXhjLYzeUTmLAu69mnVksLH9CJY3IuSeEgbKUki7GQZm0WqDkGzyxju2EZGD2wA==", - "dev": true + "version": "4.0.9", + "resolved": "https://registry.npmjs.org/@types/js-yaml/-/js-yaml-4.0.9.tgz", + "integrity": "sha512-k4MGaQl5TGo/iipqb2UDG2UwjXziSWkh0uysQelTlJpX1qGlpUZYm8PnO4DxG1qBomtJUdYJ6qR6xdIah10JLg==", + "dev": true, + "license": "MIT" }, "node_modules/@types/json-schema": { "version": "7.0.15", @@ -11577,10 +11825,11 @@ } }, "node_modules/@types/lodash": { - "version": "4.14.198", - "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.198.tgz", - "integrity": "sha512-trNJ/vtMZYMLhfN45uLq4ShQSw0/S7xCTLLVM+WM1rmFpba/VS42jVUgaO3w/NOLiWR/09lnYk0yMaA/atdIsg==", - "dev": true + "version": "4.17.24", + "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.17.24.tgz", + "integrity": "sha512-gIW7lQLZbue7lRSWEFql49QJJWThrTFFeIMJdp3eH4tKoxm1OvEPg02rm4wCCSHS0cL3/Fizimb35b7k8atwsQ==", + "dev": true, + "license": "MIT" }, "node_modules/@types/long": { "version": "4.0.2", @@ -11593,10 +11842,11 @@ "integrity": "sha512-YATxVxgRqNH6nHEIsvg6k2Boc1JHI9ZbH5iWFFv/MTkchz3b1ieGDa5T0a9RznNdI0KhVbdbWSN+KWWrQZRxTw==" }, "node_modules/@types/morgan": { - "version": "1.9.4", - "resolved": "https://registry.npmjs.org/@types/morgan/-/morgan-1.9.4.tgz", - "integrity": "sha512-cXoc4k+6+YAllH3ZHmx4hf7La1dzUk6keTR4bF4b4Sc0mZxU/zK4wO7l+ZzezXm/jkYj/qC+uYGZrarZdIVvyQ==", + "version": "1.9.10", + "resolved": "https://registry.npmjs.org/@types/morgan/-/morgan-1.9.10.tgz", + "integrity": "sha512-sS4A1zheMvsADRVfT0lYbJ4S9lmsey8Zo2F7cnbYjWHP67Q0AwMYuuzLlkIM2N8gAbb9cubhIVFwcIN2XyYCkA==", "dev": true, + "license": "MIT", "dependencies": { "@types/node": "*" } @@ -11607,9 +11857,13 @@ "integrity": "sha512-iiUgKzV9AuaEkZqkOLDIvlQiL6ltuZd9tGcW3gwpnX8JbuiuhFlEGmmFXEXkN50Cvq7Os88IY2v0dkDqXYWVgA==" }, "node_modules/@types/node": { - "version": "18.17.3", - "resolved": "https://registry.npmjs.org/@types/node/-/node-18.17.3.tgz", - "integrity": "sha512-2x8HWtFk0S99zqVQABU9wTpr8wPoaDHZUcAkoTKH+nL7kPv3WUI9cRi/Kk5Mz4xdqXSqTkKP7IWNoQQYCnDsTA==" + "version": "18.19.130", + "resolved": "https://registry.npmjs.org/@types/node/-/node-18.19.130.tgz", + "integrity": "sha512-GRaXQx6jGfL8sKfaIDD6OupbIHBr9jv7Jnaml9tB7l4v068PAOXqfcujMMo5PhbIs6ggR1XODELqahT2R8v0fg==", + "license": "MIT", + "dependencies": { + "undici-types": "~5.26.4" + } }, "node_modules/@types/passport": { "version": "1.0.17", @@ -11671,10 +11925,11 @@ } }, "node_modules/@types/stack-utils": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.1.tgz", - "integrity": "sha512-Hl219/BT5fLAaz6NDkSuhzasy49dwQS/DSdu4MdggFB8zcXv7vflBI3xp7FEmkmdDkBUI2bPUNeMttp2knYdxw==", - "dev": true + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.3.tgz", + "integrity": "sha512-9aEbYZ3TbYMznPdcdr3SmIrLXwC/AKZXQeCf9Pgao5CKb8CyHuEX5jzWPTkvregvhRJHcpRO6BFoGW9ycaOkYw==", + "dev": true, + "license": "MIT" }, "node_modules/@types/superagent": { "version": "4.1.18", @@ -11696,10 +11951,11 @@ } }, "node_modules/@types/validator": { - "version": "13.9.0", - "resolved": "https://registry.npmjs.org/@types/validator/-/validator-13.9.0.tgz", - "integrity": "sha512-NclP0IbzHj/4tJZKFqKh8E7kZdgss+MCUYV9G+TLltFfDA4lFgE4PKPpDIyS2FlcdANIfSx273emkupvChigbw==", - "dev": true + "version": "13.15.10", + "resolved": "https://registry.npmjs.org/@types/validator/-/validator-13.15.10.tgz", + "integrity": "sha512-T8L6i7wCuyoK8A/ZeLYt1+q0ty3Zb9+qbSSvrIVitzT3YjZqkTZ40IbRsPanlB4h1QB3JVL1SYCdR6ngtFYcuA==", + "dev": true, + "license": "MIT" }, "node_modules/@types/xml-encryption": { "version": "1.2.4", @@ -11736,17 +11992,17 @@ "dev": true }, "node_modules/@typescript-eslint/eslint-plugin": { - "version": "8.59.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.59.0.tgz", - "integrity": "sha512-HyAZtpdkgZwpq8Sz3FSUvCR4c+ScbuWa9AksK2Jweub7w4M3yTz4O11AqVJzLYjy/B9ZWPyc81I+mOdJU/bDQw==", + "version": "8.65.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.65.0.tgz", + "integrity": "sha512-IEgob78X12rHpUmtcwFsXhZdVGJtwTVP8FiCLZkR6GlYVrl2PcuB+KhCE5BlVC/eQpQnu8WXRtkHZuPar+gCRA==", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/regexpp": "^4.12.2", - "@typescript-eslint/scope-manager": "8.59.0", - "@typescript-eslint/type-utils": "8.59.0", - "@typescript-eslint/utils": "8.59.0", - "@typescript-eslint/visitor-keys": "8.59.0", + "@typescript-eslint/scope-manager": "8.65.0", + "@typescript-eslint/type-utils": "8.65.0", + "@typescript-eslint/utils": "8.65.0", + "@typescript-eslint/visitor-keys": "8.65.0", "ignore": "^7.0.5", "natural-compare": "^1.4.0", "ts-api-utils": "^2.5.0" @@ -11759,20 +12015,20 @@ "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "@typescript-eslint/parser": "^8.59.0", + "@typescript-eslint/parser": "^8.65.0", "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", "typescript": ">=4.8.4 <6.1.0" } }, "node_modules/@typescript-eslint/eslint-plugin/node_modules/@typescript-eslint/scope-manager": { - "version": "8.59.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.59.0.tgz", - "integrity": "sha512-UzR16Ut8IpA3Mc4DbgAShlPPkVm8xXMWafXxB0BocaVRHs8ZGakAxGRskF7FId3sdk9lgGD73GSFaWmWFDE4dg==", + "version": "8.65.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.65.0.tgz", + "integrity": "sha512-Esbl8OSYiVxBokYgWPf7VVWg/BE798wXhimnn9ML9Pt5qoDf8bfQlgjlKXR/k98+AcNzlLKYrpCcrcuZ9DZLgg==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.59.0", - "@typescript-eslint/visitor-keys": "8.59.0" + "@typescript-eslint/types": "8.65.0", + "@typescript-eslint/visitor-keys": "8.65.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -11783,9 +12039,9 @@ } }, "node_modules/@typescript-eslint/eslint-plugin/node_modules/@typescript-eslint/types": { - "version": "8.59.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.59.0.tgz", - "integrity": "sha512-nLzdsT1gdOgFxxxwrlNVUBzSNBEEHJ86bblmk4QAS6stfig7rcJzWKqCyxFy3YRRHXDWEkb2NralA1nOYkkm/A==", + "version": "8.65.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.65.0.tgz", + "integrity": "sha512-JSSwWNy+H0E/01jJEM+hrX6N0OFDzFzeIhHFSAS01tlVaevpG8cFyYRPhS5yjGOvBUx3sqQHVMjCL1CAZZMxBg==", "dev": true, "license": "MIT", "engines": { @@ -11797,13 +12053,13 @@ } }, "node_modules/@typescript-eslint/eslint-plugin/node_modules/@typescript-eslint/visitor-keys": { - "version": "8.59.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.59.0.tgz", - "integrity": "sha512-/uejZt4dSere1bx12WLlPfv8GktzcaDtuJ7s42/HEZ5zGj9oxRaD4bj7qwSunXkf+pbAhFt2zjpHYUiT5lHf0Q==", + "version": "8.65.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.65.0.tgz", + "integrity": "sha512-8C71BQkGjiMmXtop7pHVJu1l2NNShFdkCyD6a2ezzs5vU/L3LRtb69EtcteFwz0mYMPzIgOw0n6OV4VBUWZd7A==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.59.0", + "@typescript-eslint/types": "8.65.0", "eslint-visitor-keys": "^5.0.0" }, "engines": { @@ -11851,16 +12107,16 @@ } }, "node_modules/@typescript-eslint/parser": { - "version": "8.59.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.59.0.tgz", - "integrity": "sha512-TI1XGwKbDpo9tRW8UDIXCOeLk55qe9ZFGs8MTKU6/M08HWTw52DD/IYhfQtOEhEdPhLMT26Ka/x7p70nd3dzDg==", + "version": "8.65.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.65.0.tgz", + "integrity": "sha512-CZ4nMxWwgu1HEEFNkeaCptra9QCtkmKdgf3sWh1rl1trIhmxLilgTV4cwcbQ4wemnT4sWQN8CaKOmdYx+g2gMA==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/scope-manager": "8.59.0", - "@typescript-eslint/types": "8.59.0", - "@typescript-eslint/typescript-estree": "8.59.0", - "@typescript-eslint/visitor-keys": "8.59.0", + "@typescript-eslint/scope-manager": "8.65.0", + "@typescript-eslint/types": "8.65.0", + "@typescript-eslint/typescript-estree": "8.65.0", + "@typescript-eslint/visitor-keys": "8.65.0", "debug": "^4.4.3" }, "engines": { @@ -11875,67 +12131,28 @@ "typescript": ">=4.8.4 <6.1.0" } }, - "node_modules/@typescript-eslint/parser/node_modules/@typescript-eslint/project-service": { - "version": "8.59.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.59.0.tgz", - "integrity": "sha512-Lw5ITrR5s5TbC19YSvlr63ZfLaJoU6vtKTHyB0GQOpX0W7d5/Ir6vUahWi/8Sps/nOukZQ0IB3SmlxZnjaKVnw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@typescript-eslint/tsconfig-utils": "^8.59.0", - "@typescript-eslint/types": "^8.59.0", - "debug": "^4.4.3" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "typescript": ">=4.8.4 <6.1.0" - } - }, "node_modules/@typescript-eslint/parser/node_modules/@typescript-eslint/scope-manager": { - "version": "8.59.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.59.0.tgz", - "integrity": "sha512-UzR16Ut8IpA3Mc4DbgAShlPPkVm8xXMWafXxB0BocaVRHs8ZGakAxGRskF7FId3sdk9lgGD73GSFaWmWFDE4dg==", + "version": "8.65.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.65.0.tgz", + "integrity": "sha512-Esbl8OSYiVxBokYgWPf7VVWg/BE798wXhimnn9ML9Pt5qoDf8bfQlgjlKXR/k98+AcNzlLKYrpCcrcuZ9DZLgg==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.59.0", - "@typescript-eslint/visitor-keys": "8.59.0" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + "@typescript-eslint/types": "8.65.0", + "@typescript-eslint/visitor-keys": "8.65.0" }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, - "node_modules/@typescript-eslint/parser/node_modules/@typescript-eslint/tsconfig-utils": { - "version": "8.59.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.59.0.tgz", - "integrity": "sha512-91Sbl3s4Kb3SybliIY6muFBmHVv+pYXfybC4Oolp3dvk8BvIE3wOPc+403CWIT7mJNkfQRGtdqghzs2+Z91Tqg==", - "dev": true, - "license": "MIT", "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "typescript": ">=4.8.4 <6.1.0" } }, "node_modules/@typescript-eslint/parser/node_modules/@typescript-eslint/types": { - "version": "8.59.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.59.0.tgz", - "integrity": "sha512-nLzdsT1gdOgFxxxwrlNVUBzSNBEEHJ86bblmk4QAS6stfig7rcJzWKqCyxFy3YRRHXDWEkb2NralA1nOYkkm/A==", + "version": "8.65.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.65.0.tgz", + "integrity": "sha512-JSSwWNy+H0E/01jJEM+hrX6N0OFDzFzeIhHFSAS01tlVaevpG8cFyYRPhS5yjGOvBUx3sqQHVMjCL1CAZZMxBg==", "dev": true, "license": "MIT", "engines": { @@ -11947,16 +12164,16 @@ } }, "node_modules/@typescript-eslint/parser/node_modules/@typescript-eslint/typescript-estree": { - "version": "8.59.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.59.0.tgz", - "integrity": "sha512-O9Re9P1BmBLFJyikRbQpLku/QA3/AueZNO9WePLBwQrvkixTmDe8u76B6CYUAITRl/rHawggEqUGn5QIkVRLMw==", + "version": "8.65.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.65.0.tgz", + "integrity": "sha512-JboAE2swaYt4tb1fHhHTABE2K+OLy09XfcTbhnk4Pw96f9dd2e9iYsJ28gBggHlo5z5x1rkyWvcPoTuNTd4oGg==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/project-service": "8.59.0", - "@typescript-eslint/tsconfig-utils": "8.59.0", - "@typescript-eslint/types": "8.59.0", - "@typescript-eslint/visitor-keys": "8.59.0", + "@typescript-eslint/project-service": "8.65.0", + "@typescript-eslint/tsconfig-utils": "8.65.0", + "@typescript-eslint/types": "8.65.0", + "@typescript-eslint/visitor-keys": "8.65.0", "debug": "^4.4.3", "minimatch": "^10.2.2", "semver": "^7.7.3", @@ -11975,13 +12192,13 @@ } }, "node_modules/@typescript-eslint/parser/node_modules/@typescript-eslint/visitor-keys": { - "version": "8.59.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.59.0.tgz", - "integrity": "sha512-/uejZt4dSere1bx12WLlPfv8GktzcaDtuJ7s42/HEZ5zGj9oxRaD4bj7qwSunXkf+pbAhFt2zjpHYUiT5lHf0Q==", + "version": "8.65.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.65.0.tgz", + "integrity": "sha512-8C71BQkGjiMmXtop7pHVJu1l2NNShFdkCyD6a2ezzs5vU/L3LRtb69EtcteFwz0mYMPzIgOw0n6OV4VBUWZd7A==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.59.0", + "@typescript-eslint/types": "8.65.0", "eslint-visitor-keys": "^5.0.0" }, "engines": { @@ -12003,16 +12220,16 @@ } }, "node_modules/@typescript-eslint/parser/node_modules/brace-expansion": { - "version": "5.0.6", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.6.tgz", - "integrity": "sha512-kLpxurY4Z4r9sgMsyG0Z9uzsBlgiU/EFKhj/h91/8yHu0edo7XuixOIH3VcJ8kkxs6/jPzoI6U9Vj3WqbMQ94g==", + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.8.tgz", + "integrity": "sha512-JZyDyq3D4AUifKTPOB7DELf6XsB3WdPuNxCtob1vFXPsSXhdAiHBWJ/tJ8HAc9aH84BK+5JFZLNkJKx3G9kzQg==", "dev": true, "license": "MIT", "dependencies": { "balanced-match": "^4.0.2" }, "engines": { - "node": "18 || 20 || >=22" + "node": "20 || >=22" } }, "node_modules/@typescript-eslint/parser/node_modules/eslint-visitor-keys": { @@ -12029,13 +12246,13 @@ } }, "node_modules/@typescript-eslint/parser/node_modules/minimatch": { - "version": "10.2.5", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.5.tgz", - "integrity": "sha512-MULkVLfKGYDFYejP07QOurDLLQpcjk7Fw+7jXS2R2czRQzR56yHRveU5NDJEOviH+hETZKSkIk5c+T23GjFUMg==", + "version": "10.2.6", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.6.tgz", + "integrity": "sha512-vpLQEs+VLCr1nU0BXS07maYoFwlDAH0gngQuuttxIwutDFEMHq2blX+8vpgxDdK3J1PwjCJiep77OitTZ4Ll1A==", "dev": true, "license": "BlueOak-1.0.0", "dependencies": { - "brace-expansion": "^5.0.5" + "brace-expansion": "^5.0.8" }, "engines": { "node": "18 || 20 || >=22" @@ -12058,14 +12275,14 @@ } }, "node_modules/@typescript-eslint/project-service": { - "version": "8.57.2", - "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.57.2.tgz", - "integrity": "sha512-FuH0wipFywXRTHf+bTTjNyuNQQsQC3qh/dYzaM4I4W0jrCqjCVuUh99+xd9KamUfmCGPvbO8NDngo/vsnNVqgw==", + "version": "8.65.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.65.0.tgz", + "integrity": "sha512-SxnPhbTsGahizDgbu7oqFH/xVtzIqMd/s+WtnSxNxJZJpLbdT5IPdzg8EZxO3+PoKahXmwJLeNQOpKJb3/bi7Q==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/tsconfig-utils": "^8.57.2", - "@typescript-eslint/types": "^8.57.2", + "@typescript-eslint/tsconfig-utils": "^8.65.0", + "@typescript-eslint/types": "^8.65.0", "debug": "^4.4.3" }, "engines": { @@ -12076,13 +12293,13 @@ "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "typescript": ">=4.8.4 <6.0.0" + "typescript": ">=4.8.4 <6.1.0" } }, "node_modules/@typescript-eslint/project-service/node_modules/@typescript-eslint/types": { - "version": "8.57.2", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.57.2.tgz", - "integrity": "sha512-/iZM6FnM4tnx9csuTxspMW4BOSegshwX5oBDznJ7S4WggL7Vczz5d2W11ecc4vRrQMQHXRSxzrCsyG5EsPPTbA==", + "version": "8.65.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.65.0.tgz", + "integrity": "sha512-JSSwWNy+H0E/01jJEM+hrX6N0OFDzFzeIhHFSAS01tlVaevpG8cFyYRPhS5yjGOvBUx3sqQHVMjCL1CAZZMxBg==", "dev": true, "license": "MIT", "engines": { @@ -12112,9 +12329,9 @@ } }, "node_modules/@typescript-eslint/tsconfig-utils": { - "version": "8.57.2", - "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.57.2.tgz", - "integrity": "sha512-3Lm5DSM+DCowsUOJC+YqHHnKEfFh5CoGkj5Z31NQSNF4l5wdOwqGn99wmwN/LImhfY3KJnmordBq/4+VDe2eKw==", + "version": "8.65.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.65.0.tgz", + "integrity": "sha512-j6GzGqCiRdA7Qhur2VVmKZAkBLfnHFQfx4TaJGL9RMveZqCo48jSHHO0DTgizEnGhtWnqmbtCUSrqSkdiY/0Hg==", "dev": true, "license": "MIT", "engines": { @@ -12125,19 +12342,19 @@ "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "typescript": ">=4.8.4 <6.0.0" + "typescript": ">=4.8.4 <6.1.0" } }, "node_modules/@typescript-eslint/type-utils": { - "version": "8.59.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.59.0.tgz", - "integrity": "sha512-3TRiZaQSltGqGeNrJzzr1+8YcEobKH9rHnqIp/1psfKFmhRQDNMGP5hBufanYTGznwShzVLs3Mz+gDN7HkWfXg==", + "version": "8.65.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.65.0.tgz", + "integrity": "sha512-YjaZ7PRI5qY7ax2L3PbvX0rRyGtipAReCWs0mhhDBHjH/vl0g0BonaGXrKdKpMbIIsMIwDgbk/xzkBTyAltS5g==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.59.0", - "@typescript-eslint/typescript-estree": "8.59.0", - "@typescript-eslint/utils": "8.59.0", + "@typescript-eslint/types": "8.65.0", + "@typescript-eslint/typescript-estree": "8.65.0", + "@typescript-eslint/utils": "8.65.0", "debug": "^4.4.3", "ts-api-utils": "^2.5.0" }, @@ -12153,70 +12370,31 @@ "typescript": ">=4.8.4 <6.1.0" } }, - "node_modules/@typescript-eslint/type-utils/node_modules/@typescript-eslint/project-service": { - "version": "8.59.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.59.0.tgz", - "integrity": "sha512-Lw5ITrR5s5TbC19YSvlr63ZfLaJoU6vtKTHyB0GQOpX0W7d5/Ir6vUahWi/8Sps/nOukZQ0IB3SmlxZnjaKVnw==", + "node_modules/@typescript-eslint/type-utils/node_modules/@typescript-eslint/types": { + "version": "8.65.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.65.0.tgz", + "integrity": "sha512-JSSwWNy+H0E/01jJEM+hrX6N0OFDzFzeIhHFSAS01tlVaevpG8cFyYRPhS5yjGOvBUx3sqQHVMjCL1CAZZMxBg==", "dev": true, "license": "MIT", - "dependencies": { - "@typescript-eslint/tsconfig-utils": "^8.59.0", - "@typescript-eslint/types": "^8.59.0", - "debug": "^4.4.3" - }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "typescript": ">=4.8.4 <6.1.0" } }, - "node_modules/@typescript-eslint/type-utils/node_modules/@typescript-eslint/tsconfig-utils": { - "version": "8.59.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.59.0.tgz", - "integrity": "sha512-91Sbl3s4Kb3SybliIY6muFBmHVv+pYXfybC4Oolp3dvk8BvIE3wOPc+403CWIT7mJNkfQRGtdqghzs2+Z91Tqg==", - "dev": true, - "license": "MIT", - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "typescript": ">=4.8.4 <6.1.0" - } - }, - "node_modules/@typescript-eslint/type-utils/node_modules/@typescript-eslint/types": { - "version": "8.59.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.59.0.tgz", - "integrity": "sha512-nLzdsT1gdOgFxxxwrlNVUBzSNBEEHJ86bblmk4QAS6stfig7rcJzWKqCyxFy3YRRHXDWEkb2NralA1nOYkkm/A==", - "dev": true, - "license": "MIT", - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, - "node_modules/@typescript-eslint/type-utils/node_modules/@typescript-eslint/typescript-estree": { - "version": "8.59.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.59.0.tgz", - "integrity": "sha512-O9Re9P1BmBLFJyikRbQpLku/QA3/AueZNO9WePLBwQrvkixTmDe8u76B6CYUAITRl/rHawggEqUGn5QIkVRLMw==", + "node_modules/@typescript-eslint/type-utils/node_modules/@typescript-eslint/typescript-estree": { + "version": "8.65.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.65.0.tgz", + "integrity": "sha512-JboAE2swaYt4tb1fHhHTABE2K+OLy09XfcTbhnk4Pw96f9dd2e9iYsJ28gBggHlo5z5x1rkyWvcPoTuNTd4oGg==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/project-service": "8.59.0", - "@typescript-eslint/tsconfig-utils": "8.59.0", - "@typescript-eslint/types": "8.59.0", - "@typescript-eslint/visitor-keys": "8.59.0", + "@typescript-eslint/project-service": "8.65.0", + "@typescript-eslint/tsconfig-utils": "8.65.0", + "@typescript-eslint/types": "8.65.0", + "@typescript-eslint/visitor-keys": "8.65.0", "debug": "^4.4.3", "minimatch": "^10.2.2", "semver": "^7.7.3", @@ -12235,13 +12413,13 @@ } }, "node_modules/@typescript-eslint/type-utils/node_modules/@typescript-eslint/visitor-keys": { - "version": "8.59.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.59.0.tgz", - "integrity": "sha512-/uejZt4dSere1bx12WLlPfv8GktzcaDtuJ7s42/HEZ5zGj9oxRaD4bj7qwSunXkf+pbAhFt2zjpHYUiT5lHf0Q==", + "version": "8.65.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.65.0.tgz", + "integrity": "sha512-8C71BQkGjiMmXtop7pHVJu1l2NNShFdkCyD6a2ezzs5vU/L3LRtb69EtcteFwz0mYMPzIgOw0n6OV4VBUWZd7A==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.59.0", + "@typescript-eslint/types": "8.65.0", "eslint-visitor-keys": "^5.0.0" }, "engines": { @@ -12263,16 +12441,16 @@ } }, "node_modules/@typescript-eslint/type-utils/node_modules/brace-expansion": { - "version": "5.0.6", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.6.tgz", - "integrity": "sha512-kLpxurY4Z4r9sgMsyG0Z9uzsBlgiU/EFKhj/h91/8yHu0edo7XuixOIH3VcJ8kkxs6/jPzoI6U9Vj3WqbMQ94g==", + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.8.tgz", + "integrity": "sha512-JZyDyq3D4AUifKTPOB7DELf6XsB3WdPuNxCtob1vFXPsSXhdAiHBWJ/tJ8HAc9aH84BK+5JFZLNkJKx3G9kzQg==", "dev": true, "license": "MIT", "dependencies": { "balanced-match": "^4.0.2" }, "engines": { - "node": "18 || 20 || >=22" + "node": "20 || >=22" } }, "node_modules/@typescript-eslint/type-utils/node_modules/eslint-visitor-keys": { @@ -12289,13 +12467,13 @@ } }, "node_modules/@typescript-eslint/type-utils/node_modules/minimatch": { - "version": "10.2.5", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.5.tgz", - "integrity": "sha512-MULkVLfKGYDFYejP07QOurDLLQpcjk7Fw+7jXS2R2czRQzR56yHRveU5NDJEOviH+hETZKSkIk5c+T23GjFUMg==", + "version": "10.2.6", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.6.tgz", + "integrity": "sha512-vpLQEs+VLCr1nU0BXS07maYoFwlDAH0gngQuuttxIwutDFEMHq2blX+8vpgxDdK3J1PwjCJiep77OitTZ4Ll1A==", "dev": true, "license": "BlueOak-1.0.0", "dependencies": { - "brace-expansion": "^5.0.5" + "brace-expansion": "^5.0.8" }, "engines": { "node": "18 || 20 || >=22" @@ -12361,9 +12539,9 @@ } }, "node_modules/@typescript-eslint/typescript-estree/node_modules/brace-expansion": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.3.tgz", - "integrity": "sha512-MCV/fYJEbqx68aE58kv2cA/kiky1G8vux3OR6/jbS+jIMe/6fJWa0DTzJU7dqijOWYwHi1t29FlfYI9uytqlpA==", + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.1.3.tgz", + "integrity": "sha512-DRdx5neNsG/QXbniLFWi2YmC/68oeOOmKz6zOjVk6ZS1ZLXgLIKqVEc6hWsmkjBbgii0SwaBTcJ5XKj5gzY/4A==", "dev": true, "license": "MIT", "dependencies": { @@ -12387,16 +12565,16 @@ } }, "node_modules/@typescript-eslint/utils": { - "version": "8.59.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.59.0.tgz", - "integrity": "sha512-I1R/K7V07XsMJ12Oaxg/O9GfrysGTmCRhvZJBv0RE0NcULMzjqVpR5kRRQjHsz3J/bElU7HwCO7zkqL+MSUz+g==", + "version": "8.65.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.65.0.tgz", + "integrity": "sha512-gXiwIHsYreboxeJucHKPvgwl7dXt50mF8s1/c00cP/WoVTyWKFdtfhRWwZiXYFU5H2O8vVoSLNrexFZjYS/SGA==", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/eslint-utils": "^4.9.1", - "@typescript-eslint/scope-manager": "8.59.0", - "@typescript-eslint/types": "8.59.0", - "@typescript-eslint/typescript-estree": "8.59.0" + "@typescript-eslint/scope-manager": "8.65.0", + "@typescript-eslint/types": "8.65.0", + "@typescript-eslint/typescript-estree": "8.65.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -12410,37 +12588,15 @@ "typescript": ">=4.8.4 <6.1.0" } }, - "node_modules/@typescript-eslint/utils/node_modules/@typescript-eslint/project-service": { - "version": "8.59.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.59.0.tgz", - "integrity": "sha512-Lw5ITrR5s5TbC19YSvlr63ZfLaJoU6vtKTHyB0GQOpX0W7d5/Ir6vUahWi/8Sps/nOukZQ0IB3SmlxZnjaKVnw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@typescript-eslint/tsconfig-utils": "^8.59.0", - "@typescript-eslint/types": "^8.59.0", - "debug": "^4.4.3" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "typescript": ">=4.8.4 <6.1.0" - } - }, "node_modules/@typescript-eslint/utils/node_modules/@typescript-eslint/scope-manager": { - "version": "8.59.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.59.0.tgz", - "integrity": "sha512-UzR16Ut8IpA3Mc4DbgAShlPPkVm8xXMWafXxB0BocaVRHs8ZGakAxGRskF7FId3sdk9lgGD73GSFaWmWFDE4dg==", + "version": "8.65.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.65.0.tgz", + "integrity": "sha512-Esbl8OSYiVxBokYgWPf7VVWg/BE798wXhimnn9ML9Pt5qoDf8bfQlgjlKXR/k98+AcNzlLKYrpCcrcuZ9DZLgg==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.59.0", - "@typescript-eslint/visitor-keys": "8.59.0" + "@typescript-eslint/types": "8.65.0", + "@typescript-eslint/visitor-keys": "8.65.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -12450,27 +12606,10 @@ "url": "https://opencollective.com/typescript-eslint" } }, - "node_modules/@typescript-eslint/utils/node_modules/@typescript-eslint/tsconfig-utils": { - "version": "8.59.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.59.0.tgz", - "integrity": "sha512-91Sbl3s4Kb3SybliIY6muFBmHVv+pYXfybC4Oolp3dvk8BvIE3wOPc+403CWIT7mJNkfQRGtdqghzs2+Z91Tqg==", - "dev": true, - "license": "MIT", - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "typescript": ">=4.8.4 <6.1.0" - } - }, "node_modules/@typescript-eslint/utils/node_modules/@typescript-eslint/types": { - "version": "8.59.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.59.0.tgz", - "integrity": "sha512-nLzdsT1gdOgFxxxwrlNVUBzSNBEEHJ86bblmk4QAS6stfig7rcJzWKqCyxFy3YRRHXDWEkb2NralA1nOYkkm/A==", + "version": "8.65.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.65.0.tgz", + "integrity": "sha512-JSSwWNy+H0E/01jJEM+hrX6N0OFDzFzeIhHFSAS01tlVaevpG8cFyYRPhS5yjGOvBUx3sqQHVMjCL1CAZZMxBg==", "dev": true, "license": "MIT", "engines": { @@ -12482,16 +12621,16 @@ } }, "node_modules/@typescript-eslint/utils/node_modules/@typescript-eslint/typescript-estree": { - "version": "8.59.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.59.0.tgz", - "integrity": "sha512-O9Re9P1BmBLFJyikRbQpLku/QA3/AueZNO9WePLBwQrvkixTmDe8u76B6CYUAITRl/rHawggEqUGn5QIkVRLMw==", + "version": "8.65.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.65.0.tgz", + "integrity": "sha512-JboAE2swaYt4tb1fHhHTABE2K+OLy09XfcTbhnk4Pw96f9dd2e9iYsJ28gBggHlo5z5x1rkyWvcPoTuNTd4oGg==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/project-service": "8.59.0", - "@typescript-eslint/tsconfig-utils": "8.59.0", - "@typescript-eslint/types": "8.59.0", - "@typescript-eslint/visitor-keys": "8.59.0", + "@typescript-eslint/project-service": "8.65.0", + "@typescript-eslint/tsconfig-utils": "8.65.0", + "@typescript-eslint/types": "8.65.0", + "@typescript-eslint/visitor-keys": "8.65.0", "debug": "^4.4.3", "minimatch": "^10.2.2", "semver": "^7.7.3", @@ -12510,13 +12649,13 @@ } }, "node_modules/@typescript-eslint/utils/node_modules/@typescript-eslint/visitor-keys": { - "version": "8.59.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.59.0.tgz", - "integrity": "sha512-/uejZt4dSere1bx12WLlPfv8GktzcaDtuJ7s42/HEZ5zGj9oxRaD4bj7qwSunXkf+pbAhFt2zjpHYUiT5lHf0Q==", + "version": "8.65.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.65.0.tgz", + "integrity": "sha512-8C71BQkGjiMmXtop7pHVJu1l2NNShFdkCyD6a2ezzs5vU/L3LRtb69EtcteFwz0mYMPzIgOw0n6OV4VBUWZd7A==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.59.0", + "@typescript-eslint/types": "8.65.0", "eslint-visitor-keys": "^5.0.0" }, "engines": { @@ -12538,16 +12677,16 @@ } }, "node_modules/@typescript-eslint/utils/node_modules/brace-expansion": { - "version": "5.0.6", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.6.tgz", - "integrity": "sha512-kLpxurY4Z4r9sgMsyG0Z9uzsBlgiU/EFKhj/h91/8yHu0edo7XuixOIH3VcJ8kkxs6/jPzoI6U9Vj3WqbMQ94g==", + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.8.tgz", + "integrity": "sha512-JZyDyq3D4AUifKTPOB7DELf6XsB3WdPuNxCtob1vFXPsSXhdAiHBWJ/tJ8HAc9aH84BK+5JFZLNkJKx3G9kzQg==", "dev": true, "license": "MIT", "dependencies": { "balanced-match": "^4.0.2" }, "engines": { - "node": "18 || 20 || >=22" + "node": "20 || >=22" } }, "node_modules/@typescript-eslint/utils/node_modules/eslint-visitor-keys": { @@ -12564,13 +12703,13 @@ } }, "node_modules/@typescript-eslint/utils/node_modules/minimatch": { - "version": "10.2.5", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.5.tgz", - "integrity": "sha512-MULkVLfKGYDFYejP07QOurDLLQpcjk7Fw+7jXS2R2czRQzR56yHRveU5NDJEOviH+hETZKSkIk5c+T23GjFUMg==", + "version": "10.2.6", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.6.tgz", + "integrity": "sha512-vpLQEs+VLCr1nU0BXS07maYoFwlDAH0gngQuuttxIwutDFEMHq2blX+8vpgxDdK3J1PwjCJiep77OitTZ4Ll1A==", "dev": true, "license": "BlueOak-1.0.0", "dependencies": { - "brace-expansion": "^5.0.5" + "brace-expansion": "^5.0.8" }, "engines": { "node": "18 || 20 || >=22" @@ -12610,6 +12749,349 @@ "url": "https://opencollective.com/typescript-eslint" } }, + "node_modules/@unrs/resolver-binding-android-arm-eabi": { + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-android-arm-eabi/-/resolver-binding-android-arm-eabi-1.12.2.tgz", + "integrity": "sha512-g5T90pqg1bo/7mytQx6F4iBNC0Wsh9cu+z9veDbFjc7HjpesJFWD7QMS0NGStXM075+7dJPPVvBbpZlnrdpi/w==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@unrs/resolver-binding-android-arm64": { + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-android-arm64/-/resolver-binding-android-arm64-1.12.2.tgz", + "integrity": "sha512-YGCRZv/9GLhwmz6mYDeTsm/92BAyR28l6c2ReweVW5pWgfsitWLY8upvfRlGdoyD8HjeTHSYJWyZGD4KJA/nFQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@unrs/resolver-binding-darwin-arm64": { + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-darwin-arm64/-/resolver-binding-darwin-arm64-1.12.2.tgz", + "integrity": "sha512-u9DiNT1auQMO20A9SyTuG3wUgQWB9Z7KjAg0uFuCDR1FsAY8A0CG2S6JpHS1xwm/w1G08bjXZDcyOCjv1WAm2w==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@unrs/resolver-binding-darwin-x64": { + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-darwin-x64/-/resolver-binding-darwin-x64-1.12.2.tgz", + "integrity": "sha512-f7rPLi/T1HVKZu/u6t87lroib16n8vrSzcyxI7lg4BGO9UF26KhQL44sd9eOUgrTYhvRXtWOIZT5PejdPyJfUA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@unrs/resolver-binding-freebsd-x64": { + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-freebsd-x64/-/resolver-binding-freebsd-x64-1.12.2.tgz", + "integrity": "sha512-BpcOjWCJub6nRZUS2zA20pmLvjtqAtGejETaIyRLiZiQf++cbrjltLA5NN/xaXfqeOBOSlMFbemIl5/S5tljmg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@unrs/resolver-binding-linux-arm-gnueabihf": { + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-arm-gnueabihf/-/resolver-binding-linux-arm-gnueabihf-1.12.2.tgz", + "integrity": "sha512-vZTDvdSISZjJx66OzJqtsOhzifbqRjbmI1Mnu49fQDwog5GtDI4QidRiEAYbZCRj9C8YZEW+3ZjqsyS9GR4k2A==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@unrs/resolver-binding-linux-arm-musleabihf": { + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-arm-musleabihf/-/resolver-binding-linux-arm-musleabihf-1.12.2.tgz", + "integrity": "sha512-BiPI+IrIlwcW4nLLMM21+B1dFPzd55yAVgVGrdgDjNef+ch03GdxrcyaIz8X9SsQirh/kCQ7mviyWlMxdh2D7g==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@unrs/resolver-binding-linux-arm64-gnu": { + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-arm64-gnu/-/resolver-binding-linux-arm64-gnu-1.12.2.tgz", + "integrity": "sha512-zJc0H99FEPoFfSrNpa91HYfxzfAJCr502oxNK1cfdC9hlaFI43RT+JFCann9JUgZmLzzntChHyn13Sgn9ljHNg==", + "cpu": [ + "arm64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@unrs/resolver-binding-linux-arm64-musl": { + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-arm64-musl/-/resolver-binding-linux-arm64-musl-1.12.2.tgz", + "integrity": "sha512-KQ3Lki6l+Pz1k/eBipN41ES+YUK30beLGb9YqcB1O542cyLCNE6GaxrfcY3T6EezmGGk84wb5XyO9loTM9tkcA==", + "cpu": [ + "arm64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@unrs/resolver-binding-linux-loong64-gnu": { + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-loong64-gnu/-/resolver-binding-linux-loong64-gnu-1.12.2.tgz", + "integrity": "sha512-3SJGEh1DborhG6pyxvhPzCT4bbSIVihsvgJc13P1bHG7KLdNDaF9T3gsTwFc7Jw/5Y5/iWOjkEx7Zy0NvCGX3Q==", + "cpu": [ + "loong64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@unrs/resolver-binding-linux-loong64-musl": { + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-loong64-musl/-/resolver-binding-linux-loong64-musl-1.12.2.tgz", + "integrity": "sha512-jiuG/Obbel7uw1PwHNFfrkiKhLAF6mnyZ6aWlOAVN9WqKm8v0OFGnciJIHu8+CMvXLQ8AD51LPzAoUfT21D5Ew==", + "cpu": [ + "loong64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@unrs/resolver-binding-linux-ppc64-gnu": { + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-ppc64-gnu/-/resolver-binding-linux-ppc64-gnu-1.12.2.tgz", + "integrity": "sha512-q7xRvVpmcfeL+LlZg8Pbbo6QaTZwDU5BaGZbwfhkEsXJn3Was8xYfE0RBH266xZt0rM6B7i8xAYIvjthuUIWHg==", + "cpu": [ + "ppc64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@unrs/resolver-binding-linux-riscv64-gnu": { + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-riscv64-gnu/-/resolver-binding-linux-riscv64-gnu-1.12.2.tgz", + "integrity": "sha512-0CVdx6lcnT3Q9inOH8tsMIOJ6ImndllMjqJHg8RLVdB7Vq4SfkEXl9mCSsVNuNA4MCYycRicCUxPCabVHJRr6A==", + "cpu": [ + "riscv64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@unrs/resolver-binding-linux-riscv64-musl": { + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-riscv64-musl/-/resolver-binding-linux-riscv64-musl-1.12.2.tgz", + "integrity": "sha512-iOwlRo9vnp6R6ohHQS11n0NnfdXx/omhkocmIfaPRpQhKZ+3BDMkkdRVh53qjkFkpPddf+FETA28NwGN7l5l+w==", + "cpu": [ + "riscv64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@unrs/resolver-binding-linux-s390x-gnu": { + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-s390x-gnu/-/resolver-binding-linux-s390x-gnu-1.12.2.tgz", + "integrity": "sha512-HYJtLfXq94q8iZNFT1lknx258wlkkWhZeUXJRqzKBBUJ00CvZ+N33zgbCqimLjsyw5Va6uUxhVa12mI+kaveEw==", + "cpu": [ + "s390x" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@unrs/resolver-binding-linux-x64-gnu": { + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-x64-gnu/-/resolver-binding-linux-x64-gnu-1.12.2.tgz", + "integrity": "sha512-mPsUhunKKDih5O96Y6enDQyHc1SqBPlY1E/SfMWDM3EdJ95Z9CArPeCVwCCqbP45ljvivdEk8Fxn+SIb1rDAJQ==", + "cpu": [ + "x64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@unrs/resolver-binding-linux-x64-musl": { + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-x64-musl/-/resolver-binding-linux-x64-musl-1.12.2.tgz", + "integrity": "sha512-azrt6+5ydLd8Vt210AAFis/lZevSfPw93EJRIJG+xPu4WCJ8K0kppCTpMyLPcKT7H15M4Jnt2tMp5bOvCkRC6A==", + "cpu": [ + "x64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@unrs/resolver-binding-openharmony-arm64": { + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-openharmony-arm64/-/resolver-binding-openharmony-arm64-1.12.2.tgz", + "integrity": "sha512-YZ9hP4O0X9PQb8eO980qmLNGH4zT3I9+SZTdt0Pr0YyuGQhYKoOZkV02VzrzyOZJ5xIJ3UFIenKkUkGg8GjgWQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ] + }, + "node_modules/@unrs/resolver-binding-wasm32-wasi": { + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-wasm32-wasi/-/resolver-binding-wasm32-wasi-1.12.2.tgz", + "integrity": "sha512-tYFDIkMxSflfEc/h92ZWNsZlHSwgimbNHSO3PL2JWQHfCuC2q316jMyYU9TIWZsFK2bQwyK5VAdYgn8ygPj69A==", + "cpu": [ + "wasm32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@emnapi/core": "1.10.0", + "@emnapi/runtime": "1.10.0", + "@napi-rs/wasm-runtime": "^1.1.4" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@unrs/resolver-binding-win32-arm64-msvc": { + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-win32-arm64-msvc/-/resolver-binding-win32-arm64-msvc-1.12.2.tgz", + "integrity": "sha512-qzNyg3xL0VPQmCaUh+N5jSitce6k+uCBfMDesWRnlULOZaqUkaJ0ybdT+UqlAWJoQjuqfIU/0Ptx9bteN4D82g==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@unrs/resolver-binding-win32-ia32-msvc": { + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-win32-ia32-msvc/-/resolver-binding-win32-ia32-msvc-1.12.2.tgz", + "integrity": "sha512-WD9sY00OfpHVGfsnHZoA8jVT+esS/Bg8z8jzxp5BnDCjjwsuKsPQrzswwpFy4J1AUJbXPRfkpcX0mXrzeXW79g==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@unrs/resolver-binding-win32-x64-msvc": { + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-win32-x64-msvc/-/resolver-binding-win32-x64-msvc-1.12.2.tgz", + "integrity": "sha512-nAB74NfSNKknqQ1RrYj6uz8FcXEomu/MATJZxh/x+BArzN2U3JbOYC0APYzUIGhVY3m5hRxA8VPNdPBoG8txlA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, "node_modules/@whatwg-node/promise-helpers": { "version": "1.3.2", "resolved": "https://registry.npmjs.org/@whatwg-node/promise-helpers/-/promise-helpers-1.3.2.tgz", @@ -12679,9 +13161,9 @@ } }, "node_modules/acorn": { - "version": "8.15.0", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz", - "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==", + "version": "8.18.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.18.0.tgz", + "integrity": "sha512-lGq+9yr1/GuAWaVYIHRjvvySG5/4VfKIvC8EWxStPdcDh/Ka7FG3twP6v4d5BkravUilhIAsG4Qj83t02LWUPQ==", "dev": true, "license": "MIT", "bin": { @@ -12710,11 +13192,12 @@ } }, "node_modules/adm-zip": { - "version": "0.5.10", - "resolved": "https://registry.npmjs.org/adm-zip/-/adm-zip-0.5.10.tgz", - "integrity": "sha512-x0HvcHqVJNTPk/Bw8JbLWlWoo6Wwnsug0fnYYro1HBrjxZ3G7/AZk7Ahv8JwDe1uIcz8eBqvu86FuF1POiG7vQ==", + "version": "0.5.18", + "resolved": "https://registry.npmjs.org/adm-zip/-/adm-zip-0.5.18.tgz", + "integrity": "sha512-ufJnssQGbxzLNS1Ho9bCtX4rQKCCvoVuDLHoJyc3F9dOGDB4BkWs2Ci0kv53lqocAEQ/Cbi+I2XCsNYGqVYqng==", + "license": "MIT", "engines": { - "node": ">=6.0" + "node": ">=12.0" } }, "node_modules/agent-base": { @@ -13050,16 +13533,42 @@ } }, "node_modules/axios": { - "version": "1.16.0", - "resolved": "https://registry.npmjs.org/axios/-/axios-1.16.0.tgz", - "integrity": "sha512-6hp5CwvTPlN2A31g5dxnwAX0orzM7pmCRDLnZSX772mv8WDqICwFjowHuPs04Mc8deIld1+ejhtaMn5vp6b+1w==", + "version": "1.18.1", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.18.1.tgz", + "integrity": "sha512-3nTvFlvpn9Zu/RkHUqtc7/+al4UpRW5az71ap5zccp6e8RAYEzhMTecX8Dz1wWDYrPpUoB1HAQEGEAEvUr7S9g==", "license": "MIT", "dependencies": { "follow-redirects": "^1.16.0", "form-data": "^4.0.5", + "https-proxy-agent": "^5.0.1", "proxy-from-env": "^2.1.0" } }, + "node_modules/axios/node_modules/agent-base": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", + "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", + "license": "MIT", + "dependencies": { + "debug": "4" + }, + "engines": { + "node": ">= 6.0.0" + } + }, + "node_modules/axios/node_modules/https-proxy-agent": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", + "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==", + "license": "MIT", + "dependencies": { + "agent-base": "6", + "debug": "4" + }, + "engines": { + "node": ">= 6" + } + }, "node_modules/babel-jest": { "version": "29.7.0", "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-29.7.0.tgz", @@ -13115,26 +13624,30 @@ } }, "node_modules/babel-preset-current-node-syntax": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/babel-preset-current-node-syntax/-/babel-preset-current-node-syntax-1.0.1.tgz", - "integrity": "sha512-M7LQ0bxarkxQoN+vz5aJPsLBn77n8QgTFmo8WK0/44auK2xlCXrYcUxHFxgU7qW5Yzw/CjmLRK2uJzaCd7LvqQ==", + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/babel-preset-current-node-syntax/-/babel-preset-current-node-syntax-1.2.0.tgz", + "integrity": "sha512-E/VlAEzRrsLEb2+dv8yp3bo4scof3l9nR4lrld+Iy5NyVqgVYUJnDAmunkhPMisRI32Qc4iRiz425d8vM++2fg==", "dev": true, + "license": "MIT", "dependencies": { "@babel/plugin-syntax-async-generators": "^7.8.4", "@babel/plugin-syntax-bigint": "^7.8.3", - "@babel/plugin-syntax-class-properties": "^7.8.3", - "@babel/plugin-syntax-import-meta": "^7.8.3", + "@babel/plugin-syntax-class-properties": "^7.12.13", + "@babel/plugin-syntax-class-static-block": "^7.14.5", + "@babel/plugin-syntax-import-attributes": "^7.24.7", + "@babel/plugin-syntax-import-meta": "^7.10.4", "@babel/plugin-syntax-json-strings": "^7.8.3", - "@babel/plugin-syntax-logical-assignment-operators": "^7.8.3", + "@babel/plugin-syntax-logical-assignment-operators": "^7.10.4", "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3", - "@babel/plugin-syntax-numeric-separator": "^7.8.3", + "@babel/plugin-syntax-numeric-separator": "^7.10.4", "@babel/plugin-syntax-object-rest-spread": "^7.8.3", "@babel/plugin-syntax-optional-catch-binding": "^7.8.3", "@babel/plugin-syntax-optional-chaining": "^7.8.3", - "@babel/plugin-syntax-top-level-await": "^7.8.3" + "@babel/plugin-syntax-private-property-in-object": "^7.14.5", + "@babel/plugin-syntax-top-level-await": "^7.14.5" }, "peerDependencies": { - "@babel/core": "^7.0.0" + "@babel/core": "^7.0.0 || ^8.0.0-0" } }, "node_modules/babel-preset-jest": { @@ -13205,21 +13718,34 @@ } }, "node_modules/body-parser": { - "version": "2.2.2", - "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-2.2.2.tgz", - "integrity": "sha512-oP5VkATKlNwcgvxi0vM0p/D3n2C3EReYVX+DNYs5TjZFn/oQt2j+4sVJtSMr18pdRr8wjTcBl6LoV+FUwzPmNA==", + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-2.3.0.tgz", + "integrity": "sha512-2cGmJupaNgg+QUwVLAucDuWuoMZ6EX9iHDRswZ5lsNYEmwPaRknMPCLZz07yTzVq/83p4o/wzbDZbBrTvGGTIw==", "license": "MIT", "dependencies": { "bytes": "^3.1.2", - "content-type": "^1.0.5", + "content-type": "^2.0.0", "debug": "^4.4.3", - "http-errors": "^2.0.0", - "iconv-lite": "^0.7.0", + "http-errors": "^2.0.1", + "iconv-lite": "^0.7.2", "on-finished": "^2.4.1", - "qs": "^6.14.1", - "raw-body": "^3.0.1", - "type-is": "^2.0.1" + "qs": "^6.15.2", + "raw-body": "^3.0.2", + "type-is": "^2.1.0" + }, + "engines": { + "node": ">=18" }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/body-parser/node_modules/content-type": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-2.0.0.tgz", + "integrity": "sha512-j/O/d7GcZCyNl7/hwZAb606rzqkyvaDctLmckbxLzHvFBzTJHuGEdodATcP3yIRoDrLHkIATJuvzbFlp/ki2cQ==", + "license": "MIT", "engines": { "node": ">=18" }, @@ -13235,9 +13761,9 @@ "license": "MIT" }, "node_modules/brace-expansion": { - "version": "1.1.13", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.13.tgz", - "integrity": "sha512-9ZLprWS6EENmhEOpjCYW2c8VkmOvckIJZfkr7rBW6dObmfgJ/L1GpSYW5Hpo9lDz4D1+n0Ckz8rU7FwHDQiG/w==", + "version": "1.1.16", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.16.tgz", + "integrity": "sha512-IDw48K2/2kRkg9LdJxurvq3lV3aBgq0REY89duEqFRthjlPdXHKMj7EnQOXVckxzgisinf3nHfrcE2FufFLXMw==", "dev": true, "license": "MIT", "dependencies": { @@ -13423,17 +13949,26 @@ "license": "CC-BY-4.0" }, "node_modules/cassandra-driver": { - "version": "4.8.0", - "resolved": "https://registry.npmjs.org/cassandra-driver/-/cassandra-driver-4.8.0.tgz", - "integrity": "sha512-HritfMGq9V7SuESeSodHvArs0mLuMk7uh+7hQK2lqdvXrvm50aWxb4RPxkK3mPDdsgHjJ427xNRFITMH2ei+Sw==", + "version": "4.9.0", + "resolved": "https://registry.npmjs.org/cassandra-driver/-/cassandra-driver-4.9.0.tgz", + "integrity": "sha512-svYpdkLIGjD0WmuuwkkeYbfBdPX1zksK2cDyT1mWjX53OVTzuWBOVy54K6PPij8GgYpIG+K82OryrBv/xNeuWg==", "license": "Apache-2.0", "dependencies": { - "@types/node": "^18.11.18", + "@types/node": "^20.14.8", "adm-zip": "~0.5.10", "long": "~5.2.3" }, "engines": { - "node": ">=18" + "node": ">=20" + } + }, + "node_modules/cassandra-driver/node_modules/@types/node": { + "version": "20.19.43", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.19.43.tgz", + "integrity": "sha512-6oYBAi5ikg4Pl+kGsoYtawUMBT2zZMCvPNF7pVLnHZfd1zf38DRiWn/gT01RYCdUqkv7Fhr+C9ot4/tb+2sVvA==", + "license": "MIT", + "dependencies": { + "undici-types": "~6.21.0" } }, "node_modules/cassandra-driver/node_modules/long": { @@ -13442,6 +13977,12 @@ "integrity": "sha512-e0r9YBBgNCq1D1o5Dp8FMH0N5hsFtXDBiVa0qoJPHpakvZkmDKPRoGffZJII/XsHvj9An9blm+cRJ01yQqU+Dw==", "license": "Apache-2.0" }, + "node_modules/cassandra-driver/node_modules/undici-types": { + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz", + "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==", + "license": "MIT" + }, "node_modules/chalk": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", @@ -13554,9 +14095,9 @@ } }, "node_modules/comment-parser": { - "version": "1.4.5", - "resolved": "https://registry.npmjs.org/comment-parser/-/comment-parser-1.4.5.tgz", - "integrity": "sha512-aRDkn3uyIlCFfk5NUA+VdwMmMsh8JGhc4hapfV4yxymHGQ3BVskMQfoXGpCo5IoBuQ9tS5iiVKhCpTcB4pW4qw==", + "version": "1.4.6", + "resolved": "https://registry.npmjs.org/comment-parser/-/comment-parser-1.4.6.tgz", + "integrity": "sha512-ObxuY6vnbWTN6Od72xfwN9DbzC7Y2vv8u1Soi9ahRKL37gb6y1qk6/dgjs+3JWuXJHWvsg3BXIwzd/rkmAwavg==", "dev": true, "license": "MIT", "engines": { @@ -13895,10 +14436,11 @@ } }, "node_modules/dedent": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/dedent/-/dedent-1.5.1.tgz", - "integrity": "sha512-+LxW+KLWxu3HW3M2w2ympwtqPrqYRzU8fqi6Fhd18fBALe15blJPI/I4+UHveMVG6lJqB4JNd4UG0S5cnVHwIg==", + "version": "1.7.2", + "resolved": "https://registry.npmjs.org/dedent/-/dedent-1.7.2.tgz", + "integrity": "sha512-WzMx3mW98SN+zn3hgemf4OzdmyNhhhKz5Ay0pUfQiMQ3e1g+xmTJWp/pKdwKVXhdSkAEGIIzqeuWrL3mV/AXbA==", "dev": true, + "license": "MIT", "peerDependencies": { "babel-plugin-macros": "^3.1.0" }, @@ -14055,15 +14597,6 @@ "node": ">=8" } }, - "node_modules/dotenv": { - "version": "10.0.0", - "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-10.0.0.tgz", - "integrity": "sha512-rlBi9d8jpv9Sf1klPjNfFAuWDjKLwTIJJ/VxtoTwIR6hnZxcEOQCZg2oIL3MWBYw5GpUDKOEnND7LXTbIpQ03Q==", - "dev": true, - "engines": { - "node": ">=10" - } - }, "node_modules/dunder-proto": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", @@ -14156,19 +14689,6 @@ "once": "^1.4.0" } }, - "node_modules/enhanced-resolve": { - "version": "5.15.0", - "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.15.0.tgz", - "integrity": "sha512-LXYT42KJ7lpIKECr2mAXIaMldcNCh/7E0KBKOu4KSfkHmP+mZmSs+8V5gBAqisWBy0OO4W5Oyys0GO1Y8KtdKg==", - "dev": true, - "dependencies": { - "graceful-fs": "^4.2.4", - "tapable": "^2.2.0" - }, - "engines": { - "node": ">=10.13.0" - } - }, "node_modules/error-ex": { "version": "1.3.4", "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.4.tgz", @@ -14351,9 +14871,9 @@ } }, "node_modules/eslint": { - "version": "9.39.4", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.39.4.tgz", - "integrity": "sha512-XoMjdBOwe/esVgEvLmNsD3IRHkm7fbKIUGvrleloJXUZgDHig2IPWNniv+GwjyJXzuNqVjlr5+4yVUZjycJwfQ==", + "version": "9.39.5", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.39.5.tgz", + "integrity": "sha512-DgZS62aPLXKlnxILS/AYCoRvHaZeXceIzlXPkkGGzJWSow1aEk0lbTlxUSlyjC8jcaKxAdOnTDz+o1JFSBsyjw==", "dev": true, "license": "MIT", "dependencies": { @@ -14362,8 +14882,8 @@ "@eslint/config-array": "^0.21.2", "@eslint/config-helpers": "^0.4.2", "@eslint/core": "^0.17.0", - "@eslint/eslintrc": "^3.3.5", - "@eslint/js": "9.39.4", + "@eslint/eslintrc": "^3.3.6", + "@eslint/js": "9.39.5", "@eslint/plugin-kit": "^0.4.1", "@humanfs/node": "^0.16.6", "@humanwhocodes/module-importer": "^1.0.1", @@ -14433,28 +14953,38 @@ } }, "node_modules/eslint-import-resolver-typescript": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/eslint-import-resolver-typescript/-/eslint-import-resolver-typescript-3.6.0.tgz", - "integrity": "sha512-QTHR9ddNnn35RTxlaEnx2gCxqFlF2SEN0SE2d17SqwyM7YOSI2GHWRYp5BiRkObTUNYPupC/3Fq2a0PpT+EKpg==", + "version": "3.10.1", + "resolved": "https://registry.npmjs.org/eslint-import-resolver-typescript/-/eslint-import-resolver-typescript-3.10.1.tgz", + "integrity": "sha512-A1rHYb06zjMGAxdLSkN2fXPBwuSaQ0iO5M/hdyS0Ajj1VBaRp0sPD3dn1FhME3c/JluGFbwSxyCfqdSbtQLAHQ==", "dev": true, + "license": "ISC", "dependencies": { - "debug": "^4.3.4", - "enhanced-resolve": "^5.12.0", - "eslint-module-utils": "^2.7.4", - "fast-glob": "^3.3.1", - "get-tsconfig": "^4.5.0", - "is-core-module": "^2.11.0", - "is-glob": "^4.0.3" + "@nolyfill/is-core-module": "1.0.39", + "debug": "^4.4.0", + "get-tsconfig": "^4.10.0", + "is-bun-module": "^2.0.0", + "stable-hash": "^0.0.5", + "tinyglobby": "^0.2.13", + "unrs-resolver": "^1.6.2" }, "engines": { "node": "^14.18.0 || >=16.0.0" }, "funding": { - "url": "https://opencollective.com/unts/projects/eslint-import-resolver-ts" + "url": "https://opencollective.com/eslint-import-resolver-typescript" }, "peerDependencies": { "eslint": "*", - "eslint-plugin-import": "*" + "eslint-plugin-import": "*", + "eslint-plugin-import-x": "*" + }, + "peerDependenciesMeta": { + "eslint-plugin-import": { + "optional": true + }, + "eslint-plugin-import-x": { + "optional": true + } } }, "node_modules/eslint-module-utils": { @@ -14505,9 +15035,9 @@ } }, "node_modules/eslint-plugin-functional": { - "version": "9.0.4", - "resolved": "https://registry.npmjs.org/eslint-plugin-functional/-/eslint-plugin-functional-9.0.4.tgz", - "integrity": "sha512-zm4qaoqb2r50V4WXxt0Mj92buXGMECYvMxGQ6sSb+XeJ+Eec6zCHuMY2+AWK1mqiApvUz2tCtp1P3zcEPU0huw==", + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/eslint-plugin-functional/-/eslint-plugin-functional-9.0.5.tgz", + "integrity": "sha512-RYbNpGkGu5mSN7xlezyeE7TE3S9rsliAS5FOorZoxn7126UTslcQnoVOIAOA6fMOlz6dNG3x3cd3tuzrHM6mRA==", "dev": true, "funding": [ { @@ -14632,24 +15162,24 @@ } }, "node_modules/eslint-plugin-jsdoc": { - "version": "62.5.4", - "resolved": "https://registry.npmjs.org/eslint-plugin-jsdoc/-/eslint-plugin-jsdoc-62.5.4.tgz", - "integrity": "sha512-U+Q5ppErmC17VFQl542eBIaXcuq975BzoIHBXyx7UQx/i4gyHXxPiBkonkuxWyFA98hGLALLUuD+NJcXqSGKxg==", + "version": "62.9.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-jsdoc/-/eslint-plugin-jsdoc-62.9.0.tgz", + "integrity": "sha512-PY7/X4jrVgoIDncUmITlUqK546Ltmx/Pd4Hdsu4CvSjryQZJI2mEV4vrdMufyTetMiZ5taNSqvK//BTgVUlNkA==", "dev": true, "license": "BSD-3-Clause", "dependencies": { - "@es-joy/jsdoccomment": "~0.84.0", + "@es-joy/jsdoccomment": "~0.86.0", "@es-joy/resolve.exports": "1.2.0", "are-docs-informative": "^0.0.2", - "comment-parser": "1.4.5", + "comment-parser": "1.4.6", "debug": "^4.4.3", "escape-string-regexp": "^4.0.0", - "espree": "^11.1.0", + "espree": "^11.2.0", "esquery": "^1.7.0", "html-entities": "^2.6.0", "object-deep-merge": "^2.0.0", "parse-imports-exports": "^0.2.4", - "semver": "^7.7.3", + "semver": "^7.7.4", "spdx-expression-parse": "^4.0.0", "to-valid-identifier": "^1.0.0" }, @@ -14657,13 +15187,13 @@ "node": "^20.19.0 || ^22.13.0 || >=24" }, "peerDependencies": { - "eslint": "^7.0.0 || ^8.0.0 || ^9.0.0" + "eslint": "^7.0.0 || ^8.0.0 || ^9.0.0 || ^10.0.0" } }, "node_modules/eslint-plugin-jsdoc/node_modules/eslint-visitor-keys": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-5.0.0.tgz", - "integrity": "sha512-A0XeIi7CXU7nPlfHS9loMYEKxUaONu/hTEzHTGba9Huu94Cq1hPivf+DE5erJozZOky0LfvXAyrV/tcswpLI0Q==", + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-5.0.1.tgz", + "integrity": "sha512-tD40eHxA35h0PEIZNeIjkHoDR4YjjJp34biM0mDvplBe//mB+IHCqHDGV7pxF+7MklTvighcCPPZC7ynWyjdTA==", "dev": true, "license": "Apache-2.0", "engines": { @@ -14674,15 +15204,15 @@ } }, "node_modules/eslint-plugin-jsdoc/node_modules/espree": { - "version": "11.1.0", - "resolved": "https://registry.npmjs.org/espree/-/espree-11.1.0.tgz", - "integrity": "sha512-WFWYhO1fV4iYkqOOvq8FbqIhr2pYfoDY0kCotMkDeNtGpiGGkZ1iov2u8ydjtgM8yF8rzK7oaTbw2NAzbAbehw==", + "version": "11.2.0", + "resolved": "https://registry.npmjs.org/espree/-/espree-11.2.0.tgz", + "integrity": "sha512-7p3DrVEIopW1B1avAGLuCSh1jubc01H2JHc8B4qqGblmg5gI9yumBgACjWo4JlIc04ufug4xJ3SQI8HkS/Rgzw==", "dev": true, "license": "BSD-2-Clause", "dependencies": { - "acorn": "^8.15.0", + "acorn": "^8.16.0", "acorn-jsx": "^5.3.2", - "eslint-visitor-keys": "^5.0.0" + "eslint-visitor-keys": "^5.0.1" }, "engines": { "node": "^20.19.0 || ^22.13.0 || >=24" @@ -14721,9 +15251,9 @@ } }, "node_modules/eslint-plugin-promise": { - "version": "7.2.1", - "resolved": "https://registry.npmjs.org/eslint-plugin-promise/-/eslint-plugin-promise-7.2.1.tgz", - "integrity": "sha512-SWKjd+EuvWkYaS+uN2csvj0KoP43YTu7+phKQ5v+xw6+A0gutVX2yqCeCkC3uLCJFiPfR2dD8Es5L7yUsmvEaA==", + "version": "7.3.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-promise/-/eslint-plugin-promise-7.3.0.tgz", + "integrity": "sha512-6uGiOR0INuujr6PEQmeSSP7GbIMJ/ebEXXiEzb/nOj68LknH5Pxzb/AbZivmr6VE6TkTE8rTjRK9zhKpK6HsRA==", "dev": true, "license": "ISC", "dependencies": { @@ -14736,7 +15266,7 @@ "url": "https://opencollective.com/eslint" }, "peerDependencies": { - "eslint": "^7.0.0 || ^8.0.0 || ^9.0.0" + "eslint": "^7.0.0 || ^8.0.0 || ^9.0.0 || ^10.0.0" } }, "node_modules/eslint-plugin-security": { @@ -14750,10 +15280,11 @@ } }, "node_modules/eslint-plugin-switch-statement": { - "version": "0.0.11", - "resolved": "https://registry.npmjs.org/eslint-plugin-switch-statement/-/eslint-plugin-switch-statement-0.0.11.tgz", - "integrity": "sha512-hjZ1D0Ri6IYG5raNOy/dyVDO/BX9p/9d0RLR9JrctTjFWoj9tozb7vu2d683b850Hy9OPGMB0Kvn3Ee3uWostQ==", + "version": "0.0.12", + "resolved": "https://registry.npmjs.org/eslint-plugin-switch-statement/-/eslint-plugin-switch-statement-0.0.12.tgz", + "integrity": "sha512-LJd6sTnm/nptvFcHfCau6Kx946LPBMFhF8Gz1gEPhlt5Kzv8wIEYCaut1W9xYksDKwbvk8Ir3QBZKRnzCO+R2g==", "dev": true, + "license": "ISC", "dependencies": { "@typescript-eslint/utils": "^7.4.0" }, @@ -14986,7 +15517,8 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/eventemitter-asyncresource/-/eventemitter-asyncresource-1.0.0.tgz", "integrity": "sha512-39F7TBIV0G7gTelxwbEqnwhp90eqCPON1k0NwNfwhgKn4Co4ybUbj2pECcXT0B3ztRKZ7Pw1JujUUgmQJHcVAQ==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/execa": { "version": "5.1.1", @@ -15243,9 +15775,9 @@ "dev": true }, "node_modules/fast-uri": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.2.tgz", - "integrity": "sha512-rVjf7ArG3LTk+FS6Yw81V1DLuZl1bRbNrev6Tmd/9RaroeeRRJhAt7jg/6YFxbvAQXUCavSoZhPPj6oOx+5KjQ==", + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.4.tgz", + "integrity": "sha512-8JnbkQ4juDyvYs4mgFGQqg4yCYtFDtUtmp2QIQq11ZZe5CFQ5wcqm1rqDgAh/QdMySuBnPzMUiJUNZG5N/AiQw==", "funding": [ { "type": "github", @@ -16178,6 +16710,7 @@ "resolved": "https://registry.npmjs.org/hdr-histogram-js/-/hdr-histogram-js-2.0.3.tgz", "integrity": "sha512-Hkn78wwzWHNCp2uarhzQ2SGFLU3JY8SBDDd3TAABK4fc30wm+MuPOrg5QVFVfkKOQd6Bfz3ukJEI+q9sXEkK1g==", "dev": true, + "license": "BSD", "dependencies": { "@assemblyscript/loader": "^0.10.1", "base64-js": "^1.2.0", @@ -16188,7 +16721,8 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/hdr-histogram-percentiles-obj/-/hdr-histogram-percentiles-obj-3.0.0.tgz", "integrity": "sha512-7kIufnBqdsBGcSZLPJwqHT3yhk1QTsSlFsVD3kx5ixH/AlgBs9yM1q6DPhXZ8f8gtdqgh7N7/5btRLpQsS2gHw==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/heap": { "version": "0.2.7", @@ -16504,6 +17038,16 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/is-bun-module": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-bun-module/-/is-bun-module-2.0.0.tgz", + "integrity": "sha512-gNCGbnnnnFAUGKeZ9PdbyeGYJqewpmc2aKHUEMO5nQPWU9lOmv7jcmQIv+qHD8fXW6W7qfuCwX4rY9LNRjXrkQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "semver": "^7.7.1" + } + }, "node_modules/is-callable": { "version": "1.2.7", "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz", @@ -17348,6 +17892,7 @@ "resolved": "https://registry.npmjs.org/jest-light-runner/-/jest-light-runner-0.4.1.tgz", "integrity": "sha512-xO9ryg9X2LbUM5FN98xcaoYSdSgzEUoJO3yROiebsmtnEx/uH4zayM0vq8OgzLlpZcOpmNouJ4CI561GRQNb9w==", "dev": true, + "license": "MIT", "dependencies": { "@jest/expect": "^29.0.1", "jest-circus": "^29.0.1", @@ -17369,6 +17914,7 @@ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-9.4.0.tgz", "integrity": "sha512-VL+lNrEoIXww1coLPOmiEmK/0sGigko5COxI09KzHc2VJXJsQ37UaQ+8quuxjDeA7+KnLGTWRyOXSLLR2Wb4jw==", "dev": true, + "license": "MIT", "engines": { "node": ">=12" }, @@ -17750,9 +18296,9 @@ "license": "MIT" }, "node_modules/js-yaml": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.2.0.tgz", - "integrity": "sha512-ePWsvanv0DWuDRsW8dnt+R4jQ31SCRCQ7hhNcPXZPsoBZiemuZNYGf7adZdqX2D86j6rvKp3RpCxVTSb8WQlOw==", + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-5.2.2.tgz", + "integrity": "sha512-dayzUzKkJ1MkuUtZglSebU43utNXH0OWQByK9rKOOuYIO8M5TV1y+n8ALMdG0rdzBnfNkOmZEqrURepb0ejqBw==", "dev": true, "funding": [ { @@ -17769,13 +18315,13 @@ "argparse": "^2.0.1" }, "bin": { - "js-yaml": "bin/js-yaml.js" + "js-yaml": "bin/js-yaml.mjs" } }, "node_modules/jsdoc-type-pratt-parser": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/jsdoc-type-pratt-parser/-/jsdoc-type-pratt-parser-7.1.1.tgz", - "integrity": "sha512-/2uqY7x6bsrpi3i9LVU6J89352C0rpMk0as8trXxCtvd4kPk1ke/Eyif6wqfSLvoNJqcDG9Vk4UsXgygzCt2xA==", + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/jsdoc-type-pratt-parser/-/jsdoc-type-pratt-parser-7.2.0.tgz", + "integrity": "sha512-dh140MMgjyg3JhJZY/+iEzW+NO5xR2gpbDFKHqotCmexElVntw7GjWjt511+C/Ef02RU5TKYrJo/Xlzk+OLaTw==", "dev": true, "license": "MIT", "engines": { @@ -17934,9 +18480,9 @@ } }, "node_modules/knip": { - "version": "6.16.1", - "resolved": "https://registry.npmjs.org/knip/-/knip-6.16.1.tgz", - "integrity": "sha512-TKMn1rxgH6h9vXR9Y0B+Cq7AdPTr9EI02IwoT65NzqYUkvoDQAaJ/aPybiFpAhZ1px6cNYYwXf86iHkBgzCo9w==", + "version": "6.27.0", + "resolved": "https://registry.npmjs.org/knip/-/knip-6.27.0.tgz", + "integrity": "sha512-CngYEYrD0n20N06FXA8n3u/0Wnnugoa+B9k14OP+iKIgkCHuzvIdsP3nfwjhByoc1WfogpxfiriMboAXFETDUw==", "dev": true, "funding": [ { @@ -17954,13 +18500,13 @@ "formatly": "^0.3.0", "get-tsconfig": "4.14.0", "jiti": "^2.7.0", - "oxc-parser": "^0.133.0", - "oxc-resolver": "^11.20.0", + "oxc-parser": "^0.137.0", + "oxc-resolver": "11.21.3", "picomatch": "^4.0.4", "smol-toml": "^1.6.1", "strip-json-comments": "5.0.3", - "tinyglobby": "^0.2.16", - "unbash": "^3.0.0", + "tinyglobby": "^0.2.17", + "unbash": "^4.0.1", "yaml": "^2.9.0", "zod": "^4.1.11" }, @@ -18383,6 +18929,22 @@ "@msgpackr-extract/msgpackr-extract-win32-x64": "3.0.3" } }, + "node_modules/napi-postinstall": { + "version": "0.3.4", + "resolved": "https://registry.npmjs.org/napi-postinstall/-/napi-postinstall-0.3.4.tgz", + "integrity": "sha512-PHI5f1O0EP5xJ9gQmFGMS6IZcrVvTjpXjz7Na41gTE7eE2hK11lg04CECCYEEjdc17EV4DO+fkGEtt7TpTaTiQ==", + "dev": true, + "license": "MIT", + "bin": { + "napi-postinstall": "lib/cli.js" + }, + "engines": { + "node": "^12.20.0 || ^14.18.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/napi-postinstall" + } + }, "node_modules/natural-compare": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", @@ -18404,6 +18966,7 @@ "integrity": "sha512-px/KnJAJZf5RuBGcfD+Sp2pAKq0ytz8j+1NehvgIGFkvtvFrDM3T8E4x/JJODXK9WZow8RRGrbA9QQ3hs+pDhA==", "dev": true, "hasInstallScript": true, + "license": "MIT", "optional": true, "os": [ "!win32" @@ -18413,30 +18976,19 @@ "node-gyp-build": "^4.2.2" } }, - "node_modules/nice-napi/node_modules/node-addon-api": { + "node_modules/node-abort-controller": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/node-abort-controller/-/node-abort-controller-3.1.1.tgz", + "integrity": "sha512-AGK2yQKIjRuqnc6VkX2Xj5d+QW8xZ87pa1UK6yA6ouUyuxfHuMP6umE5QK7UmTeOAymo+Zx1Fxiuw9rVx8taHQ==" + }, + "node_modules/node-addon-api": { "version": "3.2.1", "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-3.2.1.tgz", "integrity": "sha512-mmcei9JghVNDYydghQmeDX8KoAm0FAiYyIcUt/N4nhyAipB17pllZQDOJD2fotxABnt4Mdz+dKTO7eftLg4d0A==", "dev": true, + "license": "MIT", "optional": true }, - "node_modules/nice-napi/node_modules/node-gyp-build": { - "version": "4.6.0", - "resolved": "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-4.6.0.tgz", - "integrity": "sha512-NTZVKn9IylLwUzaKjkas1e4u2DLNcV4rdYagA4PWdPwW87Bi7z+BznyKSRwS/761tV/lzCGXplWsiaMjLqP2zQ==", - "dev": true, - "optional": true, - "bin": { - "node-gyp-build": "bin.js", - "node-gyp-build-optional": "optional.js", - "node-gyp-build-test": "build-test.js" - } - }, - "node_modules/node-abort-controller": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/node-abort-controller/-/node-abort-controller-3.1.1.tgz", - "integrity": "sha512-AGK2yQKIjRuqnc6VkX2Xj5d+QW8xZ87pa1UK6yA6ouUyuxfHuMP6umE5QK7UmTeOAymo+Zx1Fxiuw9rVx8taHQ==" - }, "node_modules/node-cleanup": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/node-cleanup/-/node-cleanup-2.1.2.tgz", @@ -18461,6 +19013,19 @@ "node": ">=10.5.0" } }, + "node_modules/node-gyp-build": { + "version": "4.8.4", + "resolved": "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-4.8.4.tgz", + "integrity": "sha512-LA4ZjwlnUblHVgq0oBF3Jl/6h/Nvs5fzBLwdEF4nuxnFdsfajde4WfxtJr3CaiH+F6ewcIB/q4jQ4UzPyid+CQ==", + "dev": true, + "license": "MIT", + "optional": true, + "bin": { + "node-gyp-build": "bin.js", + "node-gyp-build-optional": "optional.js", + "node-gyp-build-test": "build-test.js" + } + }, "node_modules/node-gyp-build-optional-packages": { "version": "5.2.2", "resolved": "https://registry.npmjs.org/node-gyp-build-optional-packages/-/node-gyp-build-optional-packages-5.2.2.tgz", @@ -18766,13 +19331,13 @@ } }, "node_modules/oxc-parser": { - "version": "0.133.0", - "resolved": "https://registry.npmjs.org/oxc-parser/-/oxc-parser-0.133.0.tgz", - "integrity": "sha512-661RSx+ZcjBmjBYid+Fpp/2F5EbtildpeoZh5HdgnGs+jZ03nqQEQW8yGkt4BGyOC3OMPDQQRl8M5kqD2/g6jw==", + "version": "0.137.0", + "resolved": "https://registry.npmjs.org/oxc-parser/-/oxc-parser-0.137.0.tgz", + "integrity": "sha512-yFImD+WLElJpLKy8llG1qe4DCmMsL18peRp8XP1JKfig/gISbJkglnpDtX2aTmAn10kZF7164HbN2H8QPsXxGg==", "dev": true, "license": "MIT", "dependencies": { - "@oxc-project/types": "^0.133.0" + "@oxc-project/types": "^0.137.0" }, "engines": { "node": "^20.19.0 || >=22.12.0" @@ -18781,57 +19346,57 @@ "url": "https://github.com/sponsors/Boshen" }, "optionalDependencies": { - "@oxc-parser/binding-android-arm-eabi": "0.133.0", - "@oxc-parser/binding-android-arm64": "0.133.0", - "@oxc-parser/binding-darwin-arm64": "0.133.0", - "@oxc-parser/binding-darwin-x64": "0.133.0", - "@oxc-parser/binding-freebsd-x64": "0.133.0", - "@oxc-parser/binding-linux-arm-gnueabihf": "0.133.0", - "@oxc-parser/binding-linux-arm-musleabihf": "0.133.0", - "@oxc-parser/binding-linux-arm64-gnu": "0.133.0", - "@oxc-parser/binding-linux-arm64-musl": "0.133.0", - "@oxc-parser/binding-linux-ppc64-gnu": "0.133.0", - "@oxc-parser/binding-linux-riscv64-gnu": "0.133.0", - "@oxc-parser/binding-linux-riscv64-musl": "0.133.0", - "@oxc-parser/binding-linux-s390x-gnu": "0.133.0", - "@oxc-parser/binding-linux-x64-gnu": "0.133.0", - "@oxc-parser/binding-linux-x64-musl": "0.133.0", - "@oxc-parser/binding-openharmony-arm64": "0.133.0", - "@oxc-parser/binding-wasm32-wasi": "0.133.0", - "@oxc-parser/binding-win32-arm64-msvc": "0.133.0", - "@oxc-parser/binding-win32-ia32-msvc": "0.133.0", - "@oxc-parser/binding-win32-x64-msvc": "0.133.0" + "@oxc-parser/binding-android-arm-eabi": "0.137.0", + "@oxc-parser/binding-android-arm64": "0.137.0", + "@oxc-parser/binding-darwin-arm64": "0.137.0", + "@oxc-parser/binding-darwin-x64": "0.137.0", + "@oxc-parser/binding-freebsd-x64": "0.137.0", + "@oxc-parser/binding-linux-arm-gnueabihf": "0.137.0", + "@oxc-parser/binding-linux-arm-musleabihf": "0.137.0", + "@oxc-parser/binding-linux-arm64-gnu": "0.137.0", + "@oxc-parser/binding-linux-arm64-musl": "0.137.0", + "@oxc-parser/binding-linux-ppc64-gnu": "0.137.0", + "@oxc-parser/binding-linux-riscv64-gnu": "0.137.0", + "@oxc-parser/binding-linux-riscv64-musl": "0.137.0", + "@oxc-parser/binding-linux-s390x-gnu": "0.137.0", + "@oxc-parser/binding-linux-x64-gnu": "0.137.0", + "@oxc-parser/binding-linux-x64-musl": "0.137.0", + "@oxc-parser/binding-openharmony-arm64": "0.137.0", + "@oxc-parser/binding-wasm32-wasi": "0.137.0", + "@oxc-parser/binding-win32-arm64-msvc": "0.137.0", + "@oxc-parser/binding-win32-ia32-msvc": "0.137.0", + "@oxc-parser/binding-win32-x64-msvc": "0.137.0" } }, "node_modules/oxc-resolver": { - "version": "11.20.0", - "resolved": "https://registry.npmjs.org/oxc-resolver/-/oxc-resolver-11.20.0.tgz", - "integrity": "sha512-CblytBiV/a/ZXY34dsVU2NxhIOxMXst8CvDCtyBelVITgd7PLrKzbEbA6oKLdPjvDKDzCiW48qzmzZ+mYaqn+g==", + "version": "11.21.3", + "resolved": "https://registry.npmjs.org/oxc-resolver/-/oxc-resolver-11.21.3.tgz", + "integrity": "sha512-2Mx3fKQz7+xgrBONjsxOgCGtMHOn38/HxMzW1I5efwXB5a4lRN0Vp40gYUJFBWJslcrvwoofTrqoTnLbwTd3pA==", "dev": true, "license": "MIT", "funding": { "url": "https://github.com/sponsors/Boshen" }, "optionalDependencies": { - "@oxc-resolver/binding-android-arm-eabi": "11.20.0", - "@oxc-resolver/binding-android-arm64": "11.20.0", - "@oxc-resolver/binding-darwin-arm64": "11.20.0", - "@oxc-resolver/binding-darwin-x64": "11.20.0", - "@oxc-resolver/binding-freebsd-x64": "11.20.0", - "@oxc-resolver/binding-linux-arm-gnueabihf": "11.20.0", - "@oxc-resolver/binding-linux-arm-musleabihf": "11.20.0", - "@oxc-resolver/binding-linux-arm64-gnu": "11.20.0", - "@oxc-resolver/binding-linux-arm64-musl": "11.20.0", - "@oxc-resolver/binding-linux-ppc64-gnu": "11.20.0", - "@oxc-resolver/binding-linux-riscv64-gnu": "11.20.0", - "@oxc-resolver/binding-linux-riscv64-musl": "11.20.0", - "@oxc-resolver/binding-linux-s390x-gnu": "11.20.0", - "@oxc-resolver/binding-linux-x64-gnu": "11.20.0", - "@oxc-resolver/binding-linux-x64-musl": "11.20.0", - "@oxc-resolver/binding-openharmony-arm64": "11.20.0", - "@oxc-resolver/binding-wasm32-wasi": "11.20.0", - "@oxc-resolver/binding-win32-arm64-msvc": "11.20.0", - "@oxc-resolver/binding-win32-x64-msvc": "11.20.0" + "@oxc-resolver/binding-android-arm-eabi": "11.21.3", + "@oxc-resolver/binding-android-arm64": "11.21.3", + "@oxc-resolver/binding-darwin-arm64": "11.21.3", + "@oxc-resolver/binding-darwin-x64": "11.21.3", + "@oxc-resolver/binding-freebsd-x64": "11.21.3", + "@oxc-resolver/binding-linux-arm-gnueabihf": "11.21.3", + "@oxc-resolver/binding-linux-arm-musleabihf": "11.21.3", + "@oxc-resolver/binding-linux-arm64-gnu": "11.21.3", + "@oxc-resolver/binding-linux-arm64-musl": "11.21.3", + "@oxc-resolver/binding-linux-ppc64-gnu": "11.21.3", + "@oxc-resolver/binding-linux-riscv64-gnu": "11.21.3", + "@oxc-resolver/binding-linux-riscv64-musl": "11.21.3", + "@oxc-resolver/binding-linux-s390x-gnu": "11.21.3", + "@oxc-resolver/binding-linux-x64-gnu": "11.21.3", + "@oxc-resolver/binding-linux-x64-musl": "11.21.3", + "@oxc-resolver/binding-openharmony-arm64": "11.21.3", + "@oxc-resolver/binding-wasm32-wasi": "11.21.3", + "@oxc-resolver/binding-win32-arm64-msvc": "11.21.3", + "@oxc-resolver/binding-win32-x64-msvc": "11.21.3" } }, "node_modules/p-limit": { @@ -18912,7 +19477,8 @@ "version": "1.0.11", "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz", "integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==", - "dev": true + "dev": true, + "license": "(MIT AND Zlib)" }, "node_modules/parent-module": { "version": "1.0.1", @@ -19233,10 +19799,11 @@ "integrity": "sha512-J8xLz7q2OFulZ2cyGTLE1TbbZcjpno7FaN6zdJNrgAdrJ+DZzh/uFR6YrTb4C+nXakvud8Q4+rbhoIWlYQbUFQ==" }, "node_modules/pirates": { - "version": "4.0.6", - "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.6.tgz", - "integrity": "sha512-saLsH7WeYYPiD25LDuLRRY/i+6HaPYr6G1OUlN39otzkSTxKnubR9RTxS3/Kk50s1g2JTgFwWQDQyplC5/SHZg==", + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.7.tgz", + "integrity": "sha512-TfySrs/5nm8fQJDcBDuUng3VOUKsd7S+zqvbOTiGXHfxX4wK31ard+hoNuvkicM/2YFzlpDgABOevKSsB4G/FA==", "dev": true, + "license": "MIT", "engines": { "node": ">= 6" } @@ -19246,6 +19813,7 @@ "resolved": "https://registry.npmjs.org/piscina/-/piscina-3.2.0.tgz", "integrity": "sha512-yn/jMdHRw+q2ZJhFhyqsmANcbF6V2QwmD84c6xRau+QpQOmtrBCoRGdvTfeuFDYXB5W2m6MfLkjkvQa9lUSmIA==", "dev": true, + "license": "MIT", "dependencies": { "eventemitter-asyncresource": "^1.0.0", "hdr-histogram-js": "^2.0.1", @@ -19325,13 +19893,13 @@ } }, "node_modules/playwright": { - "version": "1.61.0", - "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.61.0.tgz", - "integrity": "sha512-Z+7BeeqQPRRzklHsVFP4KTGIyMxKUmfeRA4WisM6G3/XW6nwGeX6fX9qYaDa+CiUqpOkb2f6X3nar05R3kSuJQ==", + "version": "1.61.1", + "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.61.1.tgz", + "integrity": "sha512-DWnY5o3YbLWK4GovuAVwpqL+1VwGNdUGrRr++8j8PtQQzvAVZUIMjKQ90fY689sEJZJBbZVw1rXaOKSTitkzPQ==", "dev": true, "license": "Apache-2.0", "dependencies": { - "playwright-core": "1.61.0" + "playwright-core": "1.61.1" }, "bin": { "playwright": "cli.js" @@ -19344,9 +19912,9 @@ } }, "node_modules/playwright-core": { - "version": "1.61.0", - "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.61.0.tgz", - "integrity": "sha512-caX7TrY3Ml6egyDX0WUcTHDxodl/b51y5wJOdCEA36QviK/s2g081hvmGs8eaE3DWb6NYZQ6BjO/QkNRPenoPA==", + "version": "1.61.1", + "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.61.1.tgz", + "integrity": "sha512-h7Qlt6m4REp25qvIdvbDtVmD4LqVXfpRxhORv9L0jzETM05p4fuPJ3dKyuSXQxDSbXnmS79HAgi9589lGSpLkg==", "dev": true, "license": "Apache-2.0", "bin": { @@ -19471,9 +20039,9 @@ } }, "node_modules/protobufjs": { - "version": "7.6.4", - "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-7.6.4.tgz", - "integrity": "sha512-RJJPTTpvFfHcWLkIa2JFWK4XvtSzS0yEWDmunqHXli1h3JlkbcQZXDZdcWxv+JK3Xsl5/UFDPZ0iGm7DAengYw==", + "version": "7.6.5", + "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-7.6.5.tgz", + "integrity": "sha512-/FPD0nUc9jH6rfFjji9IBqOz4pcSE3CsT1m7Ep6Mdb0LxSUMj8hgl6GomOvZzpNpAqqGaXA0P3VSrZLFzIhQrw==", "hasInstallScript": true, "license": "BSD-3-Clause", "dependencies": { @@ -19910,9 +20478,9 @@ } }, "node_modules/rimraf/node_modules/brace-expansion": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.3.tgz", - "integrity": "sha512-MCV/fYJEbqx68aE58kv2cA/kiky1G8vux3OR6/jbS+jIMe/6fJWa0DTzJU7dqijOWYwHi1t29FlfYI9uytqlpA==", + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.1.3.tgz", + "integrity": "sha512-DRdx5neNsG/QXbniLFWi2YmC/68oeOOmKz6zOjVk6ZS1ZLXgLIKqVEc6hWsmkjBbgii0SwaBTcJ5XKj5gzY/4A==", "license": "MIT", "dependencies": { "balanced-match": "^1.0.0" @@ -20484,6 +21052,13 @@ "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==", "dev": true }, + "node_modules/stable-hash": { + "version": "0.0.5", + "resolved": "https://registry.npmjs.org/stable-hash/-/stable-hash-0.0.5.tgz", + "integrity": "sha512-+L3ccpzibovGXFK+Ap/f8LOS0ahMrHTf3xu7mMLSpEGU0EO9ucaysSylKo9eRDFNhWve/y275iPmIZ4z39a9iA==", + "dev": true, + "license": "MIT" + }, "node_modules/stack-utils": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-2.0.6.tgz", @@ -20852,15 +21427,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/tapable": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.1.tgz", - "integrity": "sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==", - "dev": true, - "engines": { - "node": ">=6" - } - }, "node_modules/teeny-request": { "version": "10.1.2", "resolved": "https://registry.npmjs.org/teeny-request/-/teeny-request-10.1.2.tgz", @@ -21075,10 +21641,11 @@ } }, "node_modules/ts-node": { - "version": "10.9.1", - "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.1.tgz", - "integrity": "sha512-NtVysVPkxxrwFGUUxGYhfux8k78pQB3JqYBXlLRZgdGUqTO5wU/UyHop5p70iEbGhB7q5KmiZiU0Y3KlJrScEw==", + "version": "10.9.2", + "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.2.tgz", + "integrity": "sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ==", "dev": true, + "license": "MIT", "dependencies": { "@cspotcode/source-map-support": "^0.8.0", "@tsconfig/node10": "^1.0.7", @@ -21221,17 +21788,34 @@ } }, "node_modules/type-is": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/type-is/-/type-is-2.0.1.tgz", - "integrity": "sha512-OZs6gsjF4vMp32qrCbiVSkrFmXtG/AZhY3t0iAMrMBiAZyV9oALtXO8hsrHbMXF9x6L3grlFuwW2oAz7cav+Gw==", + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-2.1.0.tgz", + "integrity": "sha512-faYHw0anBbc/kWF3zFTEnxSFOAGUX9GFbOBthvDdLsIlEoWOFOtS0zgCiQYwIskL9iGXZL3kAXD8OoZ4GmMATA==", "license": "MIT", "dependencies": { - "content-type": "^1.0.5", + "content-type": "^2.0.0", "media-typer": "^1.1.0", "mime-types": "^3.0.0" }, "engines": { - "node": ">= 0.6" + "node": ">= 18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/type-is/node_modules/content-type": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-2.0.0.tgz", + "integrity": "sha512-j/O/d7GcZCyNl7/hwZAb606rzqkyvaDctLmckbxLzHvFBzTJHuGEdodATcP3yIRoDrLHkIATJuvzbFlp/ki2cQ==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" } }, "node_modules/type-is/node_modules/mime-db": { @@ -21341,6 +21925,7 @@ "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.5.2.tgz", "integrity": "sha512-NcRtPEOsPFFWjobJEtfihkLCZCXZt/os3zf8nTxjVH3RvTSxjrCamJpbExGvYOF+tFHc3pA65qpdwPbzjohhew==", "dev": true, + "license": "Apache-2.0", "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" @@ -21350,113 +21935,16 @@ } }, "node_modules/typescript-eslint": { - "version": "8.57.2", - "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.57.2.tgz", - "integrity": "sha512-VEPQ0iPgWO/sBaZOU1xo4nuNdODVOajPnTIbog2GKYr31nIlZ0fWPoCQgGfF3ETyBl1vn63F/p50Um9Z4J8O8A==", - "dev": true, - "license": "MIT", - "dependencies": { - "@typescript-eslint/eslint-plugin": "8.57.2", - "@typescript-eslint/parser": "8.57.2", - "@typescript-eslint/typescript-estree": "8.57.2", - "@typescript-eslint/utils": "8.57.2" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", - "typescript": ">=4.8.4 <6.0.0" - } - }, - "node_modules/typescript-eslint/node_modules/@typescript-eslint/eslint-plugin": { - "version": "8.57.2", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.57.2.tgz", - "integrity": "sha512-NZZgp0Fm2IkD+La5PR81sd+g+8oS6JwJje+aRWsDocxHkjyRw0J5L5ZTlN3LI1LlOcGL7ph3eaIUmTXMIjLk0w==", - "dev": true, - "license": "MIT", - "dependencies": { - "@eslint-community/regexpp": "^4.12.2", - "@typescript-eslint/scope-manager": "8.57.2", - "@typescript-eslint/type-utils": "8.57.2", - "@typescript-eslint/utils": "8.57.2", - "@typescript-eslint/visitor-keys": "8.57.2", - "ignore": "^7.0.5", - "natural-compare": "^1.4.0", - "ts-api-utils": "^2.4.0" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "@typescript-eslint/parser": "^8.57.2", - "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", - "typescript": ">=4.8.4 <6.0.0" - } - }, - "node_modules/typescript-eslint/node_modules/@typescript-eslint/parser": { - "version": "8.57.2", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.57.2.tgz", - "integrity": "sha512-30ScMRHIAD33JJQkgfGW1t8CURZtjc2JpTrq5n2HFhOefbAhb7ucc7xJwdWcrEtqUIYJ73Nybpsggii6GtAHjA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@typescript-eslint/scope-manager": "8.57.2", - "@typescript-eslint/types": "8.57.2", - "@typescript-eslint/typescript-estree": "8.57.2", - "@typescript-eslint/visitor-keys": "8.57.2", - "debug": "^4.4.3" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", - "typescript": ">=4.8.4 <6.0.0" - } - }, - "node_modules/typescript-eslint/node_modules/@typescript-eslint/scope-manager": { - "version": "8.57.2", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.57.2.tgz", - "integrity": "sha512-snZKH+W4WbWkrBqj4gUNRIGb/jipDW3qMqVJ4C9rzdFc+wLwruxk+2a5D+uoFcKPAqyqEnSb4l2ULuZf95eSkw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@typescript-eslint/types": "8.57.2", - "@typescript-eslint/visitor-keys": "8.57.2" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, - "node_modules/typescript-eslint/node_modules/@typescript-eslint/type-utils": { - "version": "8.57.2", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.57.2.tgz", - "integrity": "sha512-Co6ZCShm6kIbAM/s+oYVpKFfW7LBc6FXoPXjTRQ449PPNBY8U0KZXuevz5IFuuUj2H9ss40atTaf9dlGLzbWZg==", + "version": "8.65.0", + "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.65.0.tgz", + "integrity": "sha512-/ggrHAwyjENDusvyxbuqxAC2dTnZg/Z8F+fgQtYIz+L6n/9HfSlEZcFGV/NsMNa6CkGk0xUjUAFwC0vHOflvIA==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.57.2", - "@typescript-eslint/typescript-estree": "8.57.2", - "@typescript-eslint/utils": "8.57.2", - "debug": "^4.4.3", - "ts-api-utils": "^2.4.0" + "@typescript-eslint/eslint-plugin": "8.65.0", + "@typescript-eslint/parser": "8.65.0", + "@typescript-eslint/typescript-estree": "8.65.0", + "@typescript-eslint/utils": "8.65.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -21467,13 +21955,13 @@ }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", - "typescript": ">=4.8.4 <6.0.0" + "typescript": ">=4.8.4 <6.1.0" } }, "node_modules/typescript-eslint/node_modules/@typescript-eslint/types": { - "version": "8.57.2", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.57.2.tgz", - "integrity": "sha512-/iZM6FnM4tnx9csuTxspMW4BOSegshwX5oBDznJ7S4WggL7Vczz5d2W11ecc4vRrQMQHXRSxzrCsyG5EsPPTbA==", + "version": "8.65.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.65.0.tgz", + "integrity": "sha512-JSSwWNy+H0E/01jJEM+hrX6N0OFDzFzeIhHFSAS01tlVaevpG8cFyYRPhS5yjGOvBUx3sqQHVMjCL1CAZZMxBg==", "dev": true, "license": "MIT", "engines": { @@ -21485,44 +21973,21 @@ } }, "node_modules/typescript-eslint/node_modules/@typescript-eslint/typescript-estree": { - "version": "8.57.2", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.57.2.tgz", - "integrity": "sha512-2MKM+I6g8tJxfSmFKOnHv2t8Sk3T6rF20A1Puk0svLK+uVapDZB/4pfAeB7nE83uAZrU6OxW+HmOd5wHVdXwXA==", + "version": "8.65.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.65.0.tgz", + "integrity": "sha512-JboAE2swaYt4tb1fHhHTABE2K+OLy09XfcTbhnk4Pw96f9dd2e9iYsJ28gBggHlo5z5x1rkyWvcPoTuNTd4oGg==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/project-service": "8.57.2", - "@typescript-eslint/tsconfig-utils": "8.57.2", - "@typescript-eslint/types": "8.57.2", - "@typescript-eslint/visitor-keys": "8.57.2", + "@typescript-eslint/project-service": "8.65.0", + "@typescript-eslint/tsconfig-utils": "8.65.0", + "@typescript-eslint/types": "8.65.0", + "@typescript-eslint/visitor-keys": "8.65.0", "debug": "^4.4.3", "minimatch": "^10.2.2", "semver": "^7.7.3", "tinyglobby": "^0.2.15", - "ts-api-utils": "^2.4.0" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "typescript": ">=4.8.4 <6.0.0" - } - }, - "node_modules/typescript-eslint/node_modules/@typescript-eslint/utils": { - "version": "8.57.2", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.57.2.tgz", - "integrity": "sha512-krRIbvPK1ju1WBKIefiX+bngPs+odIQUtR7kymzPfo1POVw3jlF+nLkmexdSSd4UCbDcQn+wMBATOOmpBbqgKg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@eslint-community/eslint-utils": "^4.9.1", - "@typescript-eslint/scope-manager": "8.57.2", - "@typescript-eslint/types": "8.57.2", - "@typescript-eslint/typescript-estree": "8.57.2" + "ts-api-utils": "^2.5.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -21532,18 +21997,17 @@ "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", - "typescript": ">=4.8.4 <6.0.0" + "typescript": ">=4.8.4 <6.1.0" } }, "node_modules/typescript-eslint/node_modules/@typescript-eslint/visitor-keys": { - "version": "8.57.2", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.57.2.tgz", - "integrity": "sha512-zhahknjobV2FiD6Ee9iLbS7OV9zi10rG26odsQdfBO/hjSzUQbkIYgda+iNKK1zNiW2ey+Lf8MU5btN17V3dUw==", + "version": "8.65.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.65.0.tgz", + "integrity": "sha512-8C71BQkGjiMmXtop7pHVJu1l2NNShFdkCyD6a2ezzs5vU/L3LRtb69EtcteFwz0mYMPzIgOw0n6OV4VBUWZd7A==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.57.2", + "@typescript-eslint/types": "8.65.0", "eslint-visitor-keys": "^5.0.0" }, "engines": { @@ -21565,16 +22029,16 @@ } }, "node_modules/typescript-eslint/node_modules/brace-expansion": { - "version": "5.0.6", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.6.tgz", - "integrity": "sha512-kLpxurY4Z4r9sgMsyG0Z9uzsBlgiU/EFKhj/h91/8yHu0edo7XuixOIH3VcJ8kkxs6/jPzoI6U9Vj3WqbMQ94g==", + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.8.tgz", + "integrity": "sha512-JZyDyq3D4AUifKTPOB7DELf6XsB3WdPuNxCtob1vFXPsSXhdAiHBWJ/tJ8HAc9aH84BK+5JFZLNkJKx3G9kzQg==", "dev": true, "license": "MIT", "dependencies": { "balanced-match": "^4.0.2" }, "engines": { - "node": "18 || 20 || >=22" + "node": "20 || >=22" } }, "node_modules/typescript-eslint/node_modules/eslint-visitor-keys": { @@ -21590,24 +22054,14 @@ "url": "https://opencollective.com/eslint" } }, - "node_modules/typescript-eslint/node_modules/ignore": { - "version": "7.0.5", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.5.tgz", - "integrity": "sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 4" - } - }, "node_modules/typescript-eslint/node_modules/minimatch": { - "version": "10.2.4", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.4.tgz", - "integrity": "sha512-oRjTw/97aTBN0RHbYCdtF1MQfvusSIBQM0IZEgzl6426+8jSC0nF1a/GmnVLpfB9yyr6g6FTqWqiZVbxrtaCIg==", + "version": "10.2.6", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.6.tgz", + "integrity": "sha512-vpLQEs+VLCr1nU0BXS07maYoFwlDAH0gngQuuttxIwutDFEMHq2blX+8vpgxDdK3J1PwjCJiep77OitTZ4Ll1A==", "dev": true, "license": "BlueOak-1.0.0", "dependencies": { - "brace-expansion": "^5.0.2" + "brace-expansion": "^5.0.8" }, "engines": { "node": "18 || 20 || >=22" @@ -21652,9 +22106,9 @@ } }, "node_modules/unbash": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/unbash/-/unbash-3.0.0.tgz", - "integrity": "sha512-FeFPZ/WFT0mbRCuydiZzpPFlrYN8ZUpphQKoq4EeElVIYjYyGzPMxQR/simUwCOJIyVhpFk4RbtyO7RuMpMnHA==", + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/unbash/-/unbash-4.0.4.tgz", + "integrity": "sha512-60m9IVGbavD6jholbxt0jVBXZkEB/HsMZq7Tyaghseve2/Sf0zQRAIfWsD34sde+DKP2tBxJS2wP88ZM0D1FhA==", "dev": true, "license": "ISC", "engines": { @@ -21689,6 +22143,12 @@ "node": ">=20.18.1" } }, + "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==", + "license": "MIT" + }, "node_modules/unhomoglyph": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/unhomoglyph/-/unhomoglyph-1.0.6.tgz", @@ -21703,6 +22163,44 @@ "node": ">= 0.8" } }, + "node_modules/unrs-resolver": { + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/unrs-resolver/-/unrs-resolver-1.12.2.tgz", + "integrity": "sha512-dmlRxBJJayXjqTwC+JtF1HhJmgf3ftQ3YejFcZrf4+KKtJv0qDsK1pjqaaVjG7wJ5NJ6UVP1OqRMQ71Z4C3rxQ==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "dependencies": { + "napi-postinstall": "^0.3.4" + }, + "funding": { + "url": "https://opencollective.com/unrs-resolver" + }, + "optionalDependencies": { + "@unrs/resolver-binding-android-arm-eabi": "1.12.2", + "@unrs/resolver-binding-android-arm64": "1.12.2", + "@unrs/resolver-binding-darwin-arm64": "1.12.2", + "@unrs/resolver-binding-darwin-x64": "1.12.2", + "@unrs/resolver-binding-freebsd-x64": "1.12.2", + "@unrs/resolver-binding-linux-arm-gnueabihf": "1.12.2", + "@unrs/resolver-binding-linux-arm-musleabihf": "1.12.2", + "@unrs/resolver-binding-linux-arm64-gnu": "1.12.2", + "@unrs/resolver-binding-linux-arm64-musl": "1.12.2", + "@unrs/resolver-binding-linux-loong64-gnu": "1.12.2", + "@unrs/resolver-binding-linux-loong64-musl": "1.12.2", + "@unrs/resolver-binding-linux-ppc64-gnu": "1.12.2", + "@unrs/resolver-binding-linux-riscv64-gnu": "1.12.2", + "@unrs/resolver-binding-linux-riscv64-musl": "1.12.2", + "@unrs/resolver-binding-linux-s390x-gnu": "1.12.2", + "@unrs/resolver-binding-linux-x64-gnu": "1.12.2", + "@unrs/resolver-binding-linux-x64-musl": "1.12.2", + "@unrs/resolver-binding-openharmony-arm64": "1.12.2", + "@unrs/resolver-binding-wasm32-wasi": "1.12.2", + "@unrs/resolver-binding-win32-arm64-msvc": "1.12.2", + "@unrs/resolver-binding-win32-ia32-msvc": "1.12.2", + "@unrs/resolver-binding-win32-x64-msvc": "1.12.2" + } + }, "node_modules/untildify": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/untildify/-/untildify-4.0.0.tgz", diff --git a/server/package.json b/server/package.json index cd0d3303c..ad42a8135 100644 --- a/server/package.json +++ b/server/package.json @@ -6,23 +6,23 @@ "scripts": { "build": "tsc && npm run copy-assets", "copy-assets": "copyfiles \"lib/**/*.lua\" transpiled/", - "start": "npm run copy-assets && tsc-watch --onSuccess \"node --trace-warnings --require dotenv/config ./transpiled/bin/www.js\"", - "start:trace": "npm run copy-assets && tsc-watch --onSuccess \"node --trace-warnings --require dotenv/config --require ../nodejs-instrumentation/transpiled/autoinstrumentation.js ./transpiled/bin/www.js\"", + "start": "npm run copy-assets && tsc-watch --onSuccess \"node --trace-warnings --env-file-if-exists=.env ./transpiled/bin/www.js\"", + "start:trace": "npm run copy-assets && tsc-watch --onSuccess \"node --trace-warnings --env-file-if-exists=.env --require ../nodejs-instrumentation/transpiled/autoinstrumentation.js ./transpiled/bin/www.js\"", "test": "npm run test:local", - "test:local": "NODE_OPTIONS=\"--no-warnings --loader ts-node/esm --require dotenv/config\" jest --watch --detectOpenHandles", - "test:prepush": "NODE_OPTIONS=\"--no-warnings --loader ts-node/esm --require dotenv/config\" jest --detectOpenHandles --no-cache --forceExit", - "test:ci": "NODE_OPTIONS=\"--loader ts-node/esm\" jest --ci --reporters=default --silent=false --reporters=jest-junit --no-cache --forceExit --runInBand", - "test:integ": "NODE_OPTIONS=\"--loader ts-node/esm\" jest --ci --reporters=default --silent=false --reporters=jest-junit --detectOpenHandles --no-cache --forceExit --runInBand --config jest.integ.config.cjs", + "test:local": "NODE_OPTIONS=\"--no-warnings --loader ts-node/esm\" node --env-file-if-exists=.env node_modules/.bin/jest --watch --detectOpenHandles", + "test:prepush": "NODE_OPTIONS=\"--no-warnings --loader ts-node/esm\" node --env-file-if-exists=.env node_modules/.bin/jest --detectOpenHandles --no-cache --forceExit", + "test:ci": "NODE_OPTIONS=\"--loader ts-node/esm\" node --env-file-if-exists=.env node_modules/.bin/jest --ci --reporters=default --silent=false --reporters=jest-junit --no-cache --forceExit --runInBand", + "test:integ": "NODE_OPTIONS=\"--loader ts-node/esm\" node --env-file-if-exists=.env node_modules/.bin/jest --ci --reporters=default --silent=false --reporters=jest-junit --detectOpenHandles --no-cache --forceExit --runInBand --config jest.integ.config.cjs", "typecheck": "tsc --noEmit", - "test:e2e": "playwright test --config e2e/playwright.config.ts", - "test:e2e:ui": "playwright test --config e2e/playwright.config.ts --ui", + "test:e2e": "node --env-file-if-exists=.env node_modules/.bin/playwright test --config e2e/playwright.config.ts", + "test:e2e:ui": "node --env-file-if-exists=.env node_modules/.bin/playwright test --config e2e/playwright.config.ts --ui", "e2e:install-browsers": "playwright install --with-deps chromium", "check:prepush": "npm run typecheck && npm run test:prepush", "lint": "eslint \"./**/*.{ts,tsx,js}\"", - "runWorkerOrJob": "node --loader ts-node/esm --require dotenv/config bin/run-worker-or-job.ts", - "create-org": "node --loader ts-node/esm --require dotenv/config bin/create-org-and-user.ts", - "get-invite": "node --loader ts-node/esm --require dotenv/config bin/get-invite-token.ts", - "recover-mrt-queue": "node --loader ts-node/esm --require dotenv/config bin/recover-mrt-queue.ts", + "runWorkerOrJob": "node --env-file-if-exists=.env --loader ts-node/esm bin/run-worker-or-job.ts", + "create-org": "node --env-file-if-exists=.env --loader ts-node/esm bin/create-org-and-user.ts", + "get-invite": "node --env-file-if-exists=.env --loader ts-node/esm bin/get-invite-token.ts", + "recover-mrt-queue": "node --env-file-if-exists=.env --loader ts-node/esm bin/recover-mrt-queue.ts", "knip": "knip" }, "author": "Roostorg", @@ -122,7 +122,6 @@ "@typescript-eslint/eslint-plugin": "^8.57.2", "@typescript-eslint/parser": "^8.57.2", "copyfiles": "^2.4.1", - "dotenv": "^10.0.0", "eslint": "^9.39.4", "eslint-import-resolver-typescript": "^3.6.0", "eslint-plugin-functional": "^9.0.4", @@ -131,17 +130,17 @@ "eslint-plugin-node": "^11.1.0", "eslint-plugin-promise": "^7.2.1", "eslint-plugin-security": "^1.7.1", - "eslint-plugin-switch-statement": "^0.0.11", + "eslint-plugin-switch-statement": "^0.0.12", "jest": "^29.3.1", "jest-junit": "^16.0.0", "jest-light-runner": "^0.4.1", - "js-yaml": "^4.2.0", + "js-yaml": "^5.2.2", "jsonpath-plus": "^10.4.0", "knip": "^6.16.1", "supertest": "^6.2.2", "ts-node": "^10.9.1", "tsc-watch": "^4.6.0", - "typescript": "^5.5.2", + "typescript": "5.5.2", "typescript-eslint": "^8.57.2" }, "jest-junit": { @@ -162,8 +161,8 @@ }, "uuid": "^14.0.0", "fast-xml-parser": "^5.8.0", - "axios": "^1.16.0", - "fast-uri": "^3.1.2", + "axios": "^1.18.1", + "fast-uri": "^3.1.4", "protobufjs": "^7.6.3", "@protobufjs/utf8": "^1.1.1" } diff --git a/server/plugins/warehouse/queries/IActionStatisticsAdapter.ts b/server/plugins/warehouse/queries/IActionStatisticsAdapter.ts index 1d358c3a3..9bbd85a20 100644 --- a/server/plugins/warehouse/queries/IActionStatisticsAdapter.ts +++ b/server/plugins/warehouse/queries/IActionStatisticsAdapter.ts @@ -3,17 +3,10 @@ import { type ReadonlyDeep } from 'type-fest'; export type ActionStatisticsTimeDivisionOptions = 'DAY' | 'HOUR'; export type ActionExecutionsGroupByAllowedFields = - | 'RULE_ID' - | 'ACTION_ID' - | 'ITEM_TYPE_ID' - | 'ACTION_SOURCE' - | 'POLICY_ID'; + 'RULE_ID' | 'ACTION_ID' | 'ITEM_TYPE_ID' | 'ACTION_SOURCE' | 'POLICY_ID'; export type ActionSourceOptions = - | 'automated-rule' - | 'mrt-decision' - | 'manual-action-run' - | 'post-actions'; + 'automated-rule' | 'mrt-decision' | 'manual-action-run' | 'post-actions'; export type ActionCountsInput = ReadonlyDeep<{ orgId: string; diff --git a/server/routes/items/submitItems.ts b/server/routes/items/submitItems.ts index c02e3a049..33ac96dd2 100644 --- a/server/routes/items/submitItems.ts +++ b/server/routes/items/submitItems.ts @@ -109,8 +109,7 @@ Dependencies): RequestHandlerWithBodies { ) { try { const images = itemSubmission.itemSubmission.data.images as ( - | string - | { url: string; [key: string]: unknown } + string | { url: string; [key: string]: unknown } )[]; // Get all hash banks for this org once diff --git a/server/routes/reporting/ReportingRoutes.ts b/server/routes/reporting/ReportingRoutes.ts index 56bac5987..252d1bfd5 100644 --- a/server/routes/reporting/ReportingRoutes.ts +++ b/server/routes/reporting/ReportingRoutes.ts @@ -12,8 +12,7 @@ import submitReport from './submitReport.js'; export type ReportItemInput = { reporter: - | { kind: 'rule'; id: string } - | { kind: 'user'; typeId: string; id: string }; + { kind: 'rule'; id: string } | { kind: 'user'; typeId: string; id: string }; reportedAt: string; reportedForReason?: { policyId?: string | null; diff --git a/server/scylla/cqlUtils.ts b/server/scylla/cqlUtils.ts index b9a39a89d..52f77cc10 100644 --- a/server/scylla/cqlUtils.ts +++ b/server/scylla/cqlUtils.ts @@ -23,8 +23,7 @@ type SelectClause = readonly [ ]; type Selector = - | Cols - | { aggregate: 'count' | 'sum' | 'avg' | 'min' | 'max'; col: Cols }; + Cols | { aggregate: 'count' | 'sum' | 'avg' | 'min' | 'max'; col: Cols }; export type CqlSelectOptions< DBRelations extends DBDefinition, diff --git a/server/scylla/noOpScylla.test.ts b/server/scylla/noOpScylla.test.ts new file mode 100644 index 000000000..bbf1c4049 --- /dev/null +++ b/server/scylla/noOpScylla.test.ts @@ -0,0 +1,78 @@ +import NoOpScylla, { + itemInvestigationAndStrikesEnabled, +} from './noOpScylla.js'; +import Scylla from './scylla.js'; + +/** + * Tests for the Scylla-disabled path used when + * `ITEM_INVESTIGATION_AND_STRIKES_ENABLED=false`. + * + * Two things are covered: + * 1. The behavioural contract of {@link NoOpScylla} (drops writes, empty reads, + * connect/close resolve). + * 2. The exact flag-parsing predicate (`itemInvestigationAndStrikesEnabled`) + * used by the `Scylla` DI factory in `iocContainer` to decide + * enabled-vs-disabled. Imported directly (not mirrored) so the + * default-enabled (upstream-preserving) behaviour is guarded by a test. + */ + +describe('ITEM_INVESTIGATION_AND_STRIKES_ENABLED gate predicate', () => { + test('defaults to enabled when unset (preserves upstream behaviour)', () => { + expect(itemInvestigationAndStrikesEnabled(undefined)).toBe(true); + expect(itemInvestigationAndStrikesEnabled('')).toBe(true); + }); + + test('is disabled only for explicit falsey values', () => { + for (const v of ['false', 'FALSE', ' false ', '0', 'no', 'No']) { + expect(itemInvestigationAndStrikesEnabled(v)).toBe(false); + } + }); + + test('stays enabled for truthy / unrelated values', () => { + for (const v of ['true', 'TRUE', '1', 'yes', 'anything']) { + expect(itemInvestigationAndStrikesEnabled(v)).toBe(true); + } + }); +}); + +describe('NoOpScylla', () => { + // A minimal DB shape for the generic parameter. + type TestDB = { widgets: { id: number; name: string } }; + const noop = new NoOpScylla(); + + test('is a Scylla so it satisfies every consumer unchanged', () => { + expect(noop).toBeInstanceOf(Scylla); + }); + + test('connect() and close() resolve (eager callers proceed)', async () => { + await expect(noop.connect()).resolves.toBeUndefined(); + await expect(noop.close()).resolves.toBeUndefined(); + }); + + test('insert() resolves and drops the write', async () => { + await expect( + noop.insert({ into: 'widgets', row: { id: 1, name: 'a' } }), + ).resolves.toBeDefined(); + }); + + test('select() returns an empty result set', async () => { + await expect( + noop.select({ from: 'widgets', select: '*' }), + ).resolves.toEqual([]); + }); + + test('selectStream() yields nothing', async () => { + const rows = await (async () => { + const collected = []; + for await (const row of noop.selectStream({ + from: 'widgets', + select: '*', + })) { + // eslint-disable-next-line functional/immutable-data + collected.push(row); + } + return collected; + })(); + expect(rows).toEqual([]); + }); +}); diff --git a/server/scylla/noOpScylla.ts b/server/scylla/noOpScylla.ts new file mode 100644 index 000000000..99ab71fa7 --- /dev/null +++ b/server/scylla/noOpScylla.ts @@ -0,0 +1,104 @@ +import { type CqlSelectOptions, type DBDefinition } from './cqlUtils.js'; +import Scylla from './scylla.js'; + +/** + * Parses the `ITEM_INVESTIGATION_AND_STRIKES_ENABLED` feature flag from its raw + * string value (i.e. `process.env.ITEM_INVESTIGATION_AND_STRIKES_ENABLED`). + * + * Shared by the `Scylla` DI factory in `iocContainer` (to decide whether to + * return a real Scylla or a {@link NoOpScylla}) and by the unit tests. Defaults + * to enabled when unset/empty so existing deployments are unaffected; only the + * explicit falsey values `false`/`0`/`no` (case/whitespace-insensitive) disable + * the Scylla-backed features. + * + * Kept as a pure function of its argument (it does not read `process.env` + * itself) so callers own where the value comes from and tests stay independent + * of the ambient environment. + */ +export function itemInvestigationAndStrikesEnabled( + raw: string | undefined, +): boolean { + return !['false', '0', 'no'].includes((raw ?? 'true').trim().toLowerCase()); +} + +/** + * A no-op implementation of {@link Scylla} used when the Scylla-backed features + * (item investigation and user strikes) are disabled via + * `ITEM_INVESTIGATION_AND_STRIKES_ENABLED=false`. + * + * Scylla has no managed offering on some deployment platforms, and some + * operators do not need the features that depend on it. Rather than gate the + * ~100+ call sites that touch Scylla, we gate at the single dependency-injection + * chokepoint (the `Scylla` factory in `iocContainer`) and return this no-op. + * + * Behaviour when disabled: + * - `connect()` / `close()` resolve immediately (so the item-processing worker's + * eager `await scylla.connect()` succeeds without a real cluster). + * - `insert()` resolves and drops the write. + * - `select()` returns an empty result set. + * - `selectStream()` yields nothing. + * + * This keeps every consumer compiling and running unchanged; they simply observe + * empty data (e.g. user strike counts read as 0) and their writes are discarded. + */ +export default class NoOpScylla extends Scylla { + constructor() { + // The base class only stores the client and never touches it once all + // query methods are overridden below, so a null client cast is safe here. + // eslint-disable-next-line @typescript-eslint/no-explicit-any + super(null as any); + } + + /** No cluster to connect to; resolve so eager callers proceed. */ + async connect(): Promise { + return undefined; + } + + /** Nothing to shut down. */ + async close(): Promise { + return undefined; + } + + /** Drop the write. */ + override async insert( + _opts: { + [K in RelationName]: { + into: RelationName; + row: DB[K]; + ttlInSeconds?: number; + }; + }[RelationName], + ): Promise['insert']>>> { + // There is no cluster to write to; return a minimal empty result set. The + // real driver returns a full `ResultSet`, but no-op consumers never read + // the result, so a minimal shape is sufficient here. + // @ts-expect-error - minimal stand-in for the driver's ResultSet; unused by callers + return { rows: [], rowLength: 0 }; + } + + /** Return no rows. */ + override async select< + RelationName extends keyof DB & string, + Cols extends keyof DB[RelationName] & string = keyof DB[RelationName] & + string, + >( + _opts: CqlSelectOptions, + ): Promise<{ [K in Cols]: DB[RelationName][K] }[]> { + return []; + } + + /** Yield nothing. */ + override selectStream< + RelationName extends keyof DB & string, + Cols extends keyof DB[RelationName] & string = keyof DB[RelationName] & + string, + >( + _opts: CqlSelectOptions, + ): AsyncIterableIterator<{ [K in Cols]: DB[RelationName][K] }> { + type Selection = { [K in Cols]: DB[RelationName][K] }; + async function* empty(): AsyncIterableIterator { + // Intentionally yields nothing. + } + return empty(); + } +} diff --git a/server/scylla/types.ts b/server/scylla/types.ts index cda43ab73..1398cd261 100644 --- a/server/scylla/types.ts +++ b/server/scylla/types.ts @@ -28,8 +28,7 @@ export type ScyllaNilItemIdentifier = typeof ScyllaNilItemIdentifier; export const ScyllaNilItemIdentifier = { id: '', type_id: '' } as const; export type ScyllaItemIdentifier = - | ScyllaRealItemIdentifier - | ScyllaNilItemIdentifier; + ScyllaRealItemIdentifier | ScyllaNilItemIdentifier; export function isRealItemIdentifier( it: ScyllaItemIdentifier, diff --git a/server/services/analyticsLoggers/ruleExecutionLoggingUtils.ts b/server/services/analyticsLoggers/ruleExecutionLoggingUtils.ts index cc350cec8..9b4b0855f 100644 --- a/server/services/analyticsLoggers/ruleExecutionLoggingUtils.ts +++ b/server/services/analyticsLoggers/ruleExecutionLoggingUtils.ts @@ -43,8 +43,7 @@ export type ConditionSetWithResultAsLogged = Omit< }; export type ConditionWithResultAsLogged = - | ConditionSetWithResultAsLogged - | LeafConditionWithResultAsLogged; + ConditionSetWithResultAsLogged | LeafConditionWithResultAsLogged; // NB: we make these types to ensure, at the type level, that we're generating // correlation ids consistently for everything we log. @@ -162,8 +161,8 @@ export function pickLeafConditionPropsTolog( threshold: condition.threshold, input: condition.input, result: condition.result satisfies - | ReadonlyDeep - | undefined as ConditionResultAsLogged | undefined, + ReadonlyDeep | undefined as + ConditionResultAsLogged | undefined, signal: signal && { id: signal.id, type: signal.type, diff --git a/server/services/analyticsQueries/RuleActionInsights.ts b/server/services/analyticsQueries/RuleActionInsights.ts index 988a5c338..4e468847f 100644 --- a/server/services/analyticsQueries/RuleActionInsights.ts +++ b/server/services/analyticsQueries/RuleActionInsights.ts @@ -477,8 +477,7 @@ export default inject( export { type RuleActionInsights, type SignalWithScore }; type GatherSignalsConditionWithResult = - | GatherSignalsConditionSetWithResult - | GatherSignalsLeafConditionWithResult; + GatherSignalsConditionSetWithResult | GatherSignalsLeafConditionWithResult; type GatherSignalsLeafConditionWithResult = { signal?: { diff --git a/server/services/itemInvestigationService/itemInvestigationService.ts b/server/services/itemInvestigationService/itemInvestigationService.ts index 3b8f9177c..7b7ccce6e 100644 --- a/server/services/itemInvestigationService/itemInvestigationService.ts +++ b/server/services/itemInvestigationService/itemInvestigationService.ts @@ -573,8 +573,7 @@ export class ItemInvestigationService { const submissionId = record.submissionId as SubmissionId; const itemData = record.itemData as JsonOf; const schemaVariant = record.itemTypeSchemaVariant as - | 'original' - | 'partial'; + 'original' | 'partial'; return dbRowToItemSubmissionWithItemTypeIdentifier({ submission_id: submissionId, @@ -1092,8 +1091,7 @@ export class ItemInvestigationService { item_data: record.itemData as JsonOf, item_submission_time: record.occurredAt, item_type_schema_variant: record.itemTypeSchemaVariant as - | 'original' - | 'partial', + 'original' | 'partial', }); for (const group of recordsByItem.values()) { diff --git a/server/services/manualReviewToolService/dbTypes.ts b/server/services/manualReviewToolService/dbTypes.ts index 2bb55bfcd..361ee4127 100644 --- a/server/services/manualReviewToolService/dbTypes.ts +++ b/server/services/manualReviewToolService/dbTypes.ts @@ -34,9 +34,7 @@ import { type RoutingRuleStatus } from './modules/JobRouting.js'; // What to do with a user's other pending reports when a trigger action is // taken on one of their jobs (issue #650). export type ClearReportsDisposition = - | 'AUTOMATIC_CLOSE' - | 'IGNORE' - | 'SAME_ACTION'; + 'AUTOMATIC_CLOSE' | 'IGNORE' | 'SAME_ACTION'; export type ClearReportsScope = 'CURRENT_QUEUE' | 'ALL_QUEUES'; @@ -114,9 +112,7 @@ export type ManualReviewToolServicePg = { decision_components: ManualReviewDecisionComponent[]; related_actions: ManualReviewDecisionRelatedAction[]; enqueue_source_info: - | ManualReviewJobEnqueueSourceInfo - | AppealEnqueueSourceInfo - | null; + ManualReviewJobEnqueueSourceInfo | AppealEnqueueSourceInfo | null; item_created_at: Date | null; decision_reason: string | null; }; @@ -208,8 +204,7 @@ export type ManualReviewToolServicePg = { item_type_id: string; created_at: Date; enqueue_source_info: - | ManualReviewJobEnqueueSourceInfo - | AppealEnqueueSourceInfo; + ManualReviewJobEnqueueSourceInfo | AppealEnqueueSourceInfo; policy_ids: string[]; }; 'manual_review_tool.flattened_job_creations': { diff --git a/server/services/manualReviewToolService/manualReviewToolService.test.ts b/server/services/manualReviewToolService/manualReviewToolService.test.ts index 73293091b..4987de8b2 100644 --- a/server/services/manualReviewToolService/manualReviewToolService.test.ts +++ b/server/services/manualReviewToolService/manualReviewToolService.test.ts @@ -1,7 +1,12 @@ -/* eslint-disable max-lines */ +import { uid } from 'uid'; import { v1 as uuidv1 } from 'uuid'; -import getBottle, { type Dependencies } from '../../iocContainer/index.js'; +import createMrtQueue from '../../test/fixtureHelpers/createMrtQueue.js'; +import createOrg from '../../test/fixtureHelpers/createOrg.js'; +import createUser from '../../test/fixtureHelpers/createUser.js'; +import makeDummyMrtJobPayload from '../../test/fixtureHelpers/makeDummyMrtJobPayload.js'; +import { makeTransactionalTestWithFixture } from '../../test/harness/transactionalTest.js'; +import { type MockedServer } from '../../test/setupMockedServer.js'; import { instantiateOpaqueType } from '../../utils/typescript-types.js'; import { makeSubmissionId, @@ -16,33 +21,7 @@ import { import { AUTOMATED_DECISION_REVIEWER_ID } from './modules/JobDecisioning.js'; import { jobIdToGuid } from './modules/QueueOperations.js'; -function makeDummyJob() { - return { - createdAt: new Date(), - policyIds: [] as string[], - payload: { - kind: 'DEFAULT', - reportHistory: [] as ReportHistory, - item: instantiateOpaqueType({ - submissionId: makeSubmissionId(), - submissionTime: new Date(), - // eslint-disable-next-line @typescript-eslint/consistent-type-assertions - data: {} as NormalizedItemData, - itemTypeIdentifier: { - id: uuidv1(), - version: new Date().toISOString(), - schemaVariant: 'original', - }, - creator: { - id: uuidv1(), - typeId: uuidv1(), - }, - itemId: uuidv1(), - }), - enqueueSourceInfo: { kind: 'REPORT' }, - }, - } as const; -} +type TestDeps = MockedServer['deps']; function makeDummyNcmecJob() { return { @@ -73,145 +52,128 @@ function makeDummyNcmecJob() { }; } -describe('Manual Review Tool Service', () => { - let mrtService: ManualReviewToolService; - let container: Dependencies; - - beforeAll(async () => { - // The mutation should be ok here since this is initial setup in a - // beforeAll; it doesn't involve reset state for each test in the suite - - ({ container } = await getBottle()); - mrtService = container.ManualReviewToolService; - }); - - afterAll(async () => { - await container.closeSharedResourcesForShutdown(); - }); +async function configureDecisionReasonRequirements( + mrtService: ManualReviewToolService, + orgId: string, + opts: { + onAction?: boolean; + onIgnore?: boolean; + }, +) { + if (opts.onAction !== undefined) { + await mrtService.updateRequiresDecisionReason(orgId, opts.onAction); + } + if (opts.onIgnore !== undefined) { + await mrtService.updateRequiresDecisionReasonOnIgnore(orgId, opts.onIgnore); + } +} - // Test that we can start the stalled jobs checker for manual job processing - test('should be able to start stalled jobs checker', async () => { - const worker = await mrtService['queueOps']['getBullWorker']({ - orgId: 'dummyOrg', - queueId: 'dummyQueue', - }); - // The startStalledCheckTimer method should be available and not throw - expect(worker).toBeDefined(); - }); +async function setRequiresPolicyForDecisions( + mrtService: ManualReviewToolService, + db: TestDeps['KyselyPg'], + orgId: string, + value: boolean, +) { + await mrtService.upsertDefaultSettings({ orgId }); + await db + .updateTable('manual_review_tool.manual_review_tool_settings') + .set({ requires_policy_for_decisions: value }) + .where('org_id', '=', orgId) + .execute(); +} - // TODO: rework when we rework the MRT error handling - test.skip('MRT throws for submitting a job that has already been moved to completed', async () => { - const orgId = 'e7c89ce7729', - queueId = '1', - reviewerId = uuidv1(), - reviewerEmail = 'test@test.com', - itemId = uuidv1(), - itemTypeId = uuidv1(); - - await mrtService['queueOps']['addJob']({ - queueId, - enqueueSourceInfo: { kind: 'REPORT' }, - jobPayload: { - createdAt: new Date(), - payload: { - kind: 'DEFAULT', - reportHistory: [], - item: instantiateOpaqueType({ - submissionId: makeSubmissionId(), - // eslint-disable-next-line @typescript-eslint/consistent-type-assertions - data: {} as NormalizedItemData, - itemTypeIdentifier: { - id: itemTypeId, - version: new Date().toISOString(), - schemaVariant: 'original', - }, - creator: { - id: uuidv1(), - typeId: uuidv1(), - }, - itemId, - }), - reportedForReason: undefined, - reportedForReasons: [], - enqueueSourceInfo: { kind: 'REPORT' }, - }, - policyIds: [], +describe('Manual Review Tool Service', () => { + // Just the service — for cases that don't need any org-scoped fixtures. + const testWithService = makeTransactionalTestWithFixture( + async ({ deps }) => ({ + mrtService: deps.ManualReviewToolService, + }), + ); + + // A fresh org with a queue and a CUSTOM_ACTION, so decision tests can enqueue + // a job and submit a real (validatable) action without relying on seed data. + const testWithQueue = makeTransactionalTestWithFixture(async ({ deps }) => { + const mrtService = deps.ManualReviewToolService; + const { org } = await createOrg( + { + KyselyPg: deps.KyselyPg, + ModerationConfigService: deps.ModerationConfigService, + ApiKeyService: deps.ApiKeyService, }, - orgId, - }); - - const dequeuedJob = await mrtService.dequeueNextJob({ - orgId, - queueId, - userId: reviewerId, + uid(), + ); + const { user } = await createUser(deps.KyselyPg, org.id); + const { queue } = await createMrtQueue({ + orgId: org.id, + mrtService, + userId: user.id, }); - - if (!dequeuedJob) { - throw new Error('should have dequeued successfully.'); - } - - await mrtService.submitDecision({ - queueId, - reportHistory: [], - jobId: dequeuedJob.job.id, - lockToken: dequeuedJob.lockToken, - decisionComponents: [ - { - type: 'CUSTOM_ACTION', - actions: [{ id: '8481310e8c4' }], - policies: [], - itemIds: [itemId], - itemTypeId, - }, - ], - relatedActions: [], - reviewerId, - reviewerEmail, - orgId, + const action = await deps.ModerationConfigService.createAction(org.id, { + name: `mrt-test-action-${uid()}`, + description: null, + type: 'CUSTOM_ACTION', + callbackUrl: 'https://example.com', + callbackUrlHeaders: null, + callbackUrlBody: null, }); - const duplicativeDecision = async () => { - return mrtService.submitDecision({ - queueId, - reportHistory: [], - jobId: dequeuedJob.job.id, - lockToken: dequeuedJob.lockToken, - decisionComponents: [ - { - type: 'CUSTOM_ACTION', - actions: [{ id: '8481310e8c4' }], - policies: [], - itemIds: [itemId], - itemTypeId, - }, - ], - relatedActions: [], - reviewerId, - reviewerEmail, - orgId, - }); - }; - - await expect(duplicativeDecision()).rejects.toThrow( - `No job with ID ${dequeuedJob.job.id} in queue with ID ${queueId}`, - ); + return { mrtService, org, user, queue, actionId: action.id }; }); - describe('duplicate decision handling', () => { - it('should reject duplicate decisions with the same lock token', async () => { - const orgId = 'e7c89ce7729', + // Test that we can start the stalled jobs checker for manual job processing + testWithService( + 'should be able to start stalled jobs checker', + async ({ mrtService }) => { + const worker = await mrtService['queueOps']['getBullWorker']({ + orgId: 'dummyOrg', + queueId: 'dummyQueue', + }); + // The startStalledCheckTimer method should be available and not throw + expect(worker).toBeDefined(); + }, + ); + + // TODO: rework when we rework the MRT error handling + testWithService.skip( + 'MRT throws for submitting a job that has already been moved to completed', + async ({ mrtService }) => { + const orgId = uid(), queueId = '1', reviewerId = uuidv1(), reviewerEmail = 'test@test.com', - jobPayload = makeDummyJob(); - const itemId = jobPayload.payload.item.itemId, - itemTypeId = jobPayload.payload.item.itemTypeIdentifier.id; + itemId = uuidv1(), + itemTypeId = uuidv1(); await mrtService['queueOps']['addJob']({ - jobPayload, - orgId, queueId, enqueueSourceInfo: { kind: 'REPORT' }, + jobPayload: { + createdAt: new Date(), + payload: { + kind: 'DEFAULT', + reportHistory: [], + item: instantiateOpaqueType({ + submissionId: makeSubmissionId(), + // eslint-disable-next-line @typescript-eslint/consistent-type-assertions + data: {} as NormalizedItemData, + itemTypeIdentifier: { + id: itemTypeId, + version: new Date().toISOString(), + schemaVariant: 'original', + }, + creator: { + id: uuidv1(), + typeId: uuidv1(), + }, + itemId, + }), + reportedForReason: undefined, + reportedForReasons: [], + enqueueSourceInfo: { kind: 'REPORT' }, + }, + policyIds: [], + }, + orgId, }); const dequeuedJob = await mrtService.dequeueNextJob({ @@ -221,7 +183,7 @@ describe('Manual Review Tool Service', () => { }); if (!dequeuedJob) { - throw new Error("should've returned a job"); + throw new Error('should have dequeued successfully.'); } await mrtService.submitDecision({ @@ -245,7 +207,7 @@ describe('Manual Review Tool Service', () => { }); const duplicativeDecision = async () => { - await mrtService.submitDecision({ + return mrtService.submitDecision({ queueId, reportHistory: [], jobId: dequeuedJob.job.id, @@ -266,8 +228,86 @@ describe('Manual Review Tool Service', () => { }); }; - await expect(duplicativeDecision()).rejects.toThrow(); - }); + await expect(duplicativeDecision()).rejects.toThrow( + `No job with ID ${dequeuedJob.job.id} in queue with ID ${queueId}`, + ); + }, + ); + + describe('duplicate decision handling', () => { + testWithQueue( + 'should reject duplicate decisions with the same lock token', + async ({ mrtService, org, queue, actionId }) => { + const orgId = org.id, + queueId = queue.id, + reviewerId = uuidv1(), + reviewerEmail = 'test@test.com', + jobPayload = makeDummyMrtJobPayload(); + const itemId = jobPayload.payload.item.itemId, + itemTypeId = jobPayload.payload.item.itemTypeIdentifier.id; + + await mrtService['queueOps']['addJob']({ + jobPayload, + orgId, + queueId, + enqueueSourceInfo: { kind: 'REPORT' }, + }); + + const dequeuedJob = await mrtService.dequeueNextJob({ + orgId, + queueId, + userId: reviewerId, + }); + + if (!dequeuedJob) { + throw new Error("should've returned a job"); + } + + await mrtService.submitDecision({ + queueId, + reportHistory: [], + jobId: dequeuedJob.job.id, + lockToken: dequeuedJob.lockToken, + decisionComponents: [ + { + type: 'CUSTOM_ACTION', + actions: [{ id: actionId }], + policies: [], + itemIds: [itemId], + itemTypeId, + }, + ], + relatedActions: [], + reviewerId, + reviewerEmail, + orgId, + }); + + const duplicativeDecision = async () => { + await mrtService.submitDecision({ + queueId, + reportHistory: [], + jobId: dequeuedJob.job.id, + lockToken: dequeuedJob.lockToken, + decisionComponents: [ + { + type: 'CUSTOM_ACTION', + actions: [{ id: actionId }], + policies: [], + itemIds: [itemId], + itemTypeId, + }, + ], + relatedActions: [], + reviewerId, + reviewerEmail, + orgId, + }); + }; + + await expect(duplicativeDecision()).rejects.toThrow(); + }, + ); it.skip('should reject duplicate decisions on jobs dequeued again after the lock expires', async () => {}); }); @@ -278,51 +318,54 @@ describe('Manual Review Tool Service', () => { // stuck in a retry loop. The decision must record the empty-string // reviewer id (rendered as "Automatic" client-side) and not throw. describe('automatic close decisions', () => { - it('records an AUTOMATIC_CLOSE decision with no human reviewer', async () => { - const orgId = 'e7c89ce7729'; - const queueId = '1'; - const jobPayload = makeDummyJob(); - - await mrtService['queueOps']['addJob']({ - jobPayload, - orgId, - queueId, - enqueueSourceInfo: { kind: 'REPORT' }, - }); + testWithQueue( + 'records an AUTOMATIC_CLOSE decision with no human reviewer', + async ({ mrtService, org, queue }) => { + const orgId = org.id, + queueId = queue.id, + jobPayload = makeDummyMrtJobPayload(); + + await mrtService['queueOps']['addJob']({ + jobPayload, + orgId, + queueId, + enqueueSourceInfo: { kind: 'REPORT' }, + }); - const dequeuedJob = await mrtService.dequeueNextJob({ - orgId, - queueId, - userId: uuidv1(), - }); + const dequeuedJob = await mrtService.dequeueNextJob({ + orgId, + queueId, + userId: uuidv1(), + }); - if (!dequeuedJob) { - throw new Error("should've returned a job"); - } + if (!dequeuedJob) { + throw new Error("should've returned a job"); + } - // Used to throw 23502 (null reviewer_id) before the sentinel fix. - await mrtService.submitDecision({ - queueId, - reportHistory: [], - jobId: dequeuedJob.job.id, - lockToken: dequeuedJob.lockToken, - relatedActions: [], - orgId, - automaticCloseDecision: { - type: 'AUTOMATIC_CLOSE', - reason: 'ITEM_DELETED_BEFORE_REVIEW', - }, - }); + // Used to throw 23502 (null reviewer_id) before the sentinel fix. + await mrtService.submitDecision({ + queueId, + reportHistory: [], + jobId: dequeuedJob.job.id, + lockToken: dequeuedJob.lockToken, + relatedActions: [], + orgId, + automaticCloseDecision: { + type: 'AUTOMATIC_CLOSE', + reason: 'ITEM_DELETED_BEFORE_REVIEW', + }, + }); - const row = await mrtService['pgQuery'] - .selectFrom('manual_review_tool.manual_review_decisions') - .where('id', '=', jobIdToGuid(dequeuedJob.job.id)) - .where('org_id', '=', orgId) - .select(['reviewer_id']) - .executeTakeFirst(); + const row = await mrtService['pgQuery'] + .selectFrom('manual_review_tool.manual_review_decisions') + .where('id', '=', jobIdToGuid(dequeuedJob.job.id)) + .where('org_id', '=', orgId) + .select(['reviewer_id']) + .executeTakeFirst(); - expect(row?.reviewer_id).toBe(AUTOMATED_DECISION_REVIEWER_ID); - }); + expect(row?.reviewer_id).toBe(AUTOMATED_DECISION_REVIEWER_ID); + }, + ); }); // Issue #616: when an org sets `mrt_requires_decision_reason_on_action`, @@ -335,91 +378,100 @@ describe('Manual Review Tool Service', () => { // (`..._on_ignore`) — so the cases below also cover that an IGNORE decision is // gated by the ignore flag, not the action flag. describe('requires_decision_reason enforcement', () => { - const orgId = 'e7c89ce7729'; - const queueId = '1'; - // Pulled from the staging seed data — any CUSTOM_ACTION row on this org - // will do; the action-id validation runs before our reason check. - const seededActionId = '1873b2f15cc'; - - const setRequiresDecisionReason = async (value: boolean) => { - await mrtService.upsertDefaultSettings({ orgId }); - await mrtService['pgQuery'] - .updateTable('manual_review_tool.manual_review_tool_settings') - .set({ mrt_requires_decision_reason_on_action: value }) - .where('org_id', '=', orgId) - .execute(); - }; - - const setRequiresDecisionReasonOnIgnore = async (value: boolean) => { - await mrtService.upsertDefaultSettings({ orgId }); - await mrtService['pgQuery'] - .updateTable('manual_review_tool.manual_review_tool_settings') - .set({ mrt_requires_decision_reason_on_ignore: value }) - .where('org_id', '=', orgId) - .execute(); - }; - - beforeAll(async () => { - // The queue row must exist before addJob, but no other test in this - // file owns its lifecycle, so we seed it here idempotently. - await mrtService['pgQuery'] - .insertInto('manual_review_tool.manual_review_queues') - .values({ - id: queueId, - name: 'integ-test-queue', - description: null, - org_id: orgId, - is_default_queue: false, - is_appeals_queue: false, - auto_close_jobs: false, - }) - .onConflict((oc) => oc.doNothing()) - .execute(); - }); + testWithQueue( + 'rejects a decision with no reason when the flag is on', + async ({ mrtService, org, queue, actionId }) => { + await configureDecisionReasonRequirements(mrtService, org.id, { + onAction: true, + }); - afterEach(async () => { - // Reset so the flags don't leak into other tests in this file or - // subsequent runs that reuse the seeded org. - await setRequiresDecisionReason(false); - await setRequiresDecisionReasonOnIgnore(false); - }); + const reviewerId = uuidv1(); + const reviewerEmail = 'test@test.com'; + const jobPayload = makeDummyMrtJobPayload(); + const itemId = jobPayload.payload.item.itemId; + const itemTypeId = jobPayload.payload.item.itemTypeIdentifier.id; - it('rejects a decision with no reason when the flag is on', async () => { - await setRequiresDecisionReason(true); + await mrtService['queueOps']['addJob']({ + jobPayload, + orgId: org.id, + queueId: queue.id, + enqueueSourceInfo: { kind: 'REPORT' }, + }); - const reviewerId = uuidv1(); - const reviewerEmail = 'test@test.com'; - const jobPayload = makeDummyJob(); - const itemId = jobPayload.payload.item.itemId; - const itemTypeId = jobPayload.payload.item.itemTypeIdentifier.id; + const dequeuedJob = await mrtService.dequeueNextJob({ + orgId: org.id, + queueId: queue.id, + userId: reviewerId, + }); - await mrtService['queueOps']['addJob']({ - jobPayload, - orgId, - queueId, - enqueueSourceInfo: { kind: 'REPORT' }, - }); + if (!dequeuedJob) { + throw new Error("should've returned a job"); + } + + await expect( + mrtService.submitDecision({ + queueId: queue.id, + reportHistory: [], + jobId: dequeuedJob.job.id, + lockToken: dequeuedJob.lockToken, + decisionComponents: [ + { + type: 'CUSTOM_ACTION', + actions: [{ id: actionId }], + policies: [{ id: uuidv1() }], + itemIds: [itemId], + itemTypeId, + }, + ], + relatedActions: [], + reviewerId, + reviewerEmail, + orgId: org.id, + // decisionReason intentionally omitted + }), + ).rejects.toThrow(/requires every decision to include a reason/i); + }, + ); - const dequeuedJob = await mrtService.dequeueNextJob({ - orgId, - queueId, - userId: reviewerId, - }); + testWithQueue( + 'allows a decision with a reason when the flag is on', + async ({ mrtService, org, queue, actionId }) => { + await configureDecisionReasonRequirements(mrtService, org.id, { + onAction: true, + }); - if (!dequeuedJob) { - throw new Error("should've returned a job"); - } + const reviewerId = uuidv1(); + const reviewerEmail = 'test@test.com'; + const jobPayload = makeDummyMrtJobPayload(); + const itemId = jobPayload.payload.item.itemId; + const itemTypeId = jobPayload.payload.item.itemTypeIdentifier.id; - await expect( - mrtService.submitDecision({ - queueId, + await mrtService['queueOps']['addJob']({ + jobPayload, + orgId: org.id, + queueId: queue.id, + enqueueSourceInfo: { kind: 'REPORT' }, + }); + + const dequeuedJob = await mrtService.dequeueNextJob({ + orgId: org.id, + queueId: queue.id, + userId: reviewerId, + }); + + if (!dequeuedJob) { + throw new Error("should've returned a job"); + } + + await mrtService.submitDecision({ + queueId: queue.id, reportHistory: [], jobId: dequeuedJob.job.id, lockToken: dequeuedJob.lockToken, decisionComponents: [ { type: 'CUSTOM_ACTION', - actions: [{ id: seededActionId }], + actions: [{ id: actionId }], policies: [{ id: uuidv1() }], itemIds: [itemId], itemTypeId, @@ -428,140 +480,148 @@ describe('Manual Review Tool Service', () => { relatedActions: [], reviewerId, reviewerEmail, - orgId, - // decisionReason intentionally omitted - }), - ).rejects.toThrow(/requires every decision to include a reason/i); - }); + orgId: org.id, + decisionReason: 'Repeat offender', + }); + }, + ); - it('allows a decision with a reason when the flag is on', async () => { - await setRequiresDecisionReason(true); + testWithQueue( + 'allows a decision with no reason when the flag is off', + async ({ mrtService, org, queue, actionId }) => { + // Control case: default-off behavior must remain unchanged so orgs that + // never opt in see no difference from this PR. + await configureDecisionReasonRequirements(mrtService, org.id, { + onAction: false, + }); - const reviewerId = uuidv1(); - const reviewerEmail = 'test@test.com'; - const jobPayload = makeDummyJob(); - const itemId = jobPayload.payload.item.itemId; - const itemTypeId = jobPayload.payload.item.itemTypeIdentifier.id; + const reviewerId = uuidv1(); + const reviewerEmail = 'test@test.com'; + const jobPayload = makeDummyMrtJobPayload(); + const itemId = jobPayload.payload.item.itemId; + const itemTypeId = jobPayload.payload.item.itemTypeIdentifier.id; - await mrtService['queueOps']['addJob']({ - jobPayload, - orgId, - queueId, - enqueueSourceInfo: { kind: 'REPORT' }, - }); + await mrtService['queueOps']['addJob']({ + jobPayload, + orgId: org.id, + queueId: queue.id, + enqueueSourceInfo: { kind: 'REPORT' }, + }); - const dequeuedJob = await mrtService.dequeueNextJob({ - orgId, - queueId, - userId: reviewerId, - }); + const dequeuedJob = await mrtService.dequeueNextJob({ + orgId: org.id, + queueId: queue.id, + userId: reviewerId, + }); - if (!dequeuedJob) { - throw new Error("should've returned a job"); - } + if (!dequeuedJob) { + throw new Error("should've returned a job"); + } - await mrtService.submitDecision({ - queueId, - reportHistory: [], - jobId: dequeuedJob.job.id, - lockToken: dequeuedJob.lockToken, - decisionComponents: [ - { - type: 'CUSTOM_ACTION', - actions: [{ id: seededActionId }], - policies: [{ id: uuidv1() }], - itemIds: [itemId], - itemTypeId, - }, - ], - relatedActions: [], - reviewerId, - reviewerEmail, - orgId, - decisionReason: 'Repeat offender', - }); - }); + await mrtService.submitDecision({ + queueId: queue.id, + reportHistory: [], + jobId: dequeuedJob.job.id, + lockToken: dequeuedJob.lockToken, + decisionComponents: [ + { + type: 'CUSTOM_ACTION', + actions: [{ id: actionId }], + policies: [{ id: uuidv1() }], + itemIds: [itemId], + itemTypeId, + }, + ], + relatedActions: [], + reviewerId, + reviewerEmail, + orgId: org.id, + }); + }, + ); - it('allows a decision with no reason when the flag is off', async () => { - // Control case: default-off behavior must remain unchanged so orgs that - // never opt in see no difference from this PR. - await setRequiresDecisionReason(false); + // Issue #757: an IGNORE decision is gated by the ignore flag, not the + // action flag. With only the ignore flag on, an IGNORE with no reason is + // rejected. + testWithQueue( + 'rejects an IGNORE decision with no reason when only the ignore flag is on', + async ({ mrtService, org, queue }) => { + await configureDecisionReasonRequirements(mrtService, org.id, { + onAction: false, + onIgnore: true, + }); - const reviewerId = uuidv1(); - const reviewerEmail = 'test@test.com'; - const jobPayload = makeDummyJob(); - const itemId = jobPayload.payload.item.itemId; - const itemTypeId = jobPayload.payload.item.itemTypeIdentifier.id; + const reviewerId = uuidv1(); + const reviewerEmail = 'test@test.com'; + const jobPayload = makeDummyMrtJobPayload(); - await mrtService['queueOps']['addJob']({ - jobPayload, - orgId, - queueId, - enqueueSourceInfo: { kind: 'REPORT' }, - }); + await mrtService['queueOps']['addJob']({ + jobPayload, + orgId: org.id, + queueId: queue.id, + enqueueSourceInfo: { kind: 'REPORT' }, + }); - const dequeuedJob = await mrtService.dequeueNextJob({ - orgId, - queueId, - userId: reviewerId, - }); - - if (!dequeuedJob) { - throw new Error("should've returned a job"); - } + const dequeuedJob = await mrtService.dequeueNextJob({ + orgId: org.id, + queueId: queue.id, + userId: reviewerId, + }); - await mrtService.submitDecision({ - queueId, - reportHistory: [], - jobId: dequeuedJob.job.id, - lockToken: dequeuedJob.lockToken, - decisionComponents: [ - { - type: 'CUSTOM_ACTION', - actions: [{ id: seededActionId }], - policies: [{ id: uuidv1() }], - itemIds: [itemId], - itemTypeId, - }, - ], - relatedActions: [], - reviewerId, - reviewerEmail, - orgId, - }); - }); + if (!dequeuedJob) { + throw new Error("should've returned a job"); + } + + await expect( + mrtService.submitDecision({ + queueId: queue.id, + reportHistory: [], + jobId: dequeuedJob.job.id, + lockToken: dequeuedJob.lockToken, + decisionComponents: [{ type: 'IGNORE' }], + relatedActions: [], + reviewerId, + reviewerEmail, + orgId: org.id, + // decisionReason intentionally omitted + }), + ).rejects.toThrow(/requires every decision to include a reason/i); + }, + ); - // Issue #757: an IGNORE decision is gated by the ignore flag, not the - // action flag. With only the ignore flag on, an IGNORE with no reason is - // rejected. - it('rejects an IGNORE decision with no reason when only the ignore flag is on', async () => { - await setRequiresDecisionReason(false); - await setRequiresDecisionReasonOnIgnore(true); + // Issue #757: with only the action flag on, ignoring a job must NOT require + // a reason — this is the bug from the issue. + testWithQueue( + 'allows an IGNORE decision with no reason when only the action flag is on', + async ({ mrtService, org, queue }) => { + await configureDecisionReasonRequirements(mrtService, org.id, { + onAction: true, + onIgnore: false, + }); - const reviewerId = uuidv1(); - const reviewerEmail = 'test@test.com'; - const jobPayload = makeDummyJob(); + const reviewerId = uuidv1(); + const reviewerEmail = 'test@test.com'; + const jobPayload = makeDummyMrtJobPayload(); - await mrtService['queueOps']['addJob']({ - jobPayload, - orgId, - queueId, - enqueueSourceInfo: { kind: 'REPORT' }, - }); + await mrtService['queueOps']['addJob']({ + jobPayload, + orgId: org.id, + queueId: queue.id, + enqueueSourceInfo: { kind: 'REPORT' }, + }); - const dequeuedJob = await mrtService.dequeueNextJob({ - orgId, - queueId, - userId: reviewerId, - }); + const dequeuedJob = await mrtService.dequeueNextJob({ + orgId: org.id, + queueId: queue.id, + userId: reviewerId, + }); - if (!dequeuedJob) { - throw new Error("should've returned a job"); - } + if (!dequeuedJob) { + throw new Error("should've returned a job"); + } - await expect( - mrtService.submitDecision({ - queueId, + await mrtService.submitDecision({ + queueId: queue.id, reportHistory: [], jobId: dequeuedJob.job.id, lockToken: dequeuedJob.lockToken, @@ -569,224 +629,223 @@ describe('Manual Review Tool Service', () => { relatedActions: [], reviewerId, reviewerEmail, - orgId, + orgId: org.id, // decisionReason intentionally omitted - }), - ).rejects.toThrow(/requires every decision to include a reason/i); - }); - - // Issue #757: with only the action flag on, ignoring a job must NOT require - // a reason — this is the bug from the issue. - it('allows an IGNORE decision with no reason when only the action flag is on', async () => { - await setRequiresDecisionReason(true); - await setRequiresDecisionReasonOnIgnore(false); - - const reviewerId = uuidv1(); - const reviewerEmail = 'test@test.com'; - const jobPayload = makeDummyJob(); - - await mrtService['queueOps']['addJob']({ - jobPayload, - orgId, - queueId, - enqueueSourceInfo: { kind: 'REPORT' }, - }); - - const dequeuedJob = await mrtService.dequeueNextJob({ - orgId, - queueId, - userId: reviewerId, - }); - - if (!dequeuedJob) { - throw new Error("should've returned a job"); - } - - await mrtService.submitDecision({ - queueId, - reportHistory: [], - jobId: dequeuedJob.job.id, - lockToken: dequeuedJob.lockToken, - decisionComponents: [{ type: 'IGNORE' }], - relatedActions: [], - reviewerId, - reviewerEmail, - orgId, - // decisionReason intentionally omitted - }); - }); + }); + }, + ); // Issue #757: with only the ignore flag on, acting on a violating job must // NOT require a reason. - it('allows a CUSTOM_ACTION decision with no reason when only the ignore flag is on', async () => { - await setRequiresDecisionReason(false); - await setRequiresDecisionReasonOnIgnore(true); + testWithQueue( + 'allows a CUSTOM_ACTION decision with no reason when only the ignore flag is on', + async ({ mrtService, org, queue, actionId }) => { + await configureDecisionReasonRequirements(mrtService, org.id, { + onAction: false, + onIgnore: true, + }); - const reviewerId = uuidv1(); - const reviewerEmail = 'test@test.com'; - const jobPayload = makeDummyJob(); - const itemId = jobPayload.payload.item.itemId; - const itemTypeId = jobPayload.payload.item.itemTypeIdentifier.id; + const reviewerId = uuidv1(); + const reviewerEmail = 'test@test.com'; + const jobPayload = makeDummyMrtJobPayload(); + const itemId = jobPayload.payload.item.itemId; + const itemTypeId = jobPayload.payload.item.itemTypeIdentifier.id; - await mrtService['queueOps']['addJob']({ - jobPayload, - orgId, - queueId, - enqueueSourceInfo: { kind: 'REPORT' }, - }); + await mrtService['queueOps']['addJob']({ + jobPayload, + orgId: org.id, + queueId: queue.id, + enqueueSourceInfo: { kind: 'REPORT' }, + }); - const dequeuedJob = await mrtService.dequeueNextJob({ - orgId, - queueId, - userId: reviewerId, - }); + const dequeuedJob = await mrtService.dequeueNextJob({ + orgId: org.id, + queueId: queue.id, + userId: reviewerId, + }); - if (!dequeuedJob) { - throw new Error("should've returned a job"); - } + if (!dequeuedJob) { + throw new Error("should've returned a job"); + } - await mrtService.submitDecision({ - queueId, - reportHistory: [], - jobId: dequeuedJob.job.id, - lockToken: dequeuedJob.lockToken, - decisionComponents: [ - { - type: 'CUSTOM_ACTION', - actions: [{ id: seededActionId }], - policies: [{ id: uuidv1() }], - itemIds: [itemId], - itemTypeId, - }, - ], - relatedActions: [], - reviewerId, - reviewerEmail, - orgId, - // decisionReason intentionally omitted - }); - }); + await mrtService.submitDecision({ + queueId: queue.id, + reportHistory: [], + jobId: dequeuedJob.job.id, + lockToken: dequeuedJob.lockToken, + decisionComponents: [ + { + type: 'CUSTOM_ACTION', + actions: [{ id: actionId }], + policies: [{ id: uuidv1() }], + itemIds: [itemId], + itemTypeId, + }, + ], + relatedActions: [], + reviewerId, + reviewerEmail, + orgId: org.id, + // decisionReason intentionally omitted + }); + }, + ); // Issue #736: NCMEC review uses Submit NCMEC Report or Ignore, neither of // which carries a written decision reason. The require-reason flag is for // moderation decisions on standard MRT jobs and should not block the // NCMEC path. - it('allows an IGNORE decision on an NCMEC job with no reason when the flag is on', async () => { - await setRequiresDecisionReason(true); - await setRequiresDecisionReasonOnIgnore(true); + testWithQueue( + 'allows an IGNORE decision on an NCMEC job with no reason when the flag is on', + async ({ mrtService, org, queue }) => { + await configureDecisionReasonRequirements(mrtService, org.id, { + onAction: true, + onIgnore: true, + }); - const reviewerId = uuidv1(); - const reviewerEmail = 'test@test.com'; - const jobPayload = makeDummyNcmecJob(); + const reviewerId = uuidv1(); + const reviewerEmail = 'test@test.com'; + const jobPayload = makeDummyNcmecJob(); - await mrtService['queueOps']['addJob']({ - jobPayload, - orgId, - queueId, - enqueueSourceInfo: { kind: 'REPORT' }, - }); + await mrtService['queueOps']['addJob']({ + jobPayload, + orgId: org.id, + queueId: queue.id, + enqueueSourceInfo: { kind: 'REPORT' }, + }); - const dequeuedJob = await mrtService.dequeueNextJob({ - orgId, - queueId, - userId: reviewerId, - }); + const dequeuedJob = await mrtService.dequeueNextJob({ + orgId: org.id, + queueId: queue.id, + userId: reviewerId, + }); - if (!dequeuedJob) { - throw new Error("should've returned a job"); - } + if (!dequeuedJob) { + throw new Error("should've returned a job"); + } - await mrtService.submitDecision({ - queueId, - reportHistory: [], - jobId: dequeuedJob.job.id, - lockToken: dequeuedJob.lockToken, - decisionComponents: [{ type: 'IGNORE' }], - relatedActions: [], - reviewerId, - reviewerEmail, - orgId, - // decisionReason intentionally omitted - }); - }); + await mrtService.submitDecision({ + queueId: queue.id, + reportHistory: [], + jobId: dequeuedJob.job.id, + lockToken: dequeuedJob.lockToken, + decisionComponents: [{ type: 'IGNORE' }], + relatedActions: [], + reviewerId, + reviewerEmail, + orgId: org.id, + // decisionReason intentionally omitted + }); + }, + ); }); // Issue #389: when an org sets `requires_policy_for_decisions`, submitDecision // must reject CUSTOM_ACTION decisions with no policies. The UI already blocks // this; the server-side check closes the API-bypass gap. describe('requires_policy_for_decisions enforcement', () => { - const orgId = 'e7c89ce7729'; - const queueId = '1'; - // Pulled from the staging seed data — any CUSTOM_ACTION row on this org - // will do; the action-id validation runs before our flag check. - const seededActionId = '1873b2f15cc'; - - const setRequiresPolicyForDecisions = async (value: boolean) => { - await mrtService.upsertDefaultSettings({ orgId }); - await mrtService['pgQuery'] - .updateTable('manual_review_tool.manual_review_tool_settings') - .set({ requires_policy_for_decisions: value }) - .where('org_id', '=', orgId) - .execute(); - }; - - beforeAll(async () => { - await mrtService['pgQuery'] - .insertInto('manual_review_tool.manual_review_queues') - .values({ - id: queueId, - name: 'integ-test-queue', - description: null, - org_id: orgId, - is_default_queue: false, - is_appeals_queue: false, - auto_close_jobs: false, - }) - .onConflict((oc) => oc.doNothing()) - .execute(); - }); - - afterEach(async () => { - await setRequiresPolicyForDecisions(false); - }); + testWithQueue( + 'rejects a CUSTOM_ACTION decision with no policies when the flag is on', + async ({ mrtService, deps, org, queue, actionId }) => { + await setRequiresPolicyForDecisions( + mrtService, + deps.KyselyPg, + org.id, + true, + ); + + const reviewerId = uuidv1(); + const reviewerEmail = 'test@test.com'; + const jobPayload = makeDummyMrtJobPayload(); + const itemId = jobPayload.payload.item.itemId; + const itemTypeId = jobPayload.payload.item.itemTypeIdentifier.id; + + await mrtService['queueOps']['addJob']({ + jobPayload, + orgId: org.id, + queueId: queue.id, + enqueueSourceInfo: { kind: 'REPORT' }, + }); - it('rejects a CUSTOM_ACTION decision with no policies when the flag is on', async () => { - await setRequiresPolicyForDecisions(true); + const dequeuedJob = await mrtService.dequeueNextJob({ + orgId: org.id, + queueId: queue.id, + userId: reviewerId, + }); - const reviewerId = uuidv1(); - const reviewerEmail = 'test@test.com'; - const jobPayload = makeDummyJob(); - const itemId = jobPayload.payload.item.itemId; - const itemTypeId = jobPayload.payload.item.itemTypeIdentifier.id; + if (!dequeuedJob) { + throw new Error("should've returned a job"); + } + + await expect( + mrtService.submitDecision({ + queueId: queue.id, + reportHistory: [], + jobId: dequeuedJob.job.id, + lockToken: dequeuedJob.lockToken, + decisionComponents: [ + { + type: 'CUSTOM_ACTION', + actions: [{ id: actionId }], + policies: [], + itemIds: [itemId], + itemTypeId, + }, + ], + relatedActions: [], + reviewerId, + reviewerEmail, + orgId: org.id, + }), + ).rejects.toThrow( + /requires every decision to include at least one policy/i, + ); + }, + ); - await mrtService['queueOps']['addJob']({ - jobPayload, - orgId, - queueId, - enqueueSourceInfo: { kind: 'REPORT' }, - }); + testWithQueue( + 'allows a CUSTOM_ACTION decision with policies when the flag is on', + async ({ mrtService, deps, org, queue, actionId }) => { + await setRequiresPolicyForDecisions( + mrtService, + deps.KyselyPg, + org.id, + true, + ); + + const reviewerId = uuidv1(); + const reviewerEmail = 'test@test.com'; + const jobPayload = makeDummyMrtJobPayload(); + const itemId = jobPayload.payload.item.itemId; + const itemTypeId = jobPayload.payload.item.itemTypeIdentifier.id; + + await mrtService['queueOps']['addJob']({ + jobPayload, + orgId: org.id, + queueId: queue.id, + enqueueSourceInfo: { kind: 'REPORT' }, + }); - const dequeuedJob = await mrtService.dequeueNextJob({ - orgId, - queueId, - userId: reviewerId, - }); + const dequeuedJob = await mrtService.dequeueNextJob({ + orgId: org.id, + queueId: queue.id, + userId: reviewerId, + }); - if (!dequeuedJob) { - throw new Error("should've returned a job"); - } + if (!dequeuedJob) { + throw new Error("should've returned a job"); + } - await expect( - mrtService.submitDecision({ - queueId, + await mrtService.submitDecision({ + queueId: queue.id, reportHistory: [], jobId: dequeuedJob.job.id, lockToken: dequeuedJob.lockToken, decisionComponents: [ { type: 'CUSTOM_ACTION', - actions: [{ id: seededActionId }], - policies: [], + actions: [{ id: actionId }], + policies: [{ id: uuidv1() }], itemIds: [itemId], itemTypeId, }, @@ -794,145 +853,113 @@ describe('Manual Review Tool Service', () => { relatedActions: [], reviewerId, reviewerEmail, - orgId, - }), - ).rejects.toThrow( - /requires every decision to include at least one policy/i, - ); - }); - - it('allows a CUSTOM_ACTION decision with policies when the flag is on', async () => { - await setRequiresPolicyForDecisions(true); - - const reviewerId = uuidv1(); - const reviewerEmail = 'test@test.com'; - const jobPayload = makeDummyJob(); - const itemId = jobPayload.payload.item.itemId; - const itemTypeId = jobPayload.payload.item.itemTypeIdentifier.id; - - await mrtService['queueOps']['addJob']({ - jobPayload, - orgId, - queueId, - enqueueSourceInfo: { kind: 'REPORT' }, - }); - - const dequeuedJob = await mrtService.dequeueNextJob({ - orgId, - queueId, - userId: reviewerId, - }); - - if (!dequeuedJob) { - throw new Error("should've returned a job"); - } - - await mrtService.submitDecision({ - queueId, - reportHistory: [], - jobId: dequeuedJob.job.id, - lockToken: dequeuedJob.lockToken, - decisionComponents: [ - { - type: 'CUSTOM_ACTION', - actions: [{ id: seededActionId }], - policies: [{ id: uuidv1() }], - itemIds: [itemId], - itemTypeId, - }, - ], - relatedActions: [], - reviewerId, - reviewerEmail, - orgId, - }); - }); - - it('allows a CUSTOM_ACTION decision without policies when the flag is off', async () => { - await setRequiresPolicyForDecisions(false); - - const reviewerId = uuidv1(); - const reviewerEmail = 'test@test.com'; - const jobPayload = makeDummyJob(); - const itemId = jobPayload.payload.item.itemId; - const itemTypeId = jobPayload.payload.item.itemTypeIdentifier.id; + orgId: org.id, + }); + }, + ); - await mrtService['queueOps']['addJob']({ - jobPayload, - orgId, - queueId, - enqueueSourceInfo: { kind: 'REPORT' }, - }); + testWithQueue( + 'allows a CUSTOM_ACTION decision without policies when the flag is off', + async ({ mrtService, deps, org, queue, actionId }) => { + await setRequiresPolicyForDecisions( + mrtService, + deps.KyselyPg, + org.id, + false, + ); + + const reviewerId = uuidv1(); + const reviewerEmail = 'test@test.com'; + const jobPayload = makeDummyMrtJobPayload(); + const itemId = jobPayload.payload.item.itemId; + const itemTypeId = jobPayload.payload.item.itemTypeIdentifier.id; + + await mrtService['queueOps']['addJob']({ + jobPayload, + orgId: org.id, + queueId: queue.id, + enqueueSourceInfo: { kind: 'REPORT' }, + }); - const dequeuedJob = await mrtService.dequeueNextJob({ - orgId, - queueId, - userId: reviewerId, - }); + const dequeuedJob = await mrtService.dequeueNextJob({ + orgId: org.id, + queueId: queue.id, + userId: reviewerId, + }); - if (!dequeuedJob) { - throw new Error("should've returned a job"); - } + if (!dequeuedJob) { + throw new Error("should've returned a job"); + } - await mrtService.submitDecision({ - queueId, - reportHistory: [], - jobId: dequeuedJob.job.id, - lockToken: dequeuedJob.lockToken, - decisionComponents: [ - { - type: 'CUSTOM_ACTION', - actions: [{ id: seededActionId }], - policies: [], - itemIds: [itemId], - itemTypeId, - }, - ], - relatedActions: [], - reviewerId, - reviewerEmail, - orgId, - }); - }); + await mrtService.submitDecision({ + queueId: queue.id, + reportHistory: [], + jobId: dequeuedJob.job.id, + lockToken: dequeuedJob.lockToken, + decisionComponents: [ + { + type: 'CUSTOM_ACTION', + actions: [{ id: actionId }], + policies: [], + itemIds: [itemId], + itemTypeId, + }, + ], + relatedActions: [], + reviewerId, + reviewerEmail, + orgId: org.id, + }); + }, + ); }); // Issue #615: orgs created before manual_review_tool_settings existed have no // row, so a save against them used to UPDATE zero rows and silently no-op. describe('settings persistence without a pre-existing row', () => { - const orgId = `no-row-${uuidv1()}`; - - afterEach(async () => { - await mrtService['pgQuery'] - .deleteFrom('manual_review_tool.manual_review_tool_settings') - .where('org_id', '=', orgId) - .execute(); - }); - - it('persists a boolean toggle when the org has no settings row', async () => { - expect(await mrtService.getHideSkipButtonForNonAdmins(orgId)).toBe(false); - - await mrtService.updateHideSkipButtonForNonAdmins(orgId, true); - - expect(await mrtService.getHideSkipButtonForNonAdmins(orgId)).toBe(true); - }); - - it('persists the ignore callback url when the org has no settings row', async () => { - await mrtService.updateIgnoreCallbackUrl( - orgId, - 'https://example.com/webhook/ignore', - ); + testWithService( + 'persists a boolean toggle when the org has no settings row', + async ({ mrtService }) => { + const orgId = `no-row-${uid()}`; + expect(await mrtService.getHideSkipButtonForNonAdmins(orgId)).toBe( + false, + ); + + await mrtService.updateHideSkipButtonForNonAdmins(orgId, true); + + expect(await mrtService.getHideSkipButtonForNonAdmins(orgId)).toBe( + true, + ); + }, + ); - expect(await mrtService.getIgnoreCallbackUrl(orgId)).toBe( - 'https://example.com/webhook/ignore', - ); - }); + testWithService( + 'persists the ignore callback url when the org has no settings row', + async ({ mrtService }) => { + const orgId = `no-row-${uid()}`; + await mrtService.updateIgnoreCallbackUrl( + orgId, + 'https://example.com/webhook/ignore', + ); - it('leaves other columns at their defaults when upserting one setting', async () => { - await mrtService.updatePreviewJobsViewEnabled(orgId, true); + expect(await mrtService.getIgnoreCallbackUrl(orgId)).toBe( + 'https://example.com/webhook/ignore', + ); + }, + ); - expect(await mrtService.getPreviewJobsViewEnabled(orgId)).toBe(true); - expect(await mrtService.getRequiresPolicyForDecisions(orgId)).toBe(false); - expect(await mrtService.getRequiresDecisionReason(orgId)).toBe(false); - }); + testWithService( + 'leaves other columns at their defaults when upserting one setting', + async ({ mrtService }) => { + const orgId = `no-row-${uid()}`; + await mrtService.updatePreviewJobsViewEnabled(orgId, true); + + expect(await mrtService.getPreviewJobsViewEnabled(orgId)).toBe(true); + expect(await mrtService.getRequiresPolicyForDecisions(orgId)).toBe( + false, + ); + expect(await mrtService.getRequiresDecisionReason(orgId)).toBe(false); + }, + ); }); }); diff --git a/server/services/manualReviewToolService/manualReviewToolService.ts b/server/services/manualReviewToolService/manualReviewToolService.ts index 49ed67a7e..fff63eced 100644 --- a/server/services/manualReviewToolService/manualReviewToolService.ts +++ b/server/services/manualReviewToolService/manualReviewToolService.ts @@ -270,11 +270,7 @@ export type ManualReviewJobPayload = | NcmecManualReviewJobPayload; export type ManualReviewJobEnqueueSource = - | 'APPEAL' - | 'REPORT' - | 'RULE_EXECUTION' - | 'MRT_JOB' - | 'POST_ACTIONS'; + 'APPEAL' | 'REPORT' | 'RULE_EXECUTION' | 'MRT_JOB' | 'POST_ACTIONS'; export type RuleExecutionEnqueueSourceInfo = { kind: 'RULE_EXECUTION'; @@ -343,6 +339,7 @@ export class ManualReviewToolService { pgQueryReadReplica, moderationConfigService, redis, + tracer, ); this.jobEnrichment = new JobEnrichment( partialItemsService, @@ -1667,6 +1664,14 @@ export class ManualReviewToolService { userId: string; }) { await this.skipOps.logSkip(opts); + // Hides the job from THIS reviewer for the skip window and releases their + // lock so it returns to the shared pool immediately for everyone else. + await this.queueOps.recordReviewerSkip({ + orgId: opts.orgId, + queueId: opts.queueId, + reviewerId: opts.userId, + jobId: opts.jobId, + }); } async releaseJobLock(opts: { diff --git a/server/services/manualReviewToolService/modules/CommentOperations.test.ts b/server/services/manualReviewToolService/modules/CommentOperations.test.ts index dd17ef927..7e1801ef6 100644 --- a/server/services/manualReviewToolService/modules/CommentOperations.test.ts +++ b/server/services/manualReviewToolService/modules/CommentOperations.test.ts @@ -1,39 +1,34 @@ import { v1 as uuidv1 } from 'uuid'; -import getBottle from '../../../iocContainer/index.js'; import createOrg from '../../../test/fixtureHelpers/createOrg.js'; import createUser from '../../../test/fixtureHelpers/createUser.js'; -import { makeTestWithFixture } from '../../../test/utils.js'; +import { makeTransactionalTestWithFixture } from '../../../test/harness/transactionalTest.js'; import { UserPermission } from '../../userManagementService/index.js'; import { type JobId } from '../manualReviewToolService.js'; import CommentOperations from './CommentOperations.js'; describe('CommentOperations', () => { - const testWithFixtures = makeTestWithFixture(async () => { - const container = (await getBottle()).container; - const pgQuery = container.KyselyPg; - const commentOps = new CommentOperations(pgQuery); - - // Create test org - const orgId = uuidv1(); - const { cleanup: orgCleanup } = await createOrg( - { - KyselyPg: container.KyselyPg, - ModerationConfigService: container.ModerationConfigService, - ApiKeyService: container.ApiKeyService, - }, - orgId, - ); + const testWithFixtures = makeTransactionalTestWithFixture( + async ({ deps }) => { + const pgQuery = deps.KyselyPg; + const commentOps = new CommentOperations(pgQuery); + + // Create test org + const orgId = uuidv1(); + await createOrg( + { + KyselyPg: deps.KyselyPg, + ModerationConfigService: deps.ModerationConfigService, + ApiKeyService: deps.ApiKeyService, + }, + orgId, + ); - // Create test user - const { user, cleanup: userCleanup } = await createUser( - container.KyselyPg, - orgId, - ); + // Create test user + const { user } = await createUser(deps.KyselyPg, orgId); - // Create a queue (required for job_creations foreign key) - const queue = - await container.ManualReviewToolService.createManualReviewQueue({ + // Create a queue (required for job_creations foreign key) + const queue = await deps.ManualReviewToolService.createManualReviewQueue({ name: 'Test Queue', description: null, userIds: [user.id], @@ -46,75 +41,49 @@ describe('CommentOperations', () => { }, }); - // Create test item identifiers and jobs - const itemId = uuidv1(); - const itemTypeId = uuidv1(); - const jobId1 = uuidv1(); - const jobId2 = uuidv1(); - - await pgQuery - .insertInto('manual_review_tool.job_creations') - .values([ - { - id: jobId1 as JobId, - org_id: orgId, - item_id: itemId, - item_type_id: itemTypeId, - queue_id: queue.id, - created_at: new Date('2023-01-01'), - enqueue_source_info: {}, - }, - { - id: jobId2 as JobId, - org_id: orgId, - item_id: itemId, - item_type_id: itemTypeId, - queue_id: queue.id, - created_at: new Date('2023-01-02'), - enqueue_source_info: {}, - }, - ]) - .execute(); - - return { - commentOps, - pgQuery, - orgId, - userId: user.id, - itemId, - itemTypeId, - jobId1, - jobId2, - queueId: queue.id, - async cleanup() { - // Clean up comments - await pgQuery - .deleteFrom('manual_review_tool.job_comments') - .where('org_id', '=', orgId) - .execute(); - - // Clean up job_creations - await pgQuery - .deleteFrom('manual_review_tool.job_creations') - .where('org_id', '=', orgId) - .execute(); - - // Clean up queue - await container.ManualReviewToolService.deleteManualReviewQueueForTestsDO_NOT_USE( - orgId, - queue.id, - ); - - // Clean up user and org - await userCleanup(); - await orgCleanup(); - - // Close database connections - await container.KyselyPg.destroy(); - await container.KyselyPgReadReplica.destroy(); - }, - }; - }); + // Create test item identifiers and jobs + const itemId = uuidv1(); + const itemTypeId = uuidv1(); + const jobId1 = uuidv1(); + const jobId2 = uuidv1(); + + await pgQuery + .insertInto('manual_review_tool.job_creations') + .values([ + { + id: jobId1 as JobId, + org_id: orgId, + item_id: itemId, + item_type_id: itemTypeId, + queue_id: queue.id, + created_at: new Date('2023-01-01'), + enqueue_source_info: {}, + }, + { + id: jobId2 as JobId, + org_id: orgId, + item_id: itemId, + item_type_id: itemTypeId, + queue_id: queue.id, + created_at: new Date('2023-01-02'), + enqueue_source_info: {}, + }, + ]) + .execute(); + + return { + commentOps, + pgQuery, + orgId, + userId: user.id, + itemId, + itemTypeId, + jobId1, + jobId2, + queueId: queue.id, + }; + }, + ); describe('getRelatedJobIds', () => { testWithFixtures( diff --git a/server/services/manualReviewToolService/modules/JobDecisioning.test.ts b/server/services/manualReviewToolService/modules/JobDecisioning.test.ts new file mode 100644 index 000000000..2efbdf917 --- /dev/null +++ b/server/services/manualReviewToolService/modules/JobDecisioning.test.ts @@ -0,0 +1,40 @@ +import { parseItemCreatedAt } from './JobDecisioning.js'; + +describe('parseItemCreatedAt', () => { + test('parses a valid ISO string', () => { + expect(parseItemCreatedAt('2026-01-01T00:00:00.000Z')).toEqual( + new Date('2026-01-01T00:00:00.000Z'), + ); + }); + + test('parses an epoch-millis number', () => { + expect(parseItemCreatedAt(1735689600000)).toEqual(new Date(1735689600000)); + }); + + test('treats epoch 0 as a valid timestamp, not empty', () => { + expect(parseItemCreatedAt(0)).toEqual(new Date(0)); + }); + + test('passes a Date through', () => { + const d = new Date('2026-01-01T00:00:00.000Z'); + expect(parseItemCreatedAt(d)).toEqual(d); + }); + + test.each([null, undefined, ''])( + 'returns null for empty value %p', + (value) => { + expect(parseItemCreatedAt(value)).toBeNull(); + }, + ); + + // Regression: a truthy-but-unparseable createdAt (seen on reports from + // automated sources) produced an Invalid Date, which throws on pg + // serialization and failed the entire decision insert, surfacing as + // "Job submission failed" in the reviewer UI. + test.each([' ', 'not-a-date', 'garbage', '2026-99-99T99:99:99Z'])( + 'returns null for unparseable value %p instead of an Invalid Date', + (value) => { + expect(parseItemCreatedAt(value)).toBeNull(); + }, + ); +}); diff --git a/server/services/manualReviewToolService/modules/JobDecisioning.ts b/server/services/manualReviewToolService/modules/JobDecisioning.ts index af7edccfc..652980851 100644 --- a/server/services/manualReviewToolService/modules/JobDecisioning.ts +++ b/server/services/manualReviewToolService/modules/JobDecisioning.ts @@ -5,12 +5,14 @@ import { type JsonObject } from 'type-fest'; import { type Dependencies } from '../../../iocContainer/index.js'; import { filterNullOrUndefined } from '../../../utils/collections.js'; +import { jsonStringify } from '../../../utils/encoding.js'; import { CoopError, ErrorType, type ErrorInstanceData, } from '../../../utils/errors.js'; import { assertUnreachable } from '../../../utils/misc.js'; +import { isValidDate } from '../../../utils/time.js'; import { isNonEmptyString } from '../../../utils/typescript-types.js'; import { getFieldValueForRole } from '../../itemProcessingService/index.js'; import { type NCMECMediaReport } from '../../ncmecService/ncmecReporting.js'; @@ -75,6 +77,24 @@ type MRTJobAutoCloseReason = */ export const AUTOMATED_DECISION_REVIEWER_ID = ''; +/** + * Normalizes an item's createdAt field value into a Date for the + * `item_created_at` column. A truthy-but-unparseable value (whitespace, or a + * non-ISO format some report sources emit) yields an Invalid Date, which throws + * on pg serialization and fails the entire decision insert. Return null in that + * case so the nullable column absorbs it and the decision still records. + */ +export function parseItemCreatedAt( + value: string | number | Date | null | undefined, +): Date | null { + // Guard only null/undefined/empty-string; a numeric 0 is a valid epoch. + if (value == null || value === '') { + return null; + } + const parsed = new Date(value); + return isValidDate(parsed) ? parsed : null; +} + export type ManualReviewDecisionComponent = | { type: 'IGNORE' } | { @@ -115,8 +135,7 @@ export type ManualReviewDecisionComponent = }; export type ManualReviewDecisionType = - | ManualReviewDecisionComponent['type'] - | 'RELATED_ACTION'; + ManualReviewDecisionComponent['type'] | 'RELATED_ACTION'; export type CustomActionDecisionComponent = Extract< ManualReviewDecisionComponent, @@ -413,7 +432,10 @@ export default class JobDecisioning { return { newDecisionStored: logDecisionStatus === 'SUCCESS', - error: match([logDecisionStatus, removeJobStatus] as const) + error: match([logDecisionStatus, removeJobStatus] as readonly [ + 'SUCCESS' | 'ALREADY_LOGGED', + 'SUCCESS' | 'FAILED', + ]) // Case 1, happy path. .with(['SUCCESS', 'SUCCESS'], () => undefined) // Case 2, decision logged but job not deleted. @@ -624,9 +646,36 @@ export default class JobDecisioning { job.payload.item.data, ) : null; - const itemCreatedAt = itemCreatedAtField - ? new Date(itemCreatedAtField) - : null; + const itemCreatedAt = parseItemCreatedAt(itemCreatedAtField); + + // Record when a present createdAt couldn't be parsed. We store null so the + // decision still saves, but surface the bad value so it's diagnosable and + // can be backfilled rather than silently dropped. + if ( + itemCreatedAt === null && + itemCreatedAtField != null && + itemCreatedAtField !== '' + ) { + this.tracer.addSpan( + { + resource: 'mrtService', + operation: 'logDecision.invalidItemCreatedAt', + }, + (span) => { + span.setAttribute('job.id', job.id); + span.setAttribute('org.id', orgId); + this.tracer.logSpanFailed( + span, + new Error( + `Unparseable item createdAt for job ${job.id}: ${jsonStringify( + itemCreatedAtField, + )}. Storing null.`, + ), + ); + return null; + }, + ); + } return this.pgQuery .insertInto('manual_review_tool.manual_review_decisions') diff --git a/server/services/manualReviewToolService/modules/JobRouting.test.ts b/server/services/manualReviewToolService/modules/JobRouting.test.ts index 30c01d086..eca6eb3dc 100644 --- a/server/services/manualReviewToolService/modules/JobRouting.test.ts +++ b/server/services/manualReviewToolService/modules/JobRouting.test.ts @@ -1,12 +1,10 @@ -/* eslint-disable max-lines */ import { ScalarTypes } from '@roostorg/coop-types'; import { uid } from 'uid'; -import getBottle from '../../../iocContainer/index.js'; import createContentItemTypes from '../../../test/fixtureHelpers/createContentItemTypes.js'; import createOrg from '../../../test/fixtureHelpers/createOrg.js'; import createUser from '../../../test/fixtureHelpers/createUser.js'; -import { makeTestWithFixture } from '../../../test/utils.js'; +import { makeTransactionalTestWithFixture } from '../../../test/harness/transactionalTest.js'; import { toCorrelationId } from '../../../utils/correlationIds.js'; import { jsonStringify } from '../../../utils/encoding.js'; import { type NonEmptyString } from '../../../utils/typescript-types.js'; @@ -20,25 +18,21 @@ import { SignalType } from '../../signalsService/index.js'; import { UserPermission } from '../../userManagementService/index.js'; describe('JobRouting tests', () => { - const jobRoutingTestWithFixtures = makeTestWithFixture(async () => { - const { container } = await getBottle(); - const manualReviewToolService = container.ManualReviewToolService; - const { org, cleanup: orgCleanup } = await createOrg( - { - KyselyPg: container.KyselyPg, - ModerationConfigService: container.ModerationConfigService, - ApiKeyService: container.ApiKeyService, - }, - uid(), - ); - const { user, cleanup: userCleanup } = await createUser( - container.KyselyPg, - org.id, - ); - const userId = user.id; - const { itemTypes, cleanup: itemTypesCleanup } = - await createContentItemTypes({ - moderationConfigService: container.ModerationConfigService, + const jobRoutingTestWithFixtures = makeTransactionalTestWithFixture( + async ({ deps }) => { + const manualReviewToolService = deps.ManualReviewToolService; + const { org } = await createOrg( + { + KyselyPg: deps.KyselyPg, + ModerationConfigService: deps.ModerationConfigService, + ApiKeyService: deps.ApiKeyService, + }, + uid(), + ); + const { user } = await createUser(deps.KyselyPg, org.id); + const userId = user.id; + const { itemTypes } = await createContentItemTypes({ + moderationConfigService: deps.ModerationConfigService, orgId: org.id, extra: { fields: [ @@ -51,110 +45,112 @@ describe('JobRouting tests', () => { ], }, }); - const itemType = itemTypes[0]; - - const defaultQueue = await manualReviewToolService.createManualReviewQueue({ - name: 'Default Queue', - description: null, - userIds: [userId], - hiddenActionIds: [], - isAppealsQueue: false, - invokedBy: { - userId, - permissions: [UserPermission.EDIT_MRT_QUEUES], - orgId: org.id, - }, - }); - const anotherQueue = await manualReviewToolService.createManualReviewQueue({ - name: 'Another Queue', - description: null, - userIds: [userId], - hiddenActionIds: [], - isAppealsQueue: false, - invokedBy: { - userId, - permissions: [UserPermission.EDIT_MRT_QUEUES], - orgId: org.id, - }, - }); - const policyQueue = await manualReviewToolService.createManualReviewQueue({ - name: 'Policy Queue', - description: null, - userIds: [userId], - hiddenActionIds: [], - isAppealsQueue: false, - invokedBy: { - userId, - permissions: [UserPermission.EDIT_MRT_QUEUES], - orgId: org.id, - }, - }); - const noPolicyQueue = await manualReviewToolService.createManualReviewQueue( - { - name: 'No Policy Queue', - description: null, - userIds: [userId], - hiddenActionIds: [], - isAppealsQueue: false, - invokedBy: { - userId, - permissions: [UserPermission.EDIT_MRT_QUEUES], - orgId: org.id, + const itemType = itemTypes[0]; + + const defaultQueue = + await manualReviewToolService.createManualReviewQueue({ + name: 'Default Queue', + description: null, + userIds: [userId], + hiddenActionIds: [], + isAppealsQueue: false, + invokedBy: { + userId, + permissions: [UserPermission.EDIT_MRT_QUEUES], + orgId: org.id, + }, + }); + const anotherQueue = + await manualReviewToolService.createManualReviewQueue({ + name: 'Another Queue', + description: null, + userIds: [userId], + hiddenActionIds: [], + isAppealsQueue: false, + invokedBy: { + userId, + permissions: [UserPermission.EDIT_MRT_QUEUES], + orgId: org.id, + }, + }); + const policyQueue = await manualReviewToolService.createManualReviewQueue( + { + name: 'Policy Queue', + description: null, + userIds: [userId], + hiddenActionIds: [], + isAppealsQueue: false, + invokedBy: { + userId, + permissions: [UserPermission.EDIT_MRT_QUEUES], + orgId: org.id, + }, }, - }, - ); - - const rule = await manualReviewToolService.createRoutingRule({ - orgId: org.id, - name: 'Some rule', - status: 'LIVE', - itemTypeIds: [itemType.id as NonEmptyString], - creatorId: '', - conditionSet: { - conjunction: 'AND', - conditions: [ - { - input: { - type: 'CONTENT_FIELD', - name: 'text', - contentTypeId: itemType.id, - }, - signal: { - id: jsonStringify({ + ); + const noPolicyQueue = + await manualReviewToolService.createManualReviewQueue({ + name: 'No Policy Queue', + description: null, + userIds: [userId], + hiddenActionIds: [], + isAppealsQueue: false, + invokedBy: { + userId, + permissions: [UserPermission.EDIT_MRT_QUEUES], + orgId: org.id, + }, + }); + + await manualReviewToolService.createRoutingRule({ + orgId: org.id, + name: 'Some rule', + status: 'LIVE', + itemTypeIds: [itemType.id as NonEmptyString], + creatorId: '', + conditionSet: { + conjunction: 'AND', + conditions: [ + { + input: { + type: 'CONTENT_FIELD', + name: 'text', + contentTypeId: itemType.id, + }, + signal: { + id: jsonStringify({ + type: SignalType.TEXT_MATCHING_CONTAINS_TEXT, + }), type: SignalType.TEXT_MATCHING_CONTAINS_TEXT, - }), - type: SignalType.TEXT_MATCHING_CONTAINS_TEXT, + }, + matchingValues: { strings: ['test'] }, }, - matchingValues: { strings: ['test'] }, - }, - ], - }, - destinationQueueId: anotherQueue.id, - }); - - const policyRule = await manualReviewToolService.createRoutingRule({ - orgId: org.id, - name: 'Policy ID rule', - status: 'LIVE', - itemTypeIds: [itemType.id as NonEmptyString], - creatorId: '', - conditionSet: { - conjunction: 'OR', - conditions: [ - { - input: { - type: 'CONTENT_COOP_INPUT', - name: 'Relevant Policy', + ], + }, + destinationQueueId: anotherQueue.id, + }); + + await manualReviewToolService.createRoutingRule({ + orgId: org.id, + name: 'Policy ID rule', + status: 'LIVE', + itemTypeIds: [itemType.id as NonEmptyString], + creatorId: '', + conditionSet: { + conjunction: 'OR', + conditions: [ + { + input: { + type: 'CONTENT_COOP_INPUT', + name: 'Relevant Policy', + }, + threshold: 'testPolicyId', + comparator: 'EQUALS', }, - threshold: 'testPolicyId', - comparator: 'EQUALS', - }, - ], - }, - destinationQueueId: policyQueue.id, - }); + ], + }, + destinationQueueId: policyQueue.id, + }); - const policyNotProvidedRule = await manualReviewToolService.createRoutingRule({ orgId: org.id, name: 'Policy ID not provided rule', @@ -190,76 +186,39 @@ describe('JobRouting tests', () => { destinationQueueId: noPolicyQueue.id, }); - const sourceTypeRule = await manualReviewToolService.createRoutingRule({ - orgId: org.id, - name: 'Source Type rule', - status: 'LIVE', - itemTypeIds: [itemType.id as NonEmptyString], - creatorId: '', - conditionSet: { - conjunction: 'OR', - conditions: [ - { - input: { - type: 'CONTENT_COOP_INPUT', - name: 'Source', + await manualReviewToolService.createRoutingRule({ + orgId: org.id, + name: 'Source Type rule', + status: 'LIVE', + itemTypeIds: [itemType.id as NonEmptyString], + creatorId: '', + conditionSet: { + conjunction: 'OR', + conditions: [ + { + input: { + type: 'CONTENT_COOP_INPUT', + name: 'Source', + }, + threshold: 'post-actions', + comparator: 'EQUALS', }, - threshold: 'post-actions', - comparator: 'EQUALS', - }, - ], - }, - destinationQueueId: anotherQueue.id, - }); + ], + }, + destinationQueueId: anotherQueue.id, + }); - return { - manualReviewToolService, - org, - itemType, - defaultQueue, - anotherQueue, - policyQueue, - noPolicyQueue, - async cleanup() { - await manualReviewToolService.deleteRoutingRule({ - id: rule.id, - orgId: org.id, - }); - await manualReviewToolService.deleteRoutingRule({ - id: policyRule.id, - orgId: org.id, - }); - await manualReviewToolService.deleteRoutingRule({ - id: policyNotProvidedRule.id, - orgId: org.id, - }); - await manualReviewToolService.deleteRoutingRule({ - id: sourceTypeRule.id, - orgId: org.id, - }); - await manualReviewToolService.deleteManualReviewQueueForTestsDO_NOT_USE( - org.id, - anotherQueue.id, - ); - await manualReviewToolService.deleteManualReviewQueueForTestsDO_NOT_USE( - org.id, - policyQueue.id, - ); - await manualReviewToolService.deleteManualReviewQueueForTestsDO_NOT_USE( - org.id, - defaultQueue.id, - ); - await manualReviewToolService.deleteManualReviewQueueForTestsDO_NOT_USE( - org.id, - noPolicyQueue.id, - ); - await itemTypesCleanup(); - await userCleanup(); - await orgCleanup(); - await container.closeSharedResourcesForShutdown(); - }, - }; - }); + return { + manualReviewToolService, + org, + itemType, + defaultQueue, + anotherQueue, + policyQueue, + noPolicyQueue, + }; + }, + ); jobRoutingTestWithFixtures( 'Should enqueue based off of routing rule', @@ -593,10 +552,9 @@ describe('JobRouting tests', () => { orgId: org.id, queueId: defaultQueue.id, }); - // Deleting the queue will also delete the routing rule, via cascading - // delete. However, the old routing rules will still be in the MRT Service's - // cache, meaning that running the rules will now point to a queue that - // doesn't exist. In this case, it should fall back to the default. + // deleteManualReviewQueueForTestsDO_NOT_USE removes any routing rules that + // reference the queue (RESTRICT FK) before deleting it. After deletion, + // enqueue should find no matching rules and fall back to the default queue. await manualReviewToolService.deleteManualReviewQueueForTestsDO_NOT_USE( org.id, anotherQueue.id, diff --git a/server/services/manualReviewToolService/modules/QueueOperations.reviewerSkips.test.ts b/server/services/manualReviewToolService/modules/QueueOperations.reviewerSkips.test.ts new file mode 100644 index 000000000..a98165070 --- /dev/null +++ b/server/services/manualReviewToolService/modules/QueueOperations.reviewerSkips.test.ts @@ -0,0 +1,188 @@ +import { uid } from 'uid'; + +import createMrtQueue from '../../../test/fixtureHelpers/createMrtQueue.js'; +import createOrg from '../../../test/fixtureHelpers/createOrg.js'; +import createUser from '../../../test/fixtureHelpers/createUser.js'; +import { makeTransactionalTestWithFixture } from '../../../test/harness/transactionalTest.js'; +import { instantiateOpaqueType } from '../../../utils/typescript-types.js'; +import { + makeSubmissionId, + type NormalizedItemData, +} from '../../itemProcessingService/index.js'; +import { type ItemSubmissionWithTypeIdentifier } from '../../itemProcessingService/makeItemSubmissionWithTypeIdentifier.js'; +import { type ManualReviewJobPayload } from '../manualReviewToolService.js'; + +describe('QueueOperations per-reviewer skips', () => { + // Runs inside a transaction that rolls back, so the fixtures need no manual + // teardown. + const testWithQueue = makeTransactionalTestWithFixture(async ({ deps }) => { + const { org } = await createOrg( + { + KyselyPg: deps.KyselyPg, + ModerationConfigService: deps.ModerationConfigService, + ApiKeyService: deps.ApiKeyService, + }, + uid(), + ); + + const { user } = await createUser(deps.KyselyPg, org.id); + + const { queue } = await createMrtQueue({ + orgId: org.id, + mrtService: deps.ManualReviewToolService, + userId: user.id, + }); + + return { + org, + queue, + user, + mrtService: deps.ManualReviewToolService, + }; + }); + + const makePayloadFor = + (itemTypeId: string) => + (itemId: string): ManualReviewJobPayload => ({ + kind: 'DEFAULT', + reportHistory: [], + reportedForReasons: [], + item: instantiateOpaqueType({ + submissionId: makeSubmissionId(), + submissionTime: new Date(), + // eslint-disable-next-line @typescript-eslint/consistent-type-assertions + data: {} as NormalizedItemData, + itemTypeIdentifier: { + id: itemTypeId, + version: new Date().toISOString(), + schemaVariant: 'original', + }, + creator: { id: uid(), typeId: uid() }, + itemId, + }), + enqueueSourceInfo: { kind: 'REPORT' }, + }); + + testWithQueue( + 'a skipped job is hidden from that reviewer but immediately available to others', + async ({ org, queue, mrtService }) => { + const queueOps = mrtService['queueOps']; + const payloadFor = makePayloadFor(uid()); + + const xJob = await queueOps.addJob({ + orgId: org.id, + queueId: queue.id, + enqueueSourceInfo: { kind: 'REPORT' }, + priority: 1000, + jobPayload: { policyIds: [], payload: payloadFor('item-X') }, + }); + await queueOps.addJob({ + orgId: org.id, + queueId: queue.id, + enqueueSourceInfo: { kind: 'REPORT' }, + priority: 2000, + jobPayload: { policyIds: [], payload: payloadFor('item-Y') }, + }); + + await queueOps.recordReviewerSkip({ + orgId: org.id, + queueId: queue.id, + reviewerId: 'reviewer-a', + jobId: xJob.id, + }); + + const aJob = await queueOps.dequeueNextJobWithLock({ + orgId: org.id, + queueId: queue.id, + lockToken: 'reviewer-a', + }); + expect(aJob?.job.payload.item.itemId).toBe('item-Y'); + + const bJob = await queueOps.dequeueNextJobWithLock({ + orgId: org.id, + queueId: queue.id, + lockToken: 'reviewer-b', + }); + expect(bJob?.job.payload.item.itemId).toBe('item-X'); + }, + ); + + testWithQueue( + 'a queue whose only jobs are skipped returns null instead of hanging', + async ({ org, queue, mrtService }) => { + const queueOps = mrtService['queueOps']; + const payloadFor = makePayloadFor(uid()); + + const onlyJob = await queueOps.addJob({ + orgId: org.id, + queueId: queue.id, + enqueueSourceInfo: { kind: 'REPORT' }, + priority: 1000, + jobPayload: { policyIds: [], payload: payloadFor('item-X') }, + }); + await queueOps.recordReviewerSkip({ + orgId: org.id, + queueId: queue.id, + reviewerId: 'reviewer-a', + jobId: onlyJob.id, + }); + + const result = await queueOps.dequeueNextJobWithLock({ + orgId: org.id, + queueId: queue.id, + lockToken: 'reviewer-a', + }); + expect(result).toBeNull(); + }, + ); + + testWithQueue( + 'logSkip hides the job from the skipper and releases their lock in one call', + async ({ org, queue, user, mrtService }) => { + const queueOps = mrtService['queueOps']; + const payloadFor = makePayloadFor(uid()); + + await queueOps.addJob({ + orgId: org.id, + queueId: queue.id, + enqueueSourceInfo: { kind: 'REPORT' }, + priority: 1000, + jobPayload: { policyIds: [], payload: payloadFor('item-X') }, + }); + await queueOps.addJob({ + orgId: org.id, + queueId: queue.id, + enqueueSourceInfo: { kind: 'REPORT' }, + priority: 2000, + jobPayload: { policyIds: [], payload: payloadFor('item-Y') }, + }); + + const first = await queueOps.dequeueNextJobWithLock({ + orgId: org.id, + queueId: queue.id, + lockToken: user.id, + }); + expect(first?.job.payload.item.itemId).toBe('item-X'); + await mrtService.logSkip({ + orgId: org.id, + queueId: queue.id, + jobId: first!.job.id, + userId: user.id, + }); + + const other = await queueOps.dequeueNextJobWithLock({ + orgId: org.id, + queueId: queue.id, + lockToken: 'reviewer-b', + }); + expect(other?.job.payload.item.itemId).toBe('item-X'); + + const next = await queueOps.dequeueNextJobWithLock({ + orgId: org.id, + queueId: queue.id, + lockToken: user.id, + }); + expect(next?.job.payload.item.itemId).toBe('item-Y'); + }, + ); +}); diff --git a/server/services/manualReviewToolService/modules/QueueOperations.test.ts b/server/services/manualReviewToolService/modules/QueueOperations.test.ts index 24dd8873c..b0422f29c 100644 --- a/server/services/manualReviewToolService/modules/QueueOperations.test.ts +++ b/server/services/manualReviewToolService/modules/QueueOperations.test.ts @@ -1,14 +1,14 @@ import fc from 'fast-check'; import { uid } from 'uid'; +import { v1 as uuidv1 } from 'uuid'; -import getBottle from '../../../iocContainer/index.js'; import createActions from '../../../test/fixtureHelpers/createActions.js'; import createContentItemTypes from '../../../test/fixtureHelpers/createContentItemTypes.js'; import createMrtQueue from '../../../test/fixtureHelpers/createMrtQueue.js'; import createOrg from '../../../test/fixtureHelpers/createOrg.js'; import createUser from '../../../test/fixtureHelpers/createUser.js'; +import makeDummyMrtJobPayload from '../../../test/fixtureHelpers/makeDummyMrtJobPayload.js'; import { makeTransactionalTestWithFixture } from '../../../test/harness/transactionalTest.js'; -import { makeTestWithFixture } from '../../../test/utils.js'; import { UserPermission } from '../../userManagementService/index.js'; import { bullJobIdtoExternalJobId, @@ -34,65 +34,52 @@ describe('QueueOperations', () => { }); const testWithQueueAndActions = () => - makeTestWithFixture(async () => { - const container = (await getBottle()).container; - - const { org, cleanup: orgCleanup } = await createOrg( + makeTransactionalTestWithFixture(async ({ deps }) => { + const { org } = await createOrg( { - KyselyPg: container.KyselyPg, - ModerationConfigService: container.ModerationConfigService, - ApiKeyService: container.ApiKeyService, + KyselyPg: deps.KyselyPg, + ModerationConfigService: deps.ModerationConfigService, + ApiKeyService: deps.ApiKeyService, }, uid(), ); - const { user, cleanup: userCleanup } = await createUser( - container.KyselyPg, - org.id, - ); - const { itemTypes, cleanup: itemTypesCleanup } = - await createContentItemTypes({ - moderationConfigService: container.ModerationConfigService, - orgId: org.id, - extra: { - fields: [ - { - name: 'someField', - type: 'NUMBER', - required: false, - container: null, - }, - ], - }, - }); + const { user } = await createUser(deps.KyselyPg, org.id); + const { itemTypes } = await createContentItemTypes({ + moderationConfigService: deps.ModerationConfigService, + orgId: org.id, + extra: { + fields: [ + { + name: 'someField', + type: 'NUMBER', + required: false, + container: null, + }, + ], + }, + }); - const { actions, cleanup: actionsCleanup } = await createActions({ - actionAPI: container.ActionAPIDataSource, + const { actions } = await createActions({ + actionAPI: deps.ActionAPIDataSource, itemTypeIds: itemTypes.map((it) => it.id), orgId: org.id, numActions: 3, }); - const { queue, cleanup: queuesCleanup } = await createMrtQueue({ + const { queue } = await createMrtQueue({ orgId: org.id, - mrtService: container.ManualReviewToolService, + mrtService: deps.ManualReviewToolService, userId: user.id, }); return { org, + user, actions, queue, - mrtService: container.ManualReviewToolService, - cleanup: async () => { - await queuesCleanup(); - await actionsCleanup(); - await itemTypesCleanup(); - await userCleanup(); - await orgCleanup(); - await container.KyselyPg.destroy(); - await container.KyselyPgReadReplica.destroy(); - }, + kyselyPg: deps.KyselyPg, + mrtService: deps.ManualReviewToolService, }; }); @@ -106,7 +93,6 @@ describe('QueueOperations', () => { expect(hiddenActions.length).toEqual(0); }, ); - testWithQueueAndActions()( 'Test hiding an action', async ({ org, queue, mrtService, actions }) => { @@ -127,7 +113,6 @@ describe('QueueOperations', () => { expect(hiddenActions[0]).toEqual(actionToHide.id); }, ); - testWithQueueAndActions()( 'Test unhiding an action', async ({ org, queue, mrtService, actions }) => { @@ -156,7 +141,6 @@ describe('QueueOperations', () => { expect(hiddenActions).not.toContain(actionToUnhide.id); }, ); - testWithQueueAndActions()( 'Test hiding some actions and unhiding some others', async ({ org, queue, mrtService, actions }) => { @@ -220,6 +204,44 @@ describe('QueueOperations', () => { }, ); + // Regression: this used to read the queue with getJobs([state], 0, 0), + // which defaults to descending order and returns the *newest* job — the + // dashboard's "Oldest Task Age" column showed the newest task's age. + testWithQueueAndActions()( + 'getOldestJobCreatedAt returns the oldest waiting job, not the newest', + async ({ org, queue, mrtService }) => { + const olderCreatedAt = new Date(Date.now() - 2 * 60 * 60 * 1000); + const newerCreatedAt = new Date(Date.now() - 60 * 1000); + + // Enqueue the older job first: BullMQ pushes new jobs onto the head of + // the wait list, so a descending read returns the most recently added + // job and this test fails without the oldest-first fix. + await mrtService['queueOps']['addJob']({ + orgId: org.id, + queueId: queue.id, + enqueueSourceInfo: { kind: 'REPORT' }, + jobPayload: makeDummyMrtJobPayload({ createdAt: olderCreatedAt }), + }); + await mrtService['queueOps']['addJob']({ + orgId: org.id, + queueId: queue.id, + enqueueSourceInfo: { kind: 'REPORT' }, + jobPayload: makeDummyMrtJobPayload({ createdAt: newerCreatedAt }), + }); + + const oldest = await mrtService.getOldestJobCreatedAt({ + orgId: org.id, + queueId: queue.id, + isAppealsQueue: false, + }); + + expect(oldest).not.toBeNull(); + // BullMQ round-trips job data through JSON, so createdAt may come back + // as an ISO string; compare by timestamp. + expect(new Date(oldest!).getTime()).toEqual(olderCreatedAt.getTime()); + }, + ); + testWithQueueAndActions()( 'deleteAllJobsFromQueue accepts MANAGE_ORG', async ({ org, queue, mrtService }) => { @@ -232,7 +254,6 @@ describe('QueueOperations', () => { ).resolves.toBeUndefined(); }, ); - const testWithTwoOrgs = () => makeTransactionalTestWithFixture(async ({ deps }) => { const buildOrg = async () => { @@ -279,7 +300,6 @@ describe('QueueOperations', () => { expect(viewers.map((v) => v.userId)).not.toContain(attacker.user.id); }, ); - testWithTwoOrgs()( 'addAccessibleQueuesForUser must not grant access for a user in a different org', async ({ attacker, victim, mrtService }) => { @@ -299,7 +319,6 @@ describe('QueueOperations', () => { expect(viewers.map((v) => v.userId)).not.toContain(victim.user.id); }, ); - testWithTwoOrgs()( 'removeAccessibleQueuesForUser must not revoke access for a queue in a different org', async ({ attacker, victim, mrtService }) => { @@ -325,7 +344,6 @@ describe('QueueOperations', () => { expect(viewers.map((v) => v.userId)).toContain(victim.user.id); }, ); - testWithTwoOrgs()( 'removeAccessibleQueuesForUser must not revoke access for a user in a different org', async ({ attacker, victim, mrtService }) => { @@ -407,4 +425,76 @@ describe('QueueOperations', () => { expect(viewers.map((v) => v.userId)).not.toContain(victim.user.id); }, ); + + // Regression: RESTRICT FK must block queue deletion when routing rules reference it. + type QueueFixture = Parameters< + Parameters>[1] + >[0]; + type RuleTable = + | 'manual_review_tool.routing_rules' + | 'manual_review_tool.appeals_routing_rules'; + + const expectDeletionBlockedByRoutingRule = async ( + { org, user, mrtService, kyselyPg }: QueueFixture, + table: RuleTable, + ruleName: string, + ) => { + const secondQueue = await mrtService.createManualReviewQueue({ + name: `delete-test-queue-${uid()}`, + description: null, + userIds: [user.id], + hiddenActionIds: [], + isAppealsQueue: false, + invokedBy: { + userId: user.id, + permissions: [UserPermission.EDIT_MRT_QUEUES], + orgId: org.id, + }, + }); + await kyselyPg + .insertInto(table) + .values({ + id: uuidv1(), + org_id: org.id, + name: ruleName, + description: null, + status: 'LIVE', + condition_set: { conditions: [], conjunction: 'AND' }, + destination_queue_id: secondQueue.id, + creator_id: user.id, + sequence_number: 99, + }) + .execute(); + await expect( + mrtService.deleteManualReviewQueue(org.id, secondQueue.id), + ).rejects.toMatchObject({ name: 'QueueHasDependentRoutingRulesError' }); + await kyselyPg + .deleteFrom(table) + .where('destination_queue_id', '=', secondQueue.id) + .execute(); + await mrtService.deleteManualReviewQueueForTestsDO_NOT_USE( + org.id, + secondQueue.id, + ); + }; + + testWithQueueAndActions()( + 'deleteManualReviewQueue rejects when a routing rule references the queue', + async (ctx) => + expectDeletionBlockedByRoutingRule( + ctx, + 'manual_review_tool.routing_rules', + 'block-rule', + ), + ); + + testWithQueueAndActions()( + 'deleteManualReviewQueue rejects when an appeals routing rule references the queue', + async (ctx) => + expectDeletionBlockedByRoutingRule( + ctx, + 'manual_review_tool.appeals_routing_rules', + 'block-appeals-rule', + ), + ); }); diff --git a/server/services/manualReviewToolService/modules/QueueOperations.ts b/server/services/manualReviewToolService/modules/QueueOperations.ts index 0fbbd1ec4..2aecef6bc 100644 --- a/server/services/manualReviewToolService/modules/QueueOperations.ts +++ b/server/services/manualReviewToolService/modules/QueueOperations.ts @@ -15,6 +15,7 @@ import { filterNullOrUndefined } from '../../../utils/collections.js'; import { b64UrlDecode, b64UrlEncode, + jsonStringify, type B64UrlOf, } from '../../../utils/encoding.js'; import { @@ -23,7 +24,10 @@ import { makeUnauthorizedError, type ErrorInstanceData, } from '../../../utils/errors.js'; -import { isUniqueViolationError } from '../../../utils/kysely.js'; +import { + isForeignKeyViolationError, + isUniqueViolationError, +} from '../../../utils/kysely.js'; import { makeKyselyTransactionWithRetry, type KyselyTransactionWithRetry, @@ -107,7 +111,8 @@ export type QueueOperationsErrorType = | 'DeleteAllJobsUnauthorizedError' | 'QueueDoesNotExistError' | 'UnableToDeleteDefaultQueueError' - | 'AccessibleQueueNotInOrgError'; + | 'AccessibleQueueNotInOrgError' + | 'QueueHasDependentRoutingRulesError'; // Compound identifier for a queue. orgId is needed for security, but also // because queues are/will be actually sharded across redis instances for @@ -163,7 +168,8 @@ export default class QueueOperations { private readonly pgQuery: Kysely, private readonly pgQueryReadReplica: Kysely, private readonly moderationConfigService: Dependencies['ModerationConfigService'], - redis: RedisConnection, + private readonly redis: RedisConnection, + private readonly tracer: Dependencies['Tracer'], ) { this.transactionWithRetry = makeKyselyTransactionWithRetry(this.pgQuery); // Reassingment here is a hack to work around TS syntax limitations @@ -448,10 +454,9 @@ export default class QueueOperations { } const queue = await this.getOrCreateBullQueue({ orgId, queueId }); - await queue.obliterate({ force: true }); - - const numDeletedRows = await this.transactionWithRetry( - async (transaction) => { + let numDeletedRows: bigint; + try { + numDeletedRows = await this.transactionWithRetry(async (transaction) => { // Delete the queue scoped by org first. If it doesn't belong to the // caller's org, no rows are touched and we bail before deleting any // join rows. `users_and_accessible_queues` has no `org_id` column, @@ -473,8 +478,55 @@ export default class QueueOperations { .execute(); return queueDelete.numDeletedRows; - }, - ); + }); + } catch (e) { + const constraint = (e as { constraint?: string }).constraint; + if ( + isForeignKeyViolationError(e) && + (constraint === 'routing_rules_destination_queue_id_fkey' || + constraint === 'appeals_routing_rules_destination_queue_id_fkey') + ) { + // routing_rules and appeals_routing_rules have RESTRICT FKs to this + // queue. Query for their names so the error message is actionable. + const [routingRules, appealsRoutingRules] = await Promise.all([ + this.pgQuery + .selectFrom('manual_review_tool.routing_rules') + .select(['name']) + .where('destination_queue_id', '=', queueId) + .where('org_id', '=', orgId) + .execute(), + this.pgQuery + .selectFrom('manual_review_tool.appeals_routing_rules') + .select(['name']) + .where('destination_queue_id', '=', queueId) + .where('org_id', '=', orgId) + .execute(), + ]); + const ruleNames = [ + ...routingRules.map((r) => r.name), + ...appealsRoutingRules.map((r) => r.name), + ]; + throw makeQueueHasDependentRoutingRulesError(ruleNames, { + shouldErrorSpan: false, + }); + } + throw e; + } + + if (numDeletedRows === 1n) { + try { + await queue.obliterate({ force: true }); + } catch (e) { + // The DB row is already gone at this point, so a retry would see + // numDeletedRows === 0n and skip obliterate() entirely, silently + // leaving orphaned Bull/Redis data behind. Best-effort cleanup: + // surface the failure to the active tracing span (mirrors the + // pattern used elsewhere, e.g. `UserApi.logout`) so ops can run + // `server/bin/recover-mrt-queue.ts`, but still report success since + // the DB delete itself succeeded. + this.tracer.logActiveSpanFailedIfAny(e); + } + } return numDeletedRows === 1n; } @@ -489,8 +541,23 @@ export default class QueueOperations { // See `deleteManualReviewQueue` for why this is serialized + ownership- // checked. Same pattern, just without the default-queue guard. + // + // routing_rules and appeals_routing_rules have RESTRICT FKs to this queue, + // so we must delete any referencing rules before deleting the queue. const numDeletedRows = await this.transactionWithRetry( async (transaction) => { + await transaction + .deleteFrom('manual_review_tool.routing_rules') + .where('destination_queue_id', '=', queueId) + .where('org_id', '=', orgId) + .execute(); + + await transaction + .deleteFrom('manual_review_tool.appeals_routing_rules') + .where('destination_queue_id', '=', queueId) + .where('org_id', '=', orgId) + .execute(); + const queueDelete = await transaction .deleteFrom('manual_review_tool.manual_review_queues') .where('id', '=', queueId) @@ -1253,7 +1320,9 @@ export default class QueueOperations { let hasDecision = true; while (hasDecision) { - const job = await worker.getNextJob(lockToken); + // block: false so a drained queue returns null immediately instead of + // long-polling and hanging the reviewer's request. + const job = await worker.getNextJob(lockToken, { block: false }); if (!job) { return null; @@ -1280,7 +1349,12 @@ export default class QueueOperations { queueId, lockToken, jobId: job.data.id, - }).catch(() => {}); + }).catch((error: unknown) => { + // Non-fatal: the scan should still hand this reviewer a job. But it + // must not be silent — see the note on the same call in + // `dequeueNextJobWithLock`. + this.tracer.logActiveSpanFailedIfAny(error); + }); // then continue while loop } else { // this is the most likely case, where there is a job @@ -1304,47 +1378,139 @@ export default class QueueOperations { await this.checkQueueExists(orgId, queueId); const worker = await this.getBullWorker({ orgId, queueId }); - let hasDecision = true; - while (hasDecision) { - const job = await worker.getNextJob(lockToken); - - if (!job) { - return null; - } + // Jobs this reviewer skipped within the skip window (the lock token is + // the reviewer's userId). The scan steps past them by keeping them locked + // until it finishes, then releases them in `finally` so they return to + // the shared pool — a skip is per-reviewer, not global. + const reviewerSkips = await this.getActiveReviewerSkips({ + orgId, + queueId, + reviewerId: lockToken, + }); + const heldAside: Job[] = []; - const convertedJob = await this.legacyJobToJob(job, orgId); + try { + while (true) { + const job = await worker.getNextJob(lockToken, { block: false }); - // There is a race condition due to the locking mechanism where a job can - // be decided on but not dequeued, so we check here if the first job in the - // queue has a decision, and if so use the lock token to immediately - // remove it, then grab a new job and return to the caller. it is very - // unlikely that there are multiple jobs like this at the front of the - // queue, but not impossible. - const decision = await this.pgQueryReadReplica - .selectFrom('manual_review_tool.manual_review_decisions') - .select(['decision_components']) // not really necessary to return anything - .where('created_at', '>=', new Date('2023-10-01')) - .where('org_id', '=', orgId) - .where('id', '=', jobIdToGuid(convertedJob.data.id)) - .executeTakeFirst(); + if (!job) { + return null; + } + if (reviewerSkips.has(job.data.id)) { + heldAside.push(job); + continue; + } - hasDecision = decision !== undefined; + const convertedJob = await this.legacyJobToJob(job, orgId); + + // There is a race condition due to the locking mechanism where a job can + // be decided on but not dequeued, so we check here if the first job in the + // queue has a decision, and if so use the lock token to immediately + // remove it, then grab a new job and return to the caller. it is very + // unlikely that there are multiple jobs like this at the front of the + // queue, but not impossible. + const decision = await this.pgQueryReadReplica + .selectFrom('manual_review_tool.manual_review_decisions') + .select(['decision_components']) // not really necessary to return anything + .where('created_at', '>=', new Date('2023-10-01')) + .where('org_id', '=', orgId) + .where('id', '=', jobIdToGuid(convertedJob.data.id)) + .executeTakeFirst(); - if (hasDecision) { - await this.removeJob({ + if (decision !== undefined) { + await this.removeJob({ + orgId, + queueId, + lockToken, + jobId: convertedJob.data.id, + }).catch((error: unknown) => { + // Non-fatal, but not silent either. The common cause is another + // reviewer having re-locked the job, in which case they will + // retire it and doing nothing is correct. The case that matters is + // a Redis failure: the decided job then stays `active` until its + // lock expires, after which the stalled checker returns it to + // `wait` — forever. `maxStalledCount` does not bound this, because + // BullMQ only applies the deferred stall failure inside + // `processJob`, which never runs on these workers (`autorun: + // false`). So a stuck decided job recirculates indefinitely and + // this report is the only signal it happened. + this.tracer.logActiveSpanFailedIfAny(error); + }); + // then continue while loop + } else { + // this is the most likely case, where there is a job + // and it has never been decided before + return { job: convertedJob.data, lockToken }; + } + } + } finally { + // Release the held-aside jobs so other reviewers can pick them up + // immediately. This reviewer stays excluded via the skip set. + for (const held of heldAside) { + // No `.catch` here on purpose: `releaseJobLock` never rejects — it + // reports its own failures to the active span and returns. Chaining a + // handler here would be dead code. Releasing is best-effort anyway; a + // failure leaves the job locked until `lockDuration` expires, and + // throwing from a `finally` would replace whatever result or error the + // caller was about to receive. + await this.releaseJobLock({ orgId, queueId, + jobId: held.data.id, lockToken, - jobId: convertedJob.data.id, - }).catch(() => {}); - // then continue while loop - } else { - // this is the most likely case, where there is a job - // and it has never been decided before - return { job: convertedJob.data, lockToken }; + }); } } - return null; + } + + static readonly REVIEWER_SKIP_TTL_MS = 30 * 60 * 1000; + + #reviewerSkipKey(orgId: string, queueId: string, reviewerId: string): string { + return `{${orgId}}:mrt-reviewer-skips:${queueId}:${reviewerId}`; + } + + /** + * Hides a job from one reviewer for the skip window and hands it straight + * back to everyone else. + * + * Releasing the lock is part of skipping, not a separate step callers have to + * remember — there is no case for recording a skip while still holding the + * job. `releaseJobLock` is a no-op when no lock is held, so this is safe even + * when the caller never took one. + */ + async recordReviewerSkip(opts: { + orgId: string; + queueId: string; + reviewerId: string; + jobId: string; + }): Promise { + const { orgId, queueId, reviewerId, jobId } = opts; + const key = this.#reviewerSkipKey(orgId, queueId, reviewerId); + const expiresAt = Date.now() + QueueOperations.REVIEWER_SKIP_TTL_MS; + await this.redis.zadd(key, expiresAt, jobId); + // Backstop: the whole set disappears once everything in it has expired. + await this.redis.pexpire(key, QueueOperations.REVIEWER_SKIP_TTL_MS); + + // The lock token is the reviewer's own id. + await this.releaseJobLock({ + orgId, + queueId, + jobId: instantiateOpaqueType(jobId), + lockToken: reviewerId, + }); + } + + async getActiveReviewerSkips(opts: { + orgId: string; + queueId: string; + reviewerId: string; + }): Promise> { + const { orgId, queueId, reviewerId } = opts; + const key = this.#reviewerSkipKey(orgId, queueId, reviewerId); + // Drop expired entries, then read what's still active. + await this.redis.zremrangebyscore(key, 0, Date.now()); + const ids = await this.redis.zrange(key, 0, -1); + return new Set(ids); } /** @@ -1621,9 +1787,14 @@ export default class QueueOperations { // The token parameter ensures only the holder of the lock can release it await job.moveToDelayed(Date.now(), lockToken); } catch (error: unknown) { - // If the lock has already expired or the job is in a different state, - // we can safely ignore the error as the job is already released - // or will be handled by the stalled job checker + // Non-fatal: if the lock has already expired or the job moved state, the + // job is effectively released already (or the stalled checker will get + // it), and callers — including the `releaseJobLock` mutation and the + // held-aside loop in `dequeueNextJobWithLock` — treat releasing as + // best-effort cleanup that must not fail the operation around it. + // Reported rather than swallowed: a persistent failure here keeps a job + // locked for the full `lockDuration`, which is invisible otherwise. + this.tracer.logActiveSpanFailedIfAny(error); } } @@ -1667,12 +1838,14 @@ export default class QueueOperations { ? await this.#getBullAppealQueue(orgId, queueId) : await this.#getBullQueue(orgId, queueId); - // Get the first waiting job and first delayed job - // BullMQ maintains FIFO order within each state, so we only need to compare - // the first job from each state to find the oldest overall + // Get the first waiting job and first delayed job. getWaiting/getDelayed + // return jobs oldest-first, so we only need to compare the first job from + // each state to find the oldest overall. NB: the equivalent + // queue.getJobs([state], 0, 0) defaults to descending order and would + // return the *newest* job instead. const [waitingJobs, delayedJobs] = await Promise.all([ - queue.getJobs(['waiting'], 0, 0), - queue.getJobs(['delayed'], 0, 0), + queue.getWaiting(0, 0), + queue.getDelayed(0, 0), ]); // If no jobs exist in either state, return null @@ -1942,9 +2115,13 @@ export async function getBullWorker( await worker.startStalledCheckTimer(); // Cast worker to a version of its original type, but fixed to correctly - // indicate that getNextJob() can return undefined + // indicate that getNextJob() can return undefined and accepts a `block` + // option (false = return immediately instead of long-polling). return worker as unknown as Omit, 'getNextJob'> & { - getNextJob: (lockToken: string) => Promise | undefined>; + getNextJob: ( + lockToken: string, + opts?: { block?: boolean }, + ) => Promise | undefined>; }; } @@ -2123,3 +2300,19 @@ export const makeManualReviewQueueNameExistsError = (data: ErrorInstanceData) => name: 'ManualReviewQueueNameExistsError', ...data, }); + +export const makeQueueHasDependentRoutingRulesError = ( + ruleNames: string[], + data: ErrorInstanceData, +) => + new CoopError({ + status: 409, + type: [ErrorType.Conflict], + title: + ruleNames.length > 0 + ? `This queue cannot be deleted because it is used by the following routing rules:\n${ruleNames.join(', ')}\nUpdate or delete those rules first.` + : 'This queue cannot be deleted because it is still referenced by one or more routing rules. Update or delete those rules first.', + detail: ruleNames.length > 0 ? jsonStringify(ruleNames) : undefined, + name: 'QueueHasDependentRoutingRulesError', + ...data, + }); diff --git a/server/services/manualReviewToolService/modules/ReporterInvalidation.test.ts b/server/services/manualReviewToolService/modules/ReporterInvalidation.test.ts index 1bc400889..e7a745b74 100644 --- a/server/services/manualReviewToolService/modules/ReporterInvalidation.test.ts +++ b/server/services/manualReviewToolService/modules/ReporterInvalidation.test.ts @@ -1,13 +1,11 @@ -/* eslint-disable max-lines */ import { uid } from 'uid'; import { v1 as uuidv1 } from 'uuid'; -import getBottle from '../../../iocContainer/index.js'; import createContentItemTypes from '../../../test/fixtureHelpers/createContentItemTypes.js'; import createMrtQueue from '../../../test/fixtureHelpers/createMrtQueue.js'; import createOrg from '../../../test/fixtureHelpers/createOrg.js'; import createUser from '../../../test/fixtureHelpers/createUser.js'; -import { makeTestWithFixture } from '../../../test/utils.js'; +import { makeTransactionalTestWithFixture } from '../../../test/harness/transactionalTest.js'; import { instantiateOpaqueType } from '../../../utils/typescript-types.js'; import { makeSubmissionId, @@ -274,33 +272,28 @@ describe('scrubPayloadForReporter (pure)', () => { // Integration tests below mirror the pattern in `QueueOperations.test.ts`. const testWithQueue = () => - makeTestWithFixture(async () => { - const container = (await getBottle()).container; - const { org, cleanup: orgCleanup } = await createOrg( + makeTransactionalTestWithFixture(async ({ deps }) => { + const { org } = await createOrg( { - KyselyPg: container.KyselyPg, - ModerationConfigService: container.ModerationConfigService, - ApiKeyService: container.ApiKeyService, + KyselyPg: deps.KyselyPg, + ModerationConfigService: deps.ModerationConfigService, + ApiKeyService: deps.ApiKeyService, }, uid(), ); - const { user, cleanup: userCleanup } = await createUser( - container.KyselyPg, - org.id, - ); - const { itemTypes, cleanup: itemTypesCleanup } = - await createContentItemTypes({ - moderationConfigService: container.ModerationConfigService, - orgId: org.id, - extra: {}, - }); - const { queue, cleanup: queueCleanup } = await createMrtQueue({ + const { user } = await createUser(deps.KyselyPg, org.id); + const { itemTypes } = await createContentItemTypes({ + moderationConfigService: deps.ModerationConfigService, orgId: org.id, - mrtService: container.ManualReviewToolService, + extra: {}, + }); + const { queue } = await createMrtQueue({ + orgId: org.id, + mrtService: deps.ManualReviewToolService, userId: user.id, }); - const mrtService = container.ManualReviewToolService; + const mrtService = deps.ManualReviewToolService; // Bracket-index into the private QueueOperations to seed jobs directly, // matching the pattern in manualReviewToolService.test.ts. @@ -349,14 +342,6 @@ const testWithQueue = () => queue, mrtService, addJob, - cleanup: async () => { - await queueCleanup(); - await itemTypesCleanup(); - await userCleanup(); - await orgCleanup(); - await container.KyselyPg.destroy(); - await container.KyselyPgReadReplica.destroy(); - }, }; }); diff --git a/server/services/manualReviewToolService/modules/UserReportSweep.test.ts b/server/services/manualReviewToolService/modules/UserReportSweep.test.ts index 0e95cb169..68a8ded10 100644 --- a/server/services/manualReviewToolService/modules/UserReportSweep.test.ts +++ b/server/services/manualReviewToolService/modules/UserReportSweep.test.ts @@ -2,12 +2,11 @@ import { type ItemIdentifier } from '@roostorg/types'; import { uid } from 'uid'; import { v1 as uuidv1 } from 'uuid'; -import getBottle from '../../../iocContainer/index.js'; import createContentItemTypes from '../../../test/fixtureHelpers/createContentItemTypes.js'; import createMrtQueue from '../../../test/fixtureHelpers/createMrtQueue.js'; import createOrg from '../../../test/fixtureHelpers/createOrg.js'; import createUser from '../../../test/fixtureHelpers/createUser.js'; -import { makeTestWithFixture } from '../../../test/utils.js'; +import { makeTransactionalTestWithFixture } from '../../../test/harness/transactionalTest.js'; import { instantiateOpaqueType } from '../../../utils/typescript-types.js'; import { makeSubmissionId, @@ -19,33 +18,28 @@ import { type CustomActionDecisionComponent } from './JobDecisioning.js'; const TRIGGER_ACTION_ID = 'ban-action'; const testWithQueue = () => - makeTestWithFixture(async () => { - const container = (await getBottle()).container; - const { org, cleanup: orgCleanup } = await createOrg( + makeTransactionalTestWithFixture(async ({ deps }) => { + const { org } = await createOrg( { - KyselyPg: container.KyselyPg, - ModerationConfigService: container.ModerationConfigService, - ApiKeyService: container.ApiKeyService, + KyselyPg: deps.KyselyPg, + ModerationConfigService: deps.ModerationConfigService, + ApiKeyService: deps.ApiKeyService, }, uid(), ); - const { user, cleanup: userCleanup } = await createUser( - container.KyselyPg, - org.id, - ); - const { itemTypes, cleanup: itemTypesCleanup } = - await createContentItemTypes({ - moderationConfigService: container.ModerationConfigService, - orgId: org.id, - extra: {}, - }); - const { queue, cleanup: queueCleanup } = await createMrtQueue({ + const { user } = await createUser(deps.KyselyPg, org.id); + const { itemTypes } = await createContentItemTypes({ + moderationConfigService: deps.ModerationConfigService, orgId: org.id, - mrtService: container.ManualReviewToolService, + extra: {}, + }); + const { queue } = await createMrtQueue({ + orgId: org.id, + mrtService: deps.ManualReviewToolService, userId: user.id, }); - const mrtService = container.ManualReviewToolService; + const mrtService = deps.ManualReviewToolService; const queueOps = mrtService['queueOps']; const addJob = async (opts: { @@ -122,14 +116,6 @@ const testWithQueue = () => addJob, configureQueue, pendingJobIds, - cleanup: async () => { - await queueCleanup(); - await itemTypesCleanup(); - await userCleanup(); - await orgCleanup(); - await container.KyselyPg.destroy(); - await container.KyselyPgReadReplica.destroy(); - }, }; }); diff --git a/server/services/moderationConfigService/moderationConfigService.test.ts b/server/services/moderationConfigService/moderationConfigService.test.ts index b425296f5..4447c8e04 100644 --- a/server/services/moderationConfigService/moderationConfigService.test.ts +++ b/server/services/moderationConfigService/moderationConfigService.test.ts @@ -1,4 +1,3 @@ -/* eslint-disable max-lines */ import { faker } from '@faker-js/faker'; import { Kysely } from 'kysely'; import { type UnionToIntersection } from 'type-fest'; @@ -8,6 +7,8 @@ import getBottle from '../../iocContainer/index.js'; import createOrg from '../../test/fixtureHelpers/createOrg.js'; import createRule from '../../test/fixtureHelpers/createRule.js'; import createUser from '../../test/fixtureHelpers/createUser.js'; +import { makeTransactionalTestWithFixture } from '../../test/harness/transactionalTest.js'; +import { type MockedServer } from '../../test/setupMockedServer.js'; import { makeMockPgDialect, type MockPgExecute, @@ -19,215 +20,145 @@ import { type ModerationConfigServicePg } from './dbTypes.js'; import { RuleStatus, RuleType, - type Action, type ConditionSet, - type ItemType, type Policy, - type UserItemType, } from './index.js'; import { ModerationConfigService } from './moderationConfigService.js'; import { PolicyType } from './types/policies.js'; -describe('ModerationConfigService', () => { - let container: Awaited>['container']; - let sutWithPrimary: ModerationConfigService; - let sutWithReadReplica: ModerationConfigService; - let defaultUserItemType: UserItemType; - - // NB: because we don't create a new org for each tests (that feels like - // overkill), we have to track entities added in each write test, by adding - // them to the variables below, so that we can assert on the results when - // reading. - let allCreatedItemTypes = [] as ItemType[]; - const createdItemTypes = { - get ALL() { - return allCreatedItemTypes; - }, - get USER() { - return allCreatedItemTypes.filter((it) => it.kind === 'USER'); - }, - get CONTENT() { - return allCreatedItemTypes.filter((it) => it.kind === 'CONTENT'); - }, - get THREAD() { - return allCreatedItemTypes.filter((it) => it.kind === 'THREAD'); - }, - }; - - let createdActions = [] as Action[]; - - const createdPolicies = [ - { - id: '1', - name: 'Example policy', - orgId: 'orgId', - parentId: 'parentId', - createdAt: new Date(), - updatedAt: new Date(), - semanticVersion: 1, - policyText: '', - policyType: PolicyType.DRUG_SALES, - userStrikeCount: 1, - applyUserStrikeCountConfigToChildren: false, - penalty: 'NONE', - }, - ] satisfies Policy[]; - - const dummyOrgId = uid(); - let dummyOrgCleanup: () => Promise; - const dummySchema = [ - { name: 'fakeField', type: 'STRING', required: false, container: null }, - ] as const; - - // Every time we'll run these tests, we'll generate a new org from scratch, - // and then delete it at the end (which should hopefully do a cascading delete - // of most/all of its relevant data). Testing this way let's us truly test the - // moderationConfigService as a black box -- inserting data only using the - // public methods, and then verifying that we can retrieve it or delete it - // with only the public methods. Any other approach would require our tests to - // memorize exactly what queries the service is issuing and the schema of the - // underlying db tables, which makes the tests more brittle/harder to maintain - // than I'd like if the service is refactored. - beforeAll(async () => { - container = (await getBottle()).container; - - // An instance of kysely that will throw if any queries are run through it; - // used to test that the moderationConfigService is querying the correct db. - const kyselyShouldBeUnused = new Kysely({ - dialect: makeMockPgDialect( - jest.fn().mockImplementation(async () => { - throw new Error('Did not expect this kysely instance to be used!'); - }), - ), - }); - - // In order to test that the correct db is queried (i.e., replicas vs the - // primary), we'll just use different instances of the service, where each - // only has access to the db we expect to be hit. +type TestDeps = MockedServer['deps']; + +type Sut = ConstructorParameters[0]; + +// We test the moderationConfigService as a black box: every test gets a fresh +// org and seeds data only through the public methods, then verifies it can read +// or delete that data with only the public methods. The transactional harness +// rolls everything back after each test, so there's no cleanup and no state +// leaking between tests (and therefore no ordering dependency between them). +// +// To test that the correct db is queried (i.e., replicas vs the primary), we +// use two service instances, each with access only to the db we expect to be +// hit; the other db is a kysely instance that throws if it's ever used. +function makeSuts(primary: Sut, replica: Sut) { + const kyselyShouldBeUnused = new Kysely({ + dialect: makeMockPgDialect( + jest.fn().mockImplementation(async () => { + throw new Error('Did not expect this kysely instance to be used!'); + }), + ), + }); - sutWithPrimary = new ModerationConfigService( - container.KyselyPg, + return { + sutWithPrimary: new ModerationConfigService( + primary, kyselyShouldBeUnused, async () => {}, - ); - - sutWithReadReplica = new ModerationConfigService( + ), + sutWithReadReplica: new ModerationConfigService( kyselyShouldBeUnused, - container.KyselyPgReadReplica, + replica, async () => {}, - ); + ), + }; +} - const createOrgResult = await createOrg( - { - KyselyPg: container.KyselyPg, - ModerationConfigService: container.ModerationConfigService, - ApiKeyService: container.ApiKeyService, - }, - dummyOrgId, - ); +async function setupOrg(deps: TestDeps) { + const suts = makeSuts(deps.KyselyPg, deps.KyselyPgReadReplica); + const { org, defaultUserItemType } = await createOrg( + { + KyselyPg: deps.KyselyPg, + ModerationConfigService: deps.ModerationConfigService, + ApiKeyService: deps.ApiKeyService, + }, + uid(), + ); + return { ...suts, org, defaultUserItemType }; +} + +type SupportsReplicaMethod = Satisfies< + | 'getItemTypes' + | 'getItemType' + | 'getItemTypesByKind' + | 'getDefaultUserType' + | 'getItemTypesForAction' + | 'getItemTypesForRule' + | 'getActions' + | 'getPolicies', + keyof ModerationConfigService +>; + +async function expectReadReplicaUse( + suts: { + sutWithPrimary: ModerationConfigService; + sutWithReadReplica: ModerationConfigService; + }, + method: T, + baseFilter: Parameters[0], +) { + // cast baseFilter to prevent TS errors that would arise because TS can't + // verify that the particular string that `method` takes on at runtime + // corresponsds to the particular binding for baseFilter. + const filters = baseFilter as UnionToIntersection< + Parameters[0] + >; - defaultUserItemType = createOrgResult.defaultUserItemType; - dummyOrgCleanup = createOrgResult.cleanup; - allCreatedItemTypes = [...allCreatedItemTypes, defaultUserItemType]; - }); + // We're calling these to test that none of them throw, which'll only be + // true if the proper db is used. + await suts.sutWithPrimary[method](filters); + await suts.sutWithPrimary[method]({ ...filters, readFromReplica: false }); + await suts.sutWithReadReplica[method]({ ...filters, readFromReplica: true }); +} - afterAll(async () => { - await dummyOrgCleanup(); +const dummySchema = [ + { name: 'fakeField', type: 'STRING', required: false, container: null }, +] as const; - await Promise.all([ - container.KyselyPg.destroy(), - container.KyselyPgReadReplica.destroy(), - ]); - }); +const minimalRuleConditionSet = { + conjunction: 'AND' as const, + conditions: [ + { + input: { type: 'FULL_ITEM' as const }, + comparator: 'IS_NOT_PROVIDED' as const, + }, + ], +} satisfies ConditionSet; - const itemTypeSnapshotMatchers = { - id: expect.any(String), - version: expect.any(String), - orgId: expect.any(String), - }; +const itemTypeSnapshotMatchers = { + id: expect.any(String), + version: expect.any(String), + orgId: expect.any(String), +}; - const actionSnapshotMatchers = { - id: expect.any(String), - orgId: expect.any(String), - }; +const actionSnapshotMatchers = { + id: expect.any(String), + orgId: expect.any(String), +}; - type SupportsReplicaMethod = Satisfies< - | 'getItemTypes' - | 'getItemType' - | 'getItemTypesByKind' - | 'getDefaultUserType' - | 'getItemTypesForAction' - | 'getItemTypesForRule' - | 'getActions' - | 'getPolicies', - keyof ModerationConfigService - >; - - async function testReadReplicaUse( - method: T, - baseFilter: Parameters[0], - ) { - // cast baseFilter to prevent TS errors that would arise because TS can't - // verify that the particular string that `method` takes on at runtime - // corresponsds to the particular binding for baseFilter. - const filters = baseFilter as UnionToIntersection< - Parameters[0] - >; - - // We're calling these to test that none of them throw, which'll only be - // true if the proper db is used. - await sutWithPrimary[method](filters); - await sutWithPrimary[method]({ ...filters, readFromReplica: false }); - await sutWithReadReplica[method]({ ...filters, readFromReplica: true }); - } - - const minimalRuleConditionSet = { - conjunction: 'AND' as const, - conditions: [ - { - input: { type: 'FULL_ITEM' as const }, - comparator: 'IS_NOT_PROVIDED' as const, - }, - ], - } satisfies ConditionSet; +describe('ModerationConfigService', () => { + const testWithOrg = makeTransactionalTestWithFixture(async ({ deps }) => + setupOrg(deps), + ); describe('#getRuleByIdAndOrg', () => { - const testWithRuleRow = makeTestWithFixture(async () => { - const { org, cleanup: orgCleanup } = await createOrg( - { - KyselyPg: container.KyselyPg, - ModerationConfigService: container.ModerationConfigService, - ApiKeyService: container.ApiKeyService, - }, - uid(), - ); - const { user, cleanup: userCleanup } = await createUser( - container.KyselyPg, - org.id, - ); - const rule = await createRule(container.KyselyPg, org.id, { - creator: user, - name: 'getRuleByIdAndOrg fixture rule', - ruleType: RuleType.USER, - status: RuleStatus.DRAFT, - conditionSet: minimalRuleConditionSet, - }); + const testWithRuleRow = makeTransactionalTestWithFixture( + async ({ deps }) => { + const base = await setupOrg(deps); + const { user } = await createUser(deps.KyselyPg, base.org.id); + const rule = await createRule(deps.KyselyPg, base.org.id, { + creator: user, + name: 'getRuleByIdAndOrg fixture rule', + ruleType: RuleType.USER, + status: RuleStatus.DRAFT, + conditionSet: minimalRuleConditionSet, + }); - return { - org, - user, - ruleId: rule.id, - async cleanup() { - await rule.destroy(); - await userCleanup(); - await orgCleanup(); - }, - }; - }); + return { ...base, user, ruleId: rule.id }; + }, + ); testWithRuleRow( 'returns the rule when the org id matches the rule row', - async ({ org, ruleId }) => { + async ({ sutWithPrimary, org, ruleId }) => { const row = await sutWithPrimary.getRuleByIdAndOrg(ruleId, org.id, { readFromReplica: false, }); @@ -239,273 +170,322 @@ describe('ModerationConfigService', () => { testWithRuleRow( 'returns null when the org id does not match (IDOR guard)', - async ({ ruleId }) => { - const { org: otherOrg, cleanup: otherOrgCleanup } = await createOrg( + async ({ sutWithPrimary, deps, ruleId }) => { + const { org: otherOrg } = await createOrg( { - KyselyPg: container.KyselyPg, - ModerationConfigService: container.ModerationConfigService, - ApiKeyService: container.ApiKeyService, + KyselyPg: deps.KyselyPg, + ModerationConfigService: deps.ModerationConfigService, + ApiKeyService: deps.ApiKeyService, }, uid(), ); - try { - const row = await sutWithPrimary.getRuleByIdAndOrg( - ruleId, - otherOrg.id, - { readFromReplica: false }, - ); - expect(row).toBeNull(); - } finally { - await otherOrgCleanup(); - } + const row = await sutWithPrimary.getRuleByIdAndOrg( + ruleId, + otherOrg.id, + { + readFromReplica: false, + }, + ); + expect(row).toBeNull(); }, ); }); - // NB: there is an ordering dependency between these tests, as the creation - // tests run first and then the read tests assert on the presence of their - // writes. describe('ItemType-Returning methods', () => { describe('Creation methods', () => { describe('#createContentType', () => { - it('should return and durably save the new item type', async () => { - const saved = await sutWithPrimary.createContentType(dummyOrgId, { - schema: dummySchema, - description: null, - name: 'Content Item Type', - schemaFieldRoles: { - displayName: 'fakeField', - }, - }); + testWithOrg( + 'should return and durably save the new item type', + async ({ sutWithPrimary, org }) => { + const saved = await sutWithPrimary.createContentType(org.id, { + schema: dummySchema, + description: null, + name: 'Content Item Type', + schemaFieldRoles: { + displayName: 'fakeField', + }, + }); - const fetched = await sutWithPrimary.getItemType({ - orgId: dummyOrgId, - itemTypeSelector: { id: saved.id }, - }); + const fetched = await sutWithPrimary.getItemType({ + orgId: org.id, + itemTypeSelector: { id: saved.id }, + }); - expect(saved).toMatchInlineSnapshot( - itemTypeSnapshotMatchers, - ` - { - "description": null, - "id": Any, - "kind": "CONTENT", - "name": "Content Item Type", - "orgId": Any, - "schema": [ - { - "container": null, - "name": "fakeField", - "required": false, - "type": "STRING", + expect(saved).toMatchInlineSnapshot( + itemTypeSnapshotMatchers, + ` + { + "description": null, + "id": Any, + "kind": "CONTENT", + "name": "Content Item Type", + "orgId": Any, + "schema": [ + { + "container": null, + "name": "fakeField", + "required": false, + "type": "STRING", + }, + ], + "schemaFieldRoles": { + "createdAt": undefined, + "creatorId": undefined, + "displayName": "fakeField", + "ipAddress": undefined, + "isDeleted": undefined, + "parentId": undefined, + "threadId": undefined, }, - ], - "schemaFieldRoles": { - "createdAt": undefined, - "creatorId": undefined, - "displayName": "fakeField", - "ipAddress": undefined, - "isDeleted": undefined, - "parentId": undefined, - "threadId": undefined, - }, - "schemaVariant": "original", - "version": Any, - } - `, - ); - expect(saved.orgId).toBe(dummyOrgId); - expect(saved).toEqual(fetched); - allCreatedItemTypes = [...allCreatedItemTypes, saved]; - }); + "schemaVariant": "original", + "version": Any, + } + `, + ); + expect(saved.orgId).toBe(org.id); + expect(saved).toEqual(fetched); + }, + ); }); describe('#createThreadType', () => { - it('should return and durably save the new item type', async () => { - const saved = await sutWithPrimary.createThreadType(dummyOrgId, { - schema: dummySchema, - description: 'Test description', - name: 'Thread Item Type', - schemaFieldRoles: { - displayName: 'fakeField', - }, - }); + testWithOrg( + 'should return and durably save the new item type', + async ({ sutWithPrimary, org }) => { + const saved = await sutWithPrimary.createThreadType(org.id, { + schema: dummySchema, + description: 'Test description', + name: 'Thread Item Type', + schemaFieldRoles: { + displayName: 'fakeField', + }, + }); - const fetched = await sutWithPrimary.getItemType({ - orgId: dummyOrgId, - itemTypeSelector: { id: saved.id }, - }); + const fetched = await sutWithPrimary.getItemType({ + orgId: org.id, + itemTypeSelector: { id: saved.id }, + }); - expect(saved).toMatchInlineSnapshot( - itemTypeSnapshotMatchers, - ` - { - "description": "Test description", - "id": Any, - "kind": "THREAD", - "name": "Thread Item Type", - "orgId": Any, - "schema": [ - { - "container": null, - "name": "fakeField", - "required": false, - "type": "STRING", + expect(saved).toMatchInlineSnapshot( + itemTypeSnapshotMatchers, + ` + { + "description": "Test description", + "id": Any, + "kind": "THREAD", + "name": "Thread Item Type", + "orgId": Any, + "schema": [ + { + "container": null, + "name": "fakeField", + "required": false, + "type": "STRING", + }, + ], + "schemaFieldRoles": { + "createdAt": undefined, + "creatorId": undefined, + "displayName": "fakeField", + "ipAddress": undefined, + "isDeleted": undefined, }, - ], - "schemaFieldRoles": { - "createdAt": undefined, - "creatorId": undefined, - "displayName": "fakeField", - "ipAddress": undefined, - "isDeleted": undefined, - }, - "schemaVariant": "original", - "version": Any, - } - `, - ); - expect(saved.orgId).toBe(dummyOrgId); - expect(saved).toEqual(fetched); - allCreatedItemTypes = [...allCreatedItemTypes, saved]; - }); + "schemaVariant": "original", + "version": Any, + } + `, + ); + expect(saved.orgId).toBe(org.id); + expect(saved).toEqual(fetched); + }, + ); }); describe('#createUserType', () => { - it('should return and durably save the new item type', async () => { - const saved = await sutWithPrimary.createUserType(dummyOrgId, { - schema: dummySchema, - description: null, - name: 'User Item Type', - schemaFieldRoles: { - displayName: 'fakeField', - }, - }); + testWithOrg( + 'should return and durably save the new item type', + async ({ sutWithPrimary, org }) => { + const saved = await sutWithPrimary.createUserType(org.id, { + schema: dummySchema, + description: null, + name: 'User Item Type', + schemaFieldRoles: { + displayName: 'fakeField', + }, + }); - const fetched = await sutWithPrimary.getItemType({ - orgId: dummyOrgId, - itemTypeSelector: { id: saved.id }, - }); + const fetched = await sutWithPrimary.getItemType({ + orgId: org.id, + itemTypeSelector: { id: saved.id }, + }); - expect(saved).toMatchInlineSnapshot( - itemTypeSnapshotMatchers, - ` - { - "description": null, - "id": Any, - "isDefaultUserType": false, - "kind": "USER", - "name": "User Item Type", - "orgId": Any, - "schema": [ - { - "container": null, - "name": "fakeField", - "required": false, - "type": "STRING", + expect(saved).toMatchInlineSnapshot( + itemTypeSnapshotMatchers, + ` + { + "description": null, + "id": Any, + "isDefaultUserType": false, + "kind": "USER", + "name": "User Item Type", + "orgId": Any, + "schema": [ + { + "container": null, + "name": "fakeField", + "required": false, + "type": "STRING", + }, + ], + "schemaFieldRoles": { + "backgroundImage": undefined, + "createdAt": undefined, + "displayName": "fakeField", + "email": undefined, + "ipAddress": undefined, + "isDeleted": undefined, + "profileIcon": undefined, }, - ], - "schemaFieldRoles": { - "backgroundImage": undefined, - "createdAt": undefined, - "displayName": "fakeField", - "email": undefined, - "ipAddress": undefined, - "isDeleted": undefined, - "profileIcon": undefined, - }, - "schemaVariant": "original", - "version": Any, - } - `, - ); - expect(saved.orgId).toBe(dummyOrgId); - expect(saved).toEqual(fetched); - allCreatedItemTypes = [...allCreatedItemTypes, saved]; - }); + "schemaVariant": "original", + "version": Any, + } + `, + ); + expect(saved.orgId).toBe(org.id); + expect(saved).toEqual(fetched); + }, + ); }); }); describe('Read methods', () => { describe('#getItemTypes', () => { - it('should return all item types, properly formatted', async () => { - const res = await sutWithPrimary.getItemTypes({ orgId: dummyOrgId }); - expect(res).toHaveLength(createdItemTypes.ALL.length); - expect(res).toEqual(expect.arrayContaining(createdItemTypes.ALL)); - }); + testWithOrg( + 'should return all item types, properly formatted', + async ({ sutWithPrimary, org, defaultUserItemType }) => { + const contentType = await sutWithPrimary.createContentType(org.id, { + schema: dummySchema, + description: null, + name: 'Content Item Type', + schemaFieldRoles: { displayName: 'fakeField' }, + }); + const threadType = await sutWithPrimary.createThreadType(org.id, { + schema: dummySchema, + description: null, + name: 'Thread Item Type', + schemaFieldRoles: { displayName: 'fakeField' }, + }); + const userType = await sutWithPrimary.createUserType(org.id, { + schema: dummySchema, + description: null, + name: 'User Item Type', + schemaFieldRoles: { displayName: 'fakeField' }, + }); + + const expected = [ + defaultUserItemType, + contentType, + threadType, + userType, + ]; + const res = await sutWithPrimary.getItemTypes({ orgId: org.id }); + expect(res).toHaveLength(expected.length); + expect(res).toEqual(expect.arrayContaining(expected)); + }, + ); }); describe('#getItemTypesByKind', () => { - it('should filter by kind', async () => { - const [userItemTypes, contentItemTypes, threadItemTypes] = - await Promise.all([ - sutWithPrimary.getItemTypesByKind({ - orgId: dummyOrgId, - kind: 'USER', - }), - sutWithPrimary.getItemTypesByKind({ - orgId: dummyOrgId, - kind: 'CONTENT', - }), - sutWithPrimary.getItemTypesByKind({ - orgId: dummyOrgId, - kind: 'THREAD', - }), - ]); + testWithOrg( + 'should filter by kind', + async ({ sutWithPrimary, org, defaultUserItemType }) => { + const contentType = await sutWithPrimary.createContentType(org.id, { + schema: dummySchema, + description: null, + name: 'Content Item Type', + schemaFieldRoles: { displayName: 'fakeField' }, + }); + const threadType = await sutWithPrimary.createThreadType(org.id, { + schema: dummySchema, + description: null, + name: 'Thread Item Type', + schemaFieldRoles: { displayName: 'fakeField' }, + }); + const userType = await sutWithPrimary.createUserType(org.id, { + schema: dummySchema, + description: null, + name: 'User Item Type', + schemaFieldRoles: { displayName: 'fakeField' }, + }); - expect(userItemTypes).toHaveLength(createdItemTypes.USER.length); - expect(userItemTypes).toEqual( - expect.arrayContaining(createdItemTypes.USER), - ); + const userItemTypes = await sutWithPrimary.getItemTypesByKind({ + orgId: org.id, + kind: 'USER', + }); + const contentItemTypes = await sutWithPrimary.getItemTypesByKind({ + orgId: org.id, + kind: 'CONTENT', + }); + const threadItemTypes = await sutWithPrimary.getItemTypesByKind({ + orgId: org.id, + kind: 'THREAD', + }); - expect(contentItemTypes).toHaveLength( - createdItemTypes.CONTENT.length, - ); - expect(contentItemTypes).toEqual( - expect.arrayContaining(createdItemTypes.CONTENT), - ); + expect(userItemTypes).toHaveLength(2); + expect(userItemTypes).toEqual( + expect.arrayContaining([defaultUserItemType, userType]), + ); - expect(threadItemTypes).toHaveLength(createdItemTypes.THREAD.length); - expect(threadItemTypes).toEqual( - expect.arrayContaining(createdItemTypes.THREAD), - ); - }); + expect(contentItemTypes).toEqual([contentType]); + expect(threadItemTypes).toEqual([threadType]); + }, + ); }); describe('#getDefaultUserType', () => { - it('should return the defualt user type, properly formatted', async () => { - const res = await sutWithPrimary.getDefaultUserType({ - orgId: dummyOrgId, - }); - expect(res).toEqual(defaultUserItemType); - }); + testWithOrg( + 'should return the default user type, properly formatted', + async ({ sutWithPrimary, org, defaultUserItemType }) => { + const res = await sutWithPrimary.getDefaultUserType({ + orgId: org.id, + }); + expect(res).toEqual(defaultUserItemType); + }, + ); }); describe('#getItemTypesForAction', () => { - it('should query from the proper db', async () => { - // These tests will throw if the wrong db is used (see kyselyShouldBeUnused) - await sutWithPrimary.getItemTypesForAction({ - orgId: dummyOrgId, - actionId: 'someId', - directives: { maxAge: 0 }, - }); - await sutWithReadReplica.getItemTypesForAction({ - orgId: dummyOrgId, - actionId: 'someId', - directives: { maxAge: 10 }, - }); - }); + testWithOrg( + 'should query from the proper db', + async ({ sutWithPrimary, sutWithReadReplica, org }) => { + // These tests will throw if the wrong db is used (see kyselyShouldBeUnused) + await sutWithPrimary.getItemTypesForAction({ + orgId: org.id, + actionId: 'someId', + directives: { maxAge: 0 }, + }); + await sutWithReadReplica.getItemTypesForAction({ + orgId: org.id, + actionId: 'someId', + directives: { maxAge: 10 }, + }); + }, + ); it.skip('should return the right results', () => {}); }); describe('#getItemTypesForRule', () => { - it('should query from the proper db', async () => { - await testReadReplicaUse('getItemTypesForRule', { - orgId: dummyOrgId, - ruleId: 'sasts', - }); - }); + testWithOrg( + 'should query from the proper db', + async ({ sutWithPrimary, sutWithReadReplica, org }) => { + await expectReadReplicaUse( + { sutWithPrimary, sutWithReadReplica }, + 'getItemTypesForRule', + { orgId: org.id, ruleId: 'sasts' }, + ); + }, + ); it.skip('should return the right results', () => {}); }); @@ -515,67 +495,63 @@ describe('ModerationConfigService', () => { describe('Action-returning methods', () => { describe('Creation methods', () => { describe('#upsertBuiltInActions', () => { - it('seeds the three built-in (non-CUSTOM_ACTION) rows for the org', async () => { - const all = await sutWithPrimary.getActions({ orgId: dummyOrgId }); - const builtIns = all.filter( - (it) => it.actionType !== 'CUSTOM_ACTION', - ); - const types = builtIns.map((it) => it.actionType).sort(); - expect(types).toEqual( - [ - 'ENQUEUE_AUTHOR_TO_MRT', - 'ENQUEUE_TO_MRT', - 'ENQUEUE_TO_NCMEC', - ].sort(), - ); - for (const action of builtIns) { - expect(action.orgId).toBe(dummyOrgId); - expect(action).not.toHaveProperty('callbackUrl'); - } - }); + testWithOrg( + 'seeds the three built-in (non-CUSTOM_ACTION) rows for the org', + async ({ sutWithPrimary, org }) => { + const all = await sutWithPrimary.getActions({ orgId: org.id }); + const builtIns = all.filter( + (it) => it.actionType !== 'CUSTOM_ACTION', + ); + const types = builtIns.map((it) => it.actionType).sort(); + expect(types).toEqual( + [ + 'ENQUEUE_AUTHOR_TO_MRT', + 'ENQUEUE_TO_MRT', + 'ENQUEUE_TO_NCMEC', + ].sort(), + ); + for (const action of builtIns) { + expect(action.orgId).toBe(org.id); + expect(action).not.toHaveProperty('callbackUrl'); + } + }, + ); - it('is idempotent: calling twice does not create duplicates', async () => { - const before = await sutWithPrimary.getActions({ - orgId: dummyOrgId, - }); - const beforeBuiltIns = before - .filter((it) => it.actionType !== 'CUSTOM_ACTION') - .map((it) => it.id) - .sort(); - await sutWithPrimary.upsertBuiltInActions(dummyOrgId); - const after = await sutWithPrimary.getActions({ - orgId: dummyOrgId, - }); - const afterBuiltIns = after - .filter((it) => it.actionType !== 'CUSTOM_ACTION') - .map((it) => it.id) - .sort(); - expect(afterBuiltIns).toEqual(beforeBuiltIns); - }); + testWithOrg( + 'is idempotent: calling twice does not create duplicates', + async ({ sutWithPrimary, org }) => { + const before = await sutWithPrimary.getActions({ + orgId: org.id, + }); + const beforeBuiltIns = before + .filter((it) => it.actionType !== 'CUSTOM_ACTION') + .map((it) => it.id) + .sort(); + await sutWithPrimary.upsertBuiltInActions(org.id); + const after = await sutWithPrimary.getActions({ + orgId: org.id, + }); + const afterBuiltIns = after + .filter((it) => it.actionType !== 'CUSTOM_ACTION') + .map((it) => it.id) + .sort(); + expect(afterBuiltIns).toEqual(beforeBuiltIns); + }, + ); - it('built-ins surface for the appropriate item type kinds', async () => { - const fresh = await createOrg( - { - KyselyPg: container.KyselyPg, - ModerationConfigService: container.ModerationConfigService, - ApiKeyService: container.ApiKeyService, - }, - uid(), - ); - try { - const contentType = await sutWithPrimary.createContentType( - fresh.org.id, - { - schema: dummySchema, - description: null, - name: faker.random.alphaNumeric(16), - schemaFieldRoles: { displayName: 'fakeField' }, - }, - ); + testWithOrg( + 'built-ins surface for the appropriate item type kinds', + async ({ sutWithPrimary, org, defaultUserItemType }) => { + const contentType = await sutWithPrimary.createContentType(org.id, { + schema: dummySchema, + description: null, + name: faker.random.alphaNumeric(16), + schemaFieldRoles: { displayName: 'fakeField' }, + }); const forUser = await sutWithPrimary.getActionsForItemType({ - orgId: fresh.org.id, - itemTypeId: fresh.defaultUserItemType.id, + orgId: org.id, + itemTypeId: defaultUserItemType.id, itemTypeKind: 'USER', }); expect(forUser.map((it) => it.actionType).sort()).toEqual( @@ -583,7 +559,7 @@ describe('ModerationConfigService', () => { ); const forContent = await sutWithPrimary.getActionsForItemType({ - orgId: fresh.org.id, + orgId: org.id, itemTypeId: contentType.id, itemTypeKind: 'CONTENT', }); @@ -594,95 +570,129 @@ describe('ModerationConfigService', () => { 'ENQUEUE_TO_NCMEC', ].sort(), ); - } finally { - await fresh.cleanup(); - } - }); + }, + ); }); describe('#createAction', () => { - it('should return and durably save the new action', async () => { - const saved = await sutWithPrimary.createAction(dummyOrgId, { - name: 'Test Action', - description: 'Test description', - type: 'CUSTOM_ACTION', - callbackUrl: 'https://example.com', - callbackUrlHeaders: null, - callbackUrlBody: null, - applyUserStrikes: false, - }); + testWithOrg( + 'should return and durably save the new action', + async ({ sutWithPrimary, org }) => { + const saved = await sutWithPrimary.createAction(org.id, { + name: 'Test Action', + description: 'Test description', + type: 'CUSTOM_ACTION', + callbackUrl: 'https://example.com', + callbackUrlHeaders: null, + callbackUrlBody: null, + applyUserStrikes: false, + }); - const [fetched] = await sutWithPrimary.getActions({ - orgId: dummyOrgId, - ids: [saved.id], - }); + const [fetched] = await sutWithPrimary.getActions({ + orgId: org.id, + ids: [saved.id], + }); - expect(saved).toMatchInlineSnapshot( - actionSnapshotMatchers, - ` - { - "actionType": "CUSTOM_ACTION", - "applyUserStrikes": false, - "callbackUrl": "https://example.com", - "callbackUrlBody": null, - "callbackUrlHeaders": null, - "customMrtApiParams": null, - "description": "Test description", - "id": Any, - "name": "Test Action", - "orgId": Any, - "penalty": "NONE", - } - `, - ); - expect(saved.orgId).toBe(dummyOrgId); - expect(saved).toEqual(fetched); - createdActions = [...createdActions, saved]; - }); + expect(saved).toMatchInlineSnapshot( + actionSnapshotMatchers, + ` + { + "actionType": "CUSTOM_ACTION", + "applyUserStrikes": false, + "callbackUrl": "https://example.com", + "callbackUrlBody": null, + "callbackUrlHeaders": null, + "customMrtApiParams": null, + "description": "Test description", + "id": Any, + "name": "Test Action", + "orgId": Any, + "penalty": "NONE", + } + `, + ); + expect(saved.orgId).toBe(org.id); + expect(saved).toEqual(fetched); + }, + ); }); }); describe('Read methods', () => { describe('#getActions', () => { - it('should query from the proper db', async () => { - await testReadReplicaUse('getActions', { orgId: dummyOrgId }); - }); + testWithOrg( + 'should query from the proper db', + async ({ sutWithPrimary, sutWithReadReplica, org }) => { + await expectReadReplicaUse( + { sutWithPrimary, sutWithReadReplica }, + 'getActions', + { orgId: org.id }, + ); + }, + ); - it('should return all actions, properly formatted', async () => { - const res = await sutWithPrimary.getActions({ orgId: dummyOrgId }); - const customActions = res.filter( - (it) => it.actionType === 'CUSTOM_ACTION', - ); - expect(customActions).toHaveLength(createdActions.length); - expect(customActions).toEqual(expect.arrayContaining(createdActions)); - }); + testWithOrg( + 'should return all custom actions, properly formatted', + async ({ sutWithPrimary, org }) => { + const createdActions = [ + await sutWithPrimary.createAction(org.id, { + name: faker.random.alphaNumeric(16), + description: 'Test description', + type: 'CUSTOM_ACTION', + callbackUrl: 'https://example.com', + callbackUrlHeaders: null, + callbackUrlBody: null, + applyUserStrikes: false, + }), + await sutWithPrimary.createAction(org.id, { + name: faker.random.alphaNumeric(16), + description: null, + type: 'CUSTOM_ACTION', + callbackUrl: 'https://example.com', + callbackUrlHeaders: null, + callbackUrlBody: null, + applyUserStrikes: false, + }), + ]; - it('should round-trip a non-null customMrtApiParams value', async () => { - const action = await sutWithPrimary.createAction(dummyOrgId, { - name: faker.random.alphaNumeric(16), - description: null, - type: 'CUSTOM_ACTION', - callbackUrl: 'https://example.com', - callbackUrlHeaders: null, - callbackUrlBody: null, - }); + const res = await sutWithPrimary.getActions({ orgId: org.id }); + const customActions = res.filter( + (it) => it.actionType === 'CUSTOM_ACTION', + ); + expect(customActions).toHaveLength(createdActions.length); + expect(customActions).toEqual( + expect.arrayContaining(createdActions), + ); + }, + ); - // Legacy shape pre-dating the typed parameter spec — set it via raw - // Kysely to verify the read mapping still surfaces older rows - // unchanged for back-compat. - const params = [ - { key: 'foo', value: 'bar' }, - { key: 'baz', value: 'qux' }, - ]; - await container.KyselyPg.updateTable('public.actions') - .set({ custom_mrt_api_params: params }) - .where('id', '=', action.id) - .where('org_id', '=', dummyOrgId) - .execute(); + testWithOrg( + 'should round-trip a non-null customMrtApiParams value', + async ({ sutWithPrimary, deps, org }) => { + const action = await sutWithPrimary.createAction(org.id, { + name: faker.random.alphaNumeric(16), + description: null, + type: 'CUSTOM_ACTION', + callbackUrl: 'https://example.com', + callbackUrlHeaders: null, + callbackUrlBody: null, + }); + + // Legacy shape pre-dating the typed parameter spec — set it via raw + // Kysely to verify the read mapping still surfaces older rows + // unchanged for back-compat. + const params = [ + { key: 'foo', value: 'bar' }, + { key: 'baz', value: 'qux' }, + ]; + await deps.KyselyPg.updateTable('public.actions') + .set({ custom_mrt_api_params: params }) + .where('id', '=', action.id) + .where('org_id', '=', org.id) + .execute(); - try { const [fetched] = await sutWithPrimary.getActions({ - orgId: dummyOrgId, + orgId: org.id, ids: [action.id], }); expect(fetched).toBeDefined(); @@ -691,121 +701,110 @@ describe('ModerationConfigService', () => { expect( (fetched as { customMrtApiParams: unknown }).customMrtApiParams, ).toEqual(params); - } finally { - await sutWithPrimary.deleteCustomAction({ - orgId: dummyOrgId, - actionId: action.id, - }); - } - }); + }, + ); - it('round-trips typed parameters through createAction', async () => { - const parameters = [ - { - name: 'num_days_banned', - displayName: 'Days to ban', - type: 'NUMBER', - required: true, - min: 1, - max: 365, - defaultValue: 7, - }, - { - name: 'reason', - displayName: 'Reason', - type: 'SELECT', - required: true, - options: [ - { value: 'spam', label: 'Spam' }, - { value: 'abuse', label: 'Abuse' }, - ], - }, - { - name: 'notify_user', - displayName: 'Notify user', - type: 'BOOLEAN', - required: false, - defaultValue: false, - }, - ]; + testWithOrg( + 'round-trips typed parameters through createAction', + async ({ sutWithPrimary, org }) => { + const parameters = [ + { + name: 'num_days_banned', + displayName: 'Days to ban', + type: 'NUMBER', + required: true, + min: 1, + max: 365, + defaultValue: 7, + }, + { + name: 'reason', + displayName: 'Reason', + type: 'SELECT', + required: true, + options: [ + { value: 'spam', label: 'Spam' }, + { value: 'abuse', label: 'Abuse' }, + ], + }, + { + name: 'notify_user', + displayName: 'Notify user', + type: 'BOOLEAN', + required: false, + defaultValue: false, + }, + ]; - const created = await sutWithPrimary.createAction(dummyOrgId, { - name: faker.random.alphaNumeric(16), - description: null, - type: 'CUSTOM_ACTION', - callbackUrl: 'https://example.com', - callbackUrlHeaders: null, - callbackUrlBody: null, - parameters, - }); + const created = await sutWithPrimary.createAction(org.id, { + name: faker.random.alphaNumeric(16), + description: null, + type: 'CUSTOM_ACTION', + callbackUrl: 'https://example.com', + callbackUrlHeaders: null, + callbackUrlBody: null, + parameters, + }); - try { const [fetched] = await sutWithPrimary.getActions({ - orgId: dummyOrgId, + orgId: org.id, ids: [created.id], }); expect(fetched.actionType).toBe('CUSTOM_ACTION'); const stored = (fetched as { customMrtApiParams: unknown }) .customMrtApiParams; expect(stored).toEqual(parameters); - } finally { - await sutWithPrimary.deleteCustomAction({ - orgId: dummyOrgId, - actionId: created.id, - }); - } - }); + }, + ); - it('rejects invalid parameters at create time', async () => { - await expect( - sutWithPrimary.createAction(dummyOrgId, { - name: faker.random.alphaNumeric(16), - description: null, - type: 'CUSTOM_ACTION', - callbackUrl: 'https://example.com', - callbackUrlHeaders: null, - callbackUrlBody: null, - parameters: [ - { - name: 'invalid name with spaces', - displayName: 'X', - type: 'STRING', - required: false, - }, - ], - }), - ).rejects.toMatchObject({ status: 400 }); - }); + testWithOrg( + 'rejects invalid parameters at create time', + async ({ sutWithPrimary, org }) => { + await expect( + sutWithPrimary.createAction(org.id, { + name: faker.random.alphaNumeric(16), + description: null, + type: 'CUSTOM_ACTION', + callbackUrl: 'https://example.com', + callbackUrlHeaders: null, + callbackUrlBody: null, + parameters: [ + { + name: 'invalid name with spaces', + displayName: 'X', + type: 'STRING', + required: false, + }, + ], + }), + ).rejects.toMatchObject({ status: 400 }); + }, + ); }); }); describe('Update methods', () => { describe('#updateCustomAction', () => { - const testWithAction = makeTestWithFixture(async () => { - const action = await sutWithPrimary.createAction(dummyOrgId, { - name: faker.random.alphaNumeric(16), - description: 'before', - type: 'CUSTOM_ACTION', - callbackUrl: 'https://before.example.com', - callbackUrlHeaders: null, - callbackUrlBody: null, - applyUserStrikes: false, - }); - return { - action, - async cleanup() { - await sutWithPrimary.deleteCustomAction({ - orgId: dummyOrgId, - actionId: action.id, - }); - }, - }; - }); + const testWithAction = makeTransactionalTestWithFixture( + async ({ deps }) => { + const base = await setupOrg(deps); + const action = await base.sutWithPrimary.createAction(base.org.id, { + name: faker.random.alphaNumeric(16), + description: 'before', + type: 'CUSTOM_ACTION', + callbackUrl: 'https://before.example.com', + callbackUrlHeaders: null, + callbackUrlBody: null, + applyUserStrikes: false, + }); + return { ...base, action }; + }, + ); testWithAction( 'should update user-editable fields and bump updated_at', - async ({ action }) => { - const before = await container.KyselyPg.selectFrom('public.actions') + async ({ sutWithPrimary, deps, org, action }) => { + const before = await deps.KyselyPg.selectFrom('public.actions') .select(['updated_at']) .where('id', '=', action.id) .executeTakeFirstOrThrow(); @@ -813,24 +812,21 @@ describe('ModerationConfigService', () => { // Wait briefly so updated_at can advance even on fast clocks. await new Promise((resolve) => setTimeout(resolve, 5)); - const updated = await sutWithPrimary.updateCustomAction( - dummyOrgId, - { - actionId: action.id, - patch: { - description: 'after', - callbackUrl: 'https://after.example.com', - applyUserStrikes: true, - }, + const updated = await sutWithPrimary.updateCustomAction(org.id, { + actionId: action.id, + patch: { + description: 'after', + callbackUrl: 'https://after.example.com', + applyUserStrikes: true, }, - ); + }); expect(updated.actionType).toBe('CUSTOM_ACTION'); expect(updated.description).toBe('after'); expect(updated.callbackUrl).toBe('https://after.example.com'); expect(updated.applyUserStrikes).toBe(true); - const after = await container.KyselyPg.selectFrom('public.actions') + const after = await deps.KyselyPg.selectFrom('public.actions') .select(['updated_at', 'description']) .where('id', '=', action.id) .executeTakeFirstOrThrow(); @@ -843,20 +839,20 @@ describe('ModerationConfigService', () => { testWithAction( 'should not bump updated_at for an empty patch with no itemTypeIds', - async ({ action }) => { - const before = await container.KyselyPg.selectFrom('public.actions') + async ({ sutWithPrimary, deps, org, action }) => { + const before = await deps.KyselyPg.selectFrom('public.actions') .select(['updated_at']) .where('id', '=', action.id) .executeTakeFirstOrThrow(); await new Promise((resolve) => setTimeout(resolve, 5)); - const result = await sutWithPrimary.updateCustomAction(dummyOrgId, { + const result = await sutWithPrimary.updateCustomAction(org.id, { actionId: action.id, patch: {}, }); - const after = await container.KyselyPg.selectFrom('public.actions') + const after = await deps.KyselyPg.selectFrom('public.actions') .select(['updated_at']) .where('id', '=', action.id) .executeTakeFirstOrThrow(); @@ -869,41 +865,37 @@ describe('ModerationConfigService', () => { testWithAction( 'should throw NotFound when called with the wrong org', - async ({ action }) => { - const otherOrg = await createOrg( + async ({ sutWithPrimary, deps, action }) => { + const { org: otherOrg } = await createOrg( { - KyselyPg: container.KyselyPg, - ModerationConfigService: container.ModerationConfigService, - ApiKeyService: container.ApiKeyService, + KyselyPg: deps.KyselyPg, + ModerationConfigService: deps.ModerationConfigService, + ApiKeyService: deps.ApiKeyService, }, uid(), ); - try { - await expect( - sutWithPrimary.updateCustomAction(otherOrg.org.id, { - actionId: action.id, - patch: { description: 'leaked' }, - }), - ).rejects.toThrow( - expect.objectContaining({ type: [ErrorType.NotFound] }), - ); - - // The action's row in the original org must be untouched. - const row = await container.KyselyPg.selectFrom('public.actions') - .select(['description']) - .where('id', '=', action.id) - .executeTakeFirstOrThrow(); - expect(row.description).toBe('before'); - } finally { - await otherOrg.cleanup(); - } + await expect( + sutWithPrimary.updateCustomAction(otherOrg.id, { + actionId: action.id, + patch: { description: 'leaked' }, + }), + ).rejects.toThrow( + expect.objectContaining({ type: [ErrorType.NotFound] }), + ); + + // The action's row in the original org must be untouched. + const row = await deps.KyselyPg.selectFrom('public.actions') + .select(['description']) + .where('id', '=', action.id) + .executeTakeFirstOrThrow(); + expect(row.description).toBe('before'); }, ); testWithAction( 'updates parameters when patch.parameters is supplied', - async ({ action }) => { - await sutWithPrimary.updateCustomAction(dummyOrgId, { + async ({ sutWithPrimary, org, action }) => { + await sutWithPrimary.updateCustomAction(org.id, { actionId: action.id, patch: { parameters: [ @@ -918,7 +910,7 @@ describe('ModerationConfigService', () => { }); const [afterSet] = await sutWithPrimary.getActions({ - orgId: dummyOrgId, + orgId: org.id, ids: [action.id], }); expect( @@ -933,12 +925,12 @@ describe('ModerationConfigService', () => { ]); // Passing `[]` should clear, not leave the existing list in place. - await sutWithPrimary.updateCustomAction(dummyOrgId, { + await sutWithPrimary.updateCustomAction(org.id, { actionId: action.id, patch: { parameters: [] }, }); const [afterClear] = await sutWithPrimary.getActions({ - orgId: dummyOrgId, + orgId: org.id, ids: [action.id], }); expect( @@ -950,8 +942,8 @@ describe('ModerationConfigService', () => { testWithAction( 'leaves parameters unchanged when patch.parameters is omitted', - async ({ action }) => { - await sutWithPrimary.updateCustomAction(dummyOrgId, { + async ({ sutWithPrimary, org, action }) => { + await sutWithPrimary.updateCustomAction(org.id, { actionId: action.id, patch: { parameters: [ @@ -964,12 +956,12 @@ describe('ModerationConfigService', () => { ], }, }); - await sutWithPrimary.updateCustomAction(dummyOrgId, { + await sutWithPrimary.updateCustomAction(org.id, { actionId: action.id, patch: { description: 'after' }, }); const [fetched] = await sutWithPrimary.getActions({ - orgId: dummyOrgId, + orgId: org.id, ids: [action.id], }); expect(fetched.description).toBe('after'); @@ -988,8 +980,8 @@ describe('ModerationConfigService', () => { testWithAction( 'should reject renaming onto an existing action name', - async ({ action }) => { - const other = await sutWithPrimary.createAction(dummyOrgId, { + async ({ sutWithPrimary, org, action }) => { + const other = await sutWithPrimary.createAction(org.id, { name: faker.random.alphaNumeric(16), description: null, type: 'CUSTOM_ACTION', @@ -997,100 +989,70 @@ describe('ModerationConfigService', () => { callbackUrlHeaders: null, callbackUrlBody: null, }); - try { - await expect( - sutWithPrimary.updateCustomAction(dummyOrgId, { - actionId: action.id, - patch: { name: other.name }, - }), - ).rejects.toThrow( - expect.objectContaining({ - type: [ErrorType.UniqueViolation], - }), - ); - } finally { - await sutWithPrimary.deleteCustomAction({ - orgId: dummyOrgId, - actionId: other.id, - }); - } + await expect( + sutWithPrimary.updateCustomAction(org.id, { + actionId: action.id, + patch: { name: other.name }, + }), + ).rejects.toThrow( + expect.objectContaining({ + type: [ErrorType.UniqueViolation], + }), + ); }, ); testWithAction( 'should replace the item-type junction when itemTypeIds is provided', - async ({ action }) => { - const itemTypeA = await sutWithPrimary.createContentType( - dummyOrgId, - { - schema: dummySchema, - description: null, - name: faker.random.alphaNumeric(16), - schemaFieldRoles: { displayName: 'fakeField' }, - }, - ); - const itemTypeB = await sutWithPrimary.createContentType( - dummyOrgId, - { - schema: dummySchema, - description: null, - name: faker.random.alphaNumeric(16), - schemaFieldRoles: { displayName: 'fakeField' }, - }, - ); + async ({ sutWithPrimary, deps, org, action }) => { + const itemTypeA = await sutWithPrimary.createContentType(org.id, { + schema: dummySchema, + description: null, + name: faker.random.alphaNumeric(16), + schemaFieldRoles: { displayName: 'fakeField' }, + }); + const itemTypeB = await sutWithPrimary.createContentType(org.id, { + schema: dummySchema, + description: null, + name: faker.random.alphaNumeric(16), + schemaFieldRoles: { displayName: 'fakeField' }, + }); - try { - await sutWithPrimary.updateCustomAction(dummyOrgId, { - actionId: action.id, - patch: {}, - itemTypeIds: [itemTypeA.id], - }); - expect( - await container.KyselyPg.selectFrom( - 'public.actions_and_item_types', - ) - .select(['item_type_id']) - .where('action_id', '=', action.id) - .execute(), - ).toEqual([{ item_type_id: itemTypeA.id }]); - - await sutWithPrimary.updateCustomAction(dummyOrgId, { - actionId: action.id, - patch: {}, - itemTypeIds: [itemTypeB.id], - }); - expect( - await container.KyselyPg.selectFrom( - 'public.actions_and_item_types', - ) - .select(['item_type_id']) - .where('action_id', '=', action.id) - .execute(), - ).toEqual([{ item_type_id: itemTypeB.id }]); - - await sutWithPrimary.updateCustomAction(dummyOrgId, { - actionId: action.id, - patch: {}, - itemTypeIds: [], - }); - expect( - await container.KyselyPg.selectFrom( - 'public.actions_and_item_types', - ) - .select(['item_type_id']) - .where('action_id', '=', action.id) - .execute(), - ).toEqual([]); - } finally { - await sutWithPrimary.deleteItemType({ - orgId: dummyOrgId, - itemTypeId: itemTypeA.id, - }); - await sutWithPrimary.deleteItemType({ - orgId: dummyOrgId, - itemTypeId: itemTypeB.id, - }); - } + await sutWithPrimary.updateCustomAction(org.id, { + actionId: action.id, + patch: {}, + itemTypeIds: [itemTypeA.id], + }); + expect( + await deps.KyselyPg.selectFrom('public.actions_and_item_types') + .select(['item_type_id']) + .where('action_id', '=', action.id) + .execute(), + ).toEqual([{ item_type_id: itemTypeA.id }]); + + await sutWithPrimary.updateCustomAction(org.id, { + actionId: action.id, + patch: {}, + itemTypeIds: [itemTypeB.id], + }); + expect( + await deps.KyselyPg.selectFrom('public.actions_and_item_types') + .select(['item_type_id']) + .where('action_id', '=', action.id) + .execute(), + ).toEqual([{ item_type_id: itemTypeB.id }]); + + await sutWithPrimary.updateCustomAction(org.id, { + actionId: action.id, + patch: {}, + itemTypeIds: [], + }); + expect( + await deps.KyselyPg.selectFrom('public.actions_and_item_types') + .select(['item_type_id']) + .where('action_id', '=', action.id) + .execute(), + ).toEqual([]); }, ); }); @@ -1098,148 +1060,127 @@ describe('ModerationConfigService', () => { describe('Delete methods', () => { describe('#deleteCustomAction', () => { - const testWithAction = makeTestWithFixture(async () => { - const action = await sutWithPrimary.createAction(dummyOrgId, { - name: faker.random.alphaNumeric(16), - description: null, - type: 'CUSTOM_ACTION', - callbackUrl: 'https://example.com', - callbackUrlHeaders: null, - callbackUrlBody: null, - }); - return { - action, - // Best-effort cleanup; the test under assertion may have already - // removed the row. - async cleanup() { - await sutWithPrimary - .deleteCustomAction({ - orgId: dummyOrgId, - actionId: action.id, - }) - .catch(() => {}); - }, - }; - }); + const testWithAction = makeTransactionalTestWithFixture( + async ({ deps }) => { + const base = await setupOrg(deps); + const action = await base.sutWithPrimary.createAction(base.org.id, { + name: faker.random.alphaNumeric(16), + description: null, + type: 'CUSTOM_ACTION', + callbackUrl: 'https://example.com', + callbackUrlHeaders: null, + callbackUrlBody: null, + }); + return { ...base, action }; + }, + ); testWithAction( 'should return true and delete the action on success', - async ({ action }) => { + async ({ sutWithPrimary, org, action }) => { const result = await sutWithPrimary.deleteCustomAction({ - orgId: dummyOrgId, + orgId: org.id, actionId: action.id, }); expect(result).toBe(true); expect( await sutWithPrimary.getActions({ - orgId: dummyOrgId, + orgId: org.id, ids: [action.id], }), ).toEqual([]); }, ); - it('should return false when the action does not exist', async () => { - const result = await sutWithPrimary.deleteCustomAction({ - orgId: dummyOrgId, - actionId: uid(), - }); - expect(result).toBe(false); - }); + testWithOrg( + 'should return false when the action does not exist', + async ({ sutWithPrimary, org }) => { + const result = await sutWithPrimary.deleteCustomAction({ + orgId: org.id, + actionId: uid(), + }); + expect(result).toBe(false); + }, + ); testWithAction( 'should return false when called with the wrong org and leave the row intact', - async ({ action }) => { - const otherOrg = await createOrg( + async ({ sutWithPrimary, deps, org, action }) => { + const { org: otherOrg } = await createOrg( { - KyselyPg: container.KyselyPg, - ModerationConfigService: container.ModerationConfigService, - ApiKeyService: container.ApiKeyService, + KyselyPg: deps.KyselyPg, + ModerationConfigService: deps.ModerationConfigService, + ApiKeyService: deps.ApiKeyService, }, uid(), ); - try { - const result = await sutWithPrimary.deleteCustomAction({ - orgId: otherOrg.org.id, - actionId: action.id, - }); - expect(result).toBe(false); - const [stillThere] = await sutWithPrimary.getActions({ - orgId: dummyOrgId, - ids: [action.id], - }); - expect(stillThere.id).toBe(action.id); - } finally { - await otherOrg.cleanup(); - } + const result = await sutWithPrimary.deleteCustomAction({ + orgId: otherOrg.id, + actionId: action.id, + }); + expect(result).toBe(false); + const [stillThere] = await sutWithPrimary.getActions({ + orgId: org.id, + ids: [action.id], + }); + expect(stillThere.id).toBe(action.id); }, ); testWithAction( 'should clean up rules_and_actions and actions_and_item_types junction rows', - async ({ action }) => { - const itemType = await sutWithPrimary.createContentType( - dummyOrgId, - { - schema: dummySchema, - description: null, - name: faker.random.alphaNumeric(16), - schemaFieldRoles: { displayName: 'fakeField' }, - }, - ); - const rule = await createRule(container.KyselyPg, dummyOrgId); + async ({ sutWithPrimary, deps, org, action }) => { + const itemType = await sutWithPrimary.createContentType(org.id, { + schema: dummySchema, + description: null, + name: faker.random.alphaNumeric(16), + schemaFieldRoles: { displayName: 'fakeField' }, + }); + const rule = await createRule(deps.KyselyPg, org.id); - await container.KyselyPg.insertInto('public.actions_and_item_types') + await deps.KyselyPg.insertInto('public.actions_and_item_types') .values({ action_id: action.id, item_type_id: itemType.id }) .execute(); - await container.KyselyPg.insertInto('public.rules_and_actions') + await deps.KyselyPg.insertInto('public.rules_and_actions') .values({ action_id: action.id, rule_id: rule.id }) .execute(); - try { - const result = await sutWithPrimary.deleteCustomAction({ - orgId: dummyOrgId, - actionId: action.id, - }); - expect(result).toBe(true); - expect( - await container.KyselyPg.selectFrom( - 'public.actions_and_item_types', - ) - .select(['action_id']) - .where('action_id', '=', action.id) - .execute(), - ).toEqual([]); - expect( - await container.KyselyPg.selectFrom('public.rules_and_actions') - .select(['action_id']) - .where('action_id', '=', action.id) - .execute(), - ).toEqual([]); - } finally { - await rule.destroy(); - await sutWithPrimary.deleteItemType({ - orgId: dummyOrgId, - itemTypeId: itemType.id, - }); - } + const result = await sutWithPrimary.deleteCustomAction({ + orgId: org.id, + actionId: action.id, + }); + expect(result).toBe(true); + expect( + await deps.KyselyPg.selectFrom('public.actions_and_item_types') + .select(['action_id']) + .where('action_id', '=', action.id) + .execute(), + ).toEqual([]); + expect( + await deps.KyselyPg.selectFrom('public.rules_and_actions') + .select(['action_id']) + .where('action_id', '=', action.id) + .execute(), + ).toEqual([]); }, ); }); }); describe('#getActionsForItemType', () => { - const testWithItemTypeAndActions = makeTestWithFixture(async () => { - const itemType = await sutWithPrimary.createContentType(dummyOrgId, { - schema: dummySchema, - description: null, - name: faker.random.alphaNumeric(16), - schemaFieldRoles: { displayName: 'fakeField' }, - }); + const testWithItemTypeAndActions = makeTransactionalTestWithFixture( + async ({ deps }) => { + const base = await setupOrg(deps); + const { sutWithPrimary, org } = base; - const viaJunctionAction = await sutWithPrimary.createAction( - dummyOrgId, - { + const itemType = await sutWithPrimary.createContentType(org.id, { + schema: dummySchema, + description: null, + name: faker.random.alphaNumeric(16), + schemaFieldRoles: { displayName: 'fakeField' }, + }); + + const viaJunctionAction = await sutWithPrimary.createAction(org.id, { name: faker.random.alphaNumeric(16), description: null, type: 'CUSTOM_ACTION', @@ -1247,76 +1188,62 @@ describe('ModerationConfigService', () => { callbackUrlHeaders: null, callbackUrlBody: null, itemTypeIds: [itemType.id], - }, - ); + }); - const viaAppliesAllAction = await sutWithPrimary.createAction( - dummyOrgId, - { + const viaAppliesAllAction = await sutWithPrimary.createAction( + org.id, + { + name: faker.random.alphaNumeric(16), + description: null, + type: 'CUSTOM_ACTION', + callbackUrl: 'https://example.com', + callbackUrlHeaders: null, + callbackUrlBody: null, + }, + ); + await deps.KyselyPg.updateTable('public.actions') + .set({ applies_to_all_items_of_kind: ['CONTENT'] }) + .where('id', '=', viaAppliesAllAction.id) + .execute(); + + // Action satisfying both branches; result should still include it once. + const viaBothAction = await sutWithPrimary.createAction(org.id, { name: faker.random.alphaNumeric(16), description: null, type: 'CUSTOM_ACTION', callbackUrl: 'https://example.com', callbackUrlHeaders: null, callbackUrlBody: null, - }, - ); - await container.KyselyPg.updateTable('public.actions') - .set({ applies_to_all_items_of_kind: ['CONTENT'] }) - .where('id', '=', viaAppliesAllAction.id) - .execute(); - - // Action satisfying both branches; result should still include it once. - const viaBothAction = await sutWithPrimary.createAction(dummyOrgId, { - name: faker.random.alphaNumeric(16), - description: null, - type: 'CUSTOM_ACTION', - callbackUrl: 'https://example.com', - callbackUrlHeaders: null, - callbackUrlBody: null, - itemTypeIds: [itemType.id], - }); - await container.KyselyPg.updateTable('public.actions') - .set({ applies_to_all_items_of_kind: ['CONTENT'] }) - .where('id', '=', viaBothAction.id) - .execute(); + itemTypeIds: [itemType.id], + }); + await deps.KyselyPg.updateTable('public.actions') + .set({ applies_to_all_items_of_kind: ['CONTENT'] }) + .where('id', '=', viaBothAction.id) + .execute(); - return { - itemType, - viaJunctionAction, - viaAppliesAllAction, - viaBothAction, - async cleanup() { - await Promise.all( - [ - viaJunctionAction.id, - viaAppliesAllAction.id, - viaBothAction.id, - ].map(async (id) => - sutWithPrimary.deleteCustomAction({ - orgId: dummyOrgId, - actionId: id, - }), - ), - ); - await sutWithPrimary.deleteItemType({ - orgId: dummyOrgId, - itemTypeId: itemType.id, - }); - }, - }; - }); + return { + ...base, + itemType, + viaJunctionAction, + viaAppliesAllAction, + viaBothAction, + }; + }, + ); testWithItemTypeAndActions( 'should return actions from both branches, deduped, scoped to the org', async ({ + sutWithPrimary, + deps, + org, itemType, viaJunctionAction, viaAppliesAllAction, viaBothAction, }) => { const result = await sutWithPrimary.getActionsForItemType({ - orgId: dummyOrgId, + orgId: org.id, itemTypeId: itemType.id, itemTypeKind: 'CONTENT', readFromReplica: false, @@ -1337,63 +1264,54 @@ describe('ModerationConfigService', () => { // Calling with a different org should never surface this org's // applies-to-all rows (they'd otherwise leak across orgs since the // ANY(...) predicate alone has no tenant scope). - const otherOrg = await createOrg( + const { org: otherOrg } = await createOrg( { - KyselyPg: container.KyselyPg, - ModerationConfigService: container.ModerationConfigService, - ApiKeyService: container.ApiKeyService, + KyselyPg: deps.KyselyPg, + ModerationConfigService: deps.ModerationConfigService, + ApiKeyService: deps.ApiKeyService, }, uid(), ); - try { - const otherResult = await sutWithPrimary.getActionsForItemType({ - orgId: otherOrg.org.id, - itemTypeId: itemType.id, - itemTypeKind: 'CONTENT', - readFromReplica: false, - }); - expect( - otherResult.filter((it) => it.actionType === 'CUSTOM_ACTION'), - ).toEqual([]); - } finally { - await otherOrg.cleanup(); - } + const otherResult = await sutWithPrimary.getActionsForItemType({ + orgId: otherOrg.id, + itemTypeId: itemType.id, + itemTypeKind: 'CONTENT', + readFromReplica: false, + }); + expect( + otherResult.filter((it) => it.actionType === 'CUSTOM_ACTION'), + ).toEqual([]); }, ); }); describe('#getActionsForRuleId', () => { - const testWithRuleAndAction = makeTestWithFixture(async () => { - const rule = await createRule(container.KyselyPg, dummyOrgId); - const action = await sutWithPrimary.createAction(dummyOrgId, { - name: faker.random.alphaNumeric(16), - description: null, - type: 'CUSTOM_ACTION', - callbackUrl: 'https://example.com', - callbackUrlHeaders: null, - callbackUrlBody: null, - }); - await container.KyselyPg.insertInto('public.rules_and_actions') - .values({ action_id: action.id, rule_id: rule.id }) - .execute(); - return { - rule, - action, - async cleanup() { - await sutWithPrimary.deleteCustomAction({ - orgId: dummyOrgId, - actionId: action.id, - }); - await rule.destroy(); - }, - }; - }); + const testWithRuleAndAction = makeTransactionalTestWithFixture( + async ({ deps }) => { + const base = await setupOrg(deps); + const { sutWithPrimary, org } = base; + + const rule = await createRule(deps.KyselyPg, org.id); + const action = await sutWithPrimary.createAction(org.id, { + name: faker.random.alphaNumeric(16), + description: null, + type: 'CUSTOM_ACTION', + callbackUrl: 'https://example.com', + callbackUrlHeaders: null, + callbackUrlBody: null, + }); + await deps.KyselyPg.insertInto('public.rules_and_actions') + .values({ action_id: action.id, rule_id: rule.id }) + .execute(); + return { ...base, rule, action }; + }, + ); testWithRuleAndAction( 'should return actions for a rule scoped to the caller org', - async ({ rule, action }) => { + async ({ sutWithPrimary, org, rule, action }) => { const result = await sutWithPrimary.getActionsForRuleId({ - orgId: dummyOrgId, + orgId: org.id, ruleId: rule.id, readFromReplica: false, }); @@ -1403,25 +1321,21 @@ describe('ModerationConfigService', () => { testWithRuleAndAction( 'should not return actions when called with a different org', - async ({ rule }) => { - const otherOrg = await createOrg( + async ({ sutWithPrimary, deps, rule }) => { + const { org: otherOrg } = await createOrg( { - KyselyPg: container.KyselyPg, - ModerationConfigService: container.ModerationConfigService, - ApiKeyService: container.ApiKeyService, + KyselyPg: deps.KyselyPg, + ModerationConfigService: deps.ModerationConfigService, + ApiKeyService: deps.ApiKeyService, }, uid(), ); - try { - const result = await sutWithPrimary.getActionsForRuleId({ - orgId: otherOrg.org.id, - ruleId: rule.id, - readFromReplica: false, - }); - expect(result).toEqual([]); - } finally { - await otherOrg.cleanup(); - } + const result = await sutWithPrimary.getActionsForRuleId({ + orgId: otherOrg.id, + ruleId: rule.id, + readFromReplica: false, + }); + expect(result).toEqual([]); }, ); }); @@ -1429,46 +1343,40 @@ describe('ModerationConfigService', () => { describe('Policy returning methods', () => { describe('Read methods', () => { - it('should query from the proper db', async () => { - await testReadReplicaUse('getPolicies', { orgId: dummyOrgId }); - }); + testWithOrg( + 'should query from the proper db', + async ({ sutWithPrimary, sutWithReadReplica, org }) => { + await expectReadReplicaUse( + { sutWithPrimary, sutWithReadReplica }, + 'getPolicies', + { orgId: org.id }, + ); + }, + ); // TODO: Fill in this test once we've implemented the policy mutations - it.skip('should return all policies, properly formatted', async () => { - const res = await sutWithPrimary.getPolicies({ orgId: dummyOrgId }); - expect(res).toHaveLength(createdPolicies.length); - expect(res).toEqual(expect.arrayContaining(createdPolicies)); - }); + testWithOrg.skip( + 'should return all policies, properly formatted', + async ({ sutWithPrimary, org }) => { + const createdPolicies = [] as Policy[]; + const res = await sutWithPrimary.getPolicies({ orgId: org.id }); + expect(res).toHaveLength(createdPolicies.length); + expect(res).toEqual(expect.arrayContaining(createdPolicies)); + }, + ); }); describe('Mutations', () => { - const testWithUserAndOrg = makeTestWithFixture(async () => { - const { org, cleanup: orgCleanup } = await createOrg( - { - KyselyPg: container.KyselyPg, - ModerationConfigService: container.ModerationConfigService, - ApiKeyService: container.ApiKeyService, - }, - uid(), - ); - - const { user, cleanup: userCleanup } = await createUser( - container.KyselyPg, - org.id, - ); - - return { - org, - user, - async cleanup() { - await userCleanup(); - await orgCleanup(); - }, - }; - }); + const testWithUserAndOrg = makeTransactionalTestWithFixture( + async ({ deps }) => { + const base = await setupOrg(deps); + const { user } = await createUser(deps.KyselyPg, base.org.id); + return { ...base, user }; + }, + ); testWithUserAndOrg( 'should create a root policy', - async ({ org, user }) => { + async ({ sutWithPrimary, org, user }) => { const policy = await sutWithPrimary.createPolicy({ orgId: org.id, policy: { @@ -1493,7 +1401,7 @@ describe('ModerationConfigService', () => { testWithUserAndOrg( 'should create parent and child policies', - async ({ org, user }) => { + async ({ sutWithPrimary, org, user }) => { const parentPolicy = await sutWithPrimary.createPolicy({ orgId: org.id, policy: { @@ -1536,7 +1444,7 @@ describe('ModerationConfigService', () => { testWithUserAndOrg( 'should update an existing policy', - async ({ org, user }) => { + async ({ sutWithPrimary, org, user }) => { const policy = await sutWithPrimary.createPolicy({ orgId: org.id, policy: { @@ -1580,7 +1488,7 @@ describe('ModerationConfigService', () => { testWithUserAndOrg( 'Prevent creation of policy with the same name as an existing policy', - async ({ org, user }) => { + async ({ sutWithPrimary, org, user }) => { await sutWithPrimary.createPolicy({ orgId: org.id, policy: { @@ -1621,135 +1529,153 @@ describe('ModerationConfigService', () => { }); }); describe('TextBank-returning methods', () => { - let createdTextBanks = [] as { - id: string; - orgId: string; - name: string; - description: string | null; - type: 'STRING' | 'REGEX'; - createdAt: Date; - updatedAt: Date; - ownerId: string | null; - strings: string[]; - }[]; - describe('Mutations', () => { describe('#createTextBank', () => { - it('should create a text bank', async () => { - const textBank = await sutWithPrimary.createTextBank(dummyOrgId, { - name: 'Test Text Bank', - description: 'Test description', - type: 'STRING' as const, - strings: ['test entry 1', 'test entry 2'], - }); - - expect(textBank).toEqual( - expect.objectContaining({ - createdAt: expect.any(Date), - updatedAt: expect.any(Date), - description: 'Test description', - id: expect.any(String), + testWithOrg( + 'should create a text bank', + async ({ sutWithPrimary, org }) => { + const textBank = await sutWithPrimary.createTextBank(org.id, { name: 'Test Text Bank', - orgId: expect.any(String), - ownerId: null, + description: 'Test description', + type: 'STRING' as const, strings: ['test entry 1', 'test entry 2'], - type: 'STRING', - }), - ); + }); - expect(textBank.orgId).toBe(dummyOrgId); - createdTextBanks = [...createdTextBanks, textBank]; - }); + expect(textBank).toEqual( + expect.objectContaining({ + createdAt: expect.any(Date), + updatedAt: expect.any(Date), + description: 'Test description', + id: expect.any(String), + name: 'Test Text Bank', + orgId: expect.any(String), + ownerId: null, + strings: ['test entry 1', 'test entry 2'], + type: 'STRING', + }), + ); + + expect(textBank.orgId).toBe(org.id); + }, + ); }); }); describe('Read methods', () => { describe('#getTextBanks', () => { - it('should return all text banks, properly formatted', async () => { - const res = await sutWithPrimary.getTextBanks({ orgId: dummyOrgId }); - expect(res).toHaveLength(createdTextBanks.length); - expect(res).toEqual(expect.arrayContaining(createdTextBanks)); - }); + testWithOrg( + 'should return all text banks, properly formatted', + async ({ sutWithPrimary, org }) => { + const createdTextBanks = [ + await sutWithPrimary.createTextBank(org.id, { + name: 'Test Text Bank 1', + description: 'Test description', + type: 'STRING' as const, + strings: ['test entry 1', 'test entry 2'], + }), + await sutWithPrimary.createTextBank(org.id, { + name: 'Test Text Bank 2', + description: null, + type: 'REGEX' as const, + strings: ['.*'], + }), + ]; + + const res = await sutWithPrimary.getTextBanks({ orgId: org.id }); + expect(res).toHaveLength(createdTextBanks.length); + expect(res).toEqual(expect.arrayContaining(createdTextBanks)); + }, + ); }); describe('#getTextBank', () => { - it('should return a specific text bank, properly formatted', async () => { - const textBank = createdTextBanks[0]; - const res = await sutWithPrimary.getTextBank({ - orgId: dummyOrgId, - id: textBank.id, - }); - expect(res).toEqual(textBank); - }); + testWithOrg( + 'should return a specific text bank, properly formatted', + async ({ sutWithPrimary, org }) => { + const textBank = await sutWithPrimary.createTextBank(org.id, { + name: 'Test Text Bank', + description: 'Test description', + type: 'STRING' as const, + strings: ['test entry 1', 'test entry 2'], + }); + + const res = await sutWithPrimary.getTextBank({ + orgId: org.id, + id: textBank.id, + }); + expect(res).toEqual(textBank); + }, + ); }); }); }); describe('#getItemType', () => { - const testWithOneItemTypeFixture = makeTestWithFixture(async () => { - const itemType = await sutWithPrimary.createContentType(dummyOrgId, { - schema: dummySchema, - description: null, - name: faker.random.alphaNumeric(16), - schemaFieldRoles: { - displayName: 'fakeField', - }, - }); + const testWithOneItemTypeFixture = makeTransactionalTestWithFixture( + async ({ deps }) => { + const base = await setupOrg(deps); + const itemType = await base.sutWithPrimary.createContentType( + base.org.id, + { + schema: dummySchema, + description: null, + name: faker.random.alphaNumeric(16), + schemaFieldRoles: { + displayName: 'fakeField', + }, + }, + ); - return { - itemType, - async cleanup() { - await sutWithPrimary.deleteItemType({ - orgId: dummyOrgId, - itemTypeId: itemType.id, - }); - }, - }; - }); + return { ...base, itemType }; + }, + ); - const testWithTwoItemTypesFixture = makeTestWithFixture(async () => { - const itemType = await sutWithPrimary.createContentType(dummyOrgId, { - schema: dummySchema, - description: null, - name: faker.random.alphaNumeric(16), - schemaFieldRoles: { - displayName: 'fakeField', - }, - }); + const testWithTwoItemTypesFixture = makeTransactionalTestWithFixture( + async ({ deps }) => { + const base = await setupOrg(deps); + const itemType = await base.sutWithPrimary.createContentType( + base.org.id, + { + schema: dummySchema, + description: null, + name: faker.random.alphaNumeric(16), + schemaFieldRoles: { + displayName: 'fakeField', + }, + }, + ); - const newItemType = await sutWithPrimary.updateContentType(dummyOrgId, { - id: itemType.id, - name: faker.random.alphaNumeric(16), - schemaFieldRoles: { - creatorId: undefined, - }, - }); + const newItemType = await base.sutWithPrimary.updateContentType( + base.org.id, + { + id: itemType.id, + name: faker.random.alphaNumeric(16), + schemaFieldRoles: { + creatorId: undefined, + }, + }, + ); - return { - itemType, - newItemType, - async cleanup() { - await sutWithPrimary.deleteItemType({ - orgId: dummyOrgId, - itemTypeId: itemType.id, - }); - }, - }; - }); + return { ...base, itemType, newItemType }; + }, + ); - it("Should return undefined if an item type with the given ID doesn't exist", async () => { - const itemType = await sutWithPrimary.getItemType({ - orgId: dummyOrgId, - itemTypeSelector: { id: 'fakeId' }, - }); + testWithOrg( + "Should return undefined if an item type with the given ID doesn't exist", + async ({ sutWithPrimary, org }) => { + const itemType = await sutWithPrimary.getItemType({ + orgId: org.id, + itemTypeSelector: { id: 'fakeId' }, + }); - expect(itemType).toBeUndefined(); - }); + expect(itemType).toBeUndefined(); + }, + ); testWithOneItemTypeFixture( 'Should return a partial item type if requested for a selector without a version', - async ({ itemType }) => { + async ({ sutWithPrimary, org, itemType }) => { const fetched = await sutWithPrimary.getItemType({ - orgId: dummyOrgId, + orgId: org.id, itemTypeSelector: { id: itemType.id, schemaVariant: 'partial' }, }); @@ -1759,9 +1685,9 @@ describe('ModerationConfigService', () => { ); testWithTwoItemTypesFixture( 'Should return a partial item type if requested for a selector with a version', - async ({ itemType, newItemType }) => { + async ({ sutWithPrimary, org, itemType, newItemType }) => { const fetched = await sutWithPrimary.getItemType({ - orgId: dummyOrgId, + orgId: org.id, itemTypeSelector: { id: itemType.id, schemaVariant: 'partial', @@ -1772,18 +1698,13 @@ describe('ModerationConfigService', () => { expect(fetched).not.toBeNull(); expect(fetched!.name).toEqual(newItemType.name); fetched!.schema.forEach((it) => expect(it.required).toEqual(false)); - - await sutWithPrimary.deleteItemType({ - itemTypeId: itemType.id, - orgId: dummyOrgId, - }); }, ); testWithTwoItemTypesFixture( 'Should return latest item type if only an ID is provided', - async ({ itemType, newItemType }) => { + async ({ sutWithPrimary, org, itemType, newItemType }) => { const fetched = await sutWithPrimary.getItemType({ - orgId: dummyOrgId, + orgId: org.id, itemTypeSelector: { id: itemType.id }, }); @@ -1791,10 +1712,54 @@ describe('ModerationConfigService', () => { expect(fetched!.name).toEqual(newItemType.name); }, ); - testWithOneItemTypeFixture( + // Fetching a *historical* version needs two versions with distinct + // timestamps. `item_type_versions` is a view over the system-versioned + // `item_types` table, whose `version` is `transaction_timestamp()`. The + // rollback harness runs the whole test in one transaction, so a create + + // update there share a timestamp and collapse into a single version — there + // is no older version left to fetch. This test therefore commits its two + // versions through the real container (separate transactions => distinct + // timestamps) and cleans up after itself; it's still self-contained. + const testWithHistoricalItemType = makeTestWithFixture(async () => { + const { container } = await getBottle(); + const sut = new ModerationConfigService( + container.KyselyPg, + container.KyselyPgReadReplica, + async () => {}, + ); + const { org, cleanup: orgCleanup } = await createOrg( + { + KyselyPg: container.KyselyPg, + ModerationConfigService: container.ModerationConfigService, + ApiKeyService: container.ApiKeyService, + }, + uid(), + ); + const itemType = await sut.createContentType(org.id, { + schema: dummySchema, + description: null, + name: faker.random.alphaNumeric(16), + schemaFieldRoles: { displayName: 'fakeField' }, + }); + + return { + sut, + org, + itemType, + async cleanup() { + await orgCleanup(); + await Promise.all([ + container.KyselyPg.destroy(), + container.KyselyPgReadReplica.destroy(), + ]); + }, + }; + }); + + testWithHistoricalItemType( 'Should return requested item type version', - async ({ itemType }) => { - await sutWithPrimary.updateContentType(dummyOrgId, { + async ({ sut, org, itemType }) => { + await sut.updateContentType(org.id, { id: itemType.id, name: faker.random.alphaNumeric(16), schemaFieldRoles: { @@ -1802,8 +1767,8 @@ describe('ModerationConfigService', () => { }, }); - const fetched = await sutWithPrimary.getItemType({ - orgId: dummyOrgId, + const fetched = await sut.getItemType({ + orgId: org.id, itemTypeSelector: { id: itemType.id, version: itemType.version }, }); diff --git a/server/services/moderationConfigService/modules/MatchingBankOperations.ts b/server/services/moderationConfigService/modules/MatchingBankOperations.ts index e1481f1f6..2b4180f63 100644 --- a/server/services/moderationConfigService/modules/MatchingBankOperations.ts +++ b/server/services/moderationConfigService/modules/MatchingBankOperations.ts @@ -155,8 +155,7 @@ export default class MatchingBankOperations { } export type MatchingBankErrorType = - | 'MatchingBankNameExistsError' - | 'MatchingBankNotFoundError'; + 'MatchingBankNameExistsError' | 'MatchingBankNotFoundError'; export const makeMatchingBankNameExistsError = (data: ErrorInstanceData) => new CoopError({ diff --git a/server/services/moderationConfigService/types/conditionResults.ts b/server/services/moderationConfigService/types/conditionResults.ts index 4cf5f470a..cbcdc541d 100644 --- a/server/services/moderationConfigService/types/conditionResults.ts +++ b/server/services/moderationConfigService/types/conditionResults.ts @@ -21,8 +21,7 @@ export enum ConditionFailureOutcome { } export type ConditionOutcome = - | ConditionCompletionOutcome - | ConditionFailureOutcome; + ConditionCompletionOutcome | ConditionFailureOutcome; export type ConditionCompletionMetadata = { score?: string; @@ -49,8 +48,7 @@ export type ConditionResult = & ConditionResultCommonMetadata) export type ConditionWithResult = - | LeafConditionWithResult - | ConditionSetWithResult; + LeafConditionWithResult | ConditionSetWithResult; export type ConditionSetWithResult = Omit & { conditions: diff --git a/server/services/moderationConfigService/types/itemTypes.ts b/server/services/moderationConfigService/types/itemTypes.ts index c38c4e993..9e77179f7 100644 --- a/server/services/moderationConfigService/types/itemTypes.ts +++ b/server/services/moderationConfigService/types/itemTypes.ts @@ -15,9 +15,7 @@ export type ItemTypeSchemaVariant = 'original' | 'partial'; export { ItemTypeKind }; export type ItemType = - | Readonly - | Readonly - | Readonly; + Readonly | Readonly | Readonly; type ItemTypeBase = { id: string; @@ -84,9 +82,7 @@ export type ContentSchemaFieldRoles = { ); export type SchemaFieldRoles = - | UserSchemaFieldRoles - | ThreadSchemaFieldRoles - | ContentSchemaFieldRoles; + UserSchemaFieldRoles | ThreadSchemaFieldRoles | ContentSchemaFieldRoles; /** * These three fields uniquely identify a particular "incarnation" of a given diff --git a/server/services/ncmecService/fieldCoverage.test.ts b/server/services/ncmecService/fieldCoverage.test.ts new file mode 100644 index 000000000..194b7b330 --- /dev/null +++ b/server/services/ncmecService/fieldCoverage.test.ts @@ -0,0 +1,400 @@ +/** + * Field-coverage regression net for the NCMEC CyberTip report builder. + * + * The builder emits different XML fields depending on how much data the + * adopter has about the reported user. We lock that behaviour by rendering + * the report under three configurations and asserting, per field, which ones + * populate it: + * + * - `min` — bare minimum: no user data, no webhook, no reviewer + * escalation. Only the org-setting-backed fields NCMEC + * strictly requires appear. + * - `field-roles` — the adopter maps their user data to coop's schema field + * roles (email, IP, display name, profile icon). Fields + * read off the user via roles now populate; webhook and + * reviewer-decision fields stay absent. + * - `max` — everything wired: field roles + the additional-info + * webhook + a reviewer escalation + top-level notes. + */ +import { js2xml } from 'xml-js'; + +import { + buildSubmitReportObject, + NCMECEvent, + type BuildSubmitReportObjectInput, +} from './ncmecReporting.js'; + +const INCIDENT_DATE_TIME = '2026-06-30T18:00:00.000Z'; + +/** Minimal valid inputs for `buildSubmitReportObject`. Tests override only + * the field(s) under test, so each case is self-explanatory. */ +function makeBuildReportInput( + overrides: { + reportParams?: Partial & { + reportedUser?: Partial< + BuildSubmitReportObjectInput['reportParams']['reportedUser'] + >; + }; + userAdditionalInfo?: BuildSubmitReportObjectInput['userAdditionalInfo']; + orgSettings?: Partial; + clampedIncidentDateTime?: string; + priorCTReports?: readonly number[]; + } = {}, +): BuildSubmitReportObjectInput { + const { reportParams: paramOverrides, ...rest } = overrides; + const { reportedUser: userOverrides, ...reportParamOverrides } = + paramOverrides ?? {}; + return { + reportParams: { + orgId: 'org-1', + reviewerId: 'reviewer-1', + reportedUser: { + id: 'user-1', + typeId: 'user-type-1', + ...userOverrides, + }, + media: [], + threads: [], + incidentType: + 'Child Pornography (possession, manufacture, and distribution)', + ...reportParamOverrides, + }, + userAdditionalInfo: rest.userAdditionalInfo ?? {}, + orgSettings: { + companyTemplate: 'AcmeESP', + legalURL: 'https://acme.example/legal', + reportingPersonEmail: 'reporter@acme.example', + ...rest.orgSettings, + }, + clampedIncidentDateTime: rest.clampedIncidentDateTime ?? INCIDENT_DATE_TIME, + ...(rest.priorCTReports !== undefined + ? { priorCTReports: rest.priorCTReports } + : {}), + }; +} + +// Use `any` here to avoid a ts-node crash when many path functions are typed +// against the deeply-nested Report union. +/* eslint-disable @typescript-eslint/no-explicit-any */ +type AnyReport = any; + +function renderXml(report: ReturnType): string { + return js2xml(report, { compact: true }); +} + +type ScenarioExpectations = { + min: boolean; + 'field-roles': boolean; + max: boolean; +}; + +const presenceTable: Array<{ + field: string; + path: (r: AnyReport) => unknown; + expected: ScenarioExpectations; +}> = [ + // Required by the XSD — present in every configuration (backed by org + // settings, not user data). + { + field: 'incidentSummary.incidentType', + path: (r) => r.report.incidentSummary.incidentType, + expected: { min: true, 'field-roles': true, max: true }, + }, + { + field: 'incidentSummary.incidentDateTime', + path: (r) => r.report.incidentSummary.incidentDateTime, + expected: { min: true, 'field-roles': true, max: true }, + }, + { + field: 'reporter.reportingPerson.email', + path: (r) => r.report.reporter.reportingPerson.email, + expected: { min: true, 'field-roles': true, max: true }, + }, + { + field: 'reporter.companyTemplate', + path: (r) => r.report.reporter.companyTemplate, + expected: { min: true, 'field-roles': true, max: true }, + }, + { + field: 'reporter.legalURL', + path: (r) => r.report.reporter.legalURL, + expected: { min: true, 'field-roles': true, max: true }, + }, + // These fields are read off the reported user's data via schema field roles. + // In `min` no user data is provided, so they're absent; with roles mapped + // they populate. + { + field: 'personOrUserReportedPerson.email', + path: (r) => + r.report.personOrUserReported?.personOrUserReportedPerson?.email, + expected: { min: false, 'field-roles': true, max: true }, + }, + { + field: 'personOrUserReported.displayName', + path: (r) => r.report.personOrUserReported?.displayName, + expected: { min: false, 'field-roles': true, max: true }, + }, + { + field: 'personOrUserReported.ipCaptureEvent', + path: (r) => r.report.personOrUserReported?.ipCaptureEvent, + expected: { min: false, 'field-roles': true, max: true }, + }, + // These fields come from reviewer decisions or the additional-info webhook, + // neither of which the `min` or `field-roles` scenarios provide. + { + field: 'incidentSummary.escalateToHighPriority', + path: (r) => r.report.incidentSummary.escalateToHighPriority, + expected: { min: false, 'field-roles': false, max: true }, + }, + { + field: 'report.additionalInfo', + path: (r) => r.report.additionalInfo, + expected: { min: false, 'field-roles': false, max: true }, + }, + { + field: 'personOrUserReported.priorCTReports', + path: (r) => r.report.personOrUserReported?.priorCTReports, + expected: { min: false, 'field-roles': false, max: true }, + }, + { + field: 'reporter.contactPerson', + path: (r) => r.report.reporter.contactPerson, + expected: { min: false, 'field-roles': false, max: true }, + }, + { + field: 'reporter.termsOfService', + path: (r) => r.report.reporter.termsOfService, + expected: { min: false, 'field-roles': false, max: true }, + }, + { + field: 'internetDetails.webPageIncident', + path: (r) => r.report.internetDetails?.[0]?.webPageIncident, + expected: { min: false, 'field-roles': false, max: true }, + }, +]; +/* eslint-enable @typescript-eslint/no-explicit-any */ + +describe('NCMEC field coverage', () => { + // One rendered report per configuration (see the file header for what each + // represents). The presence table below asserts which fields each one emits. + const scenarios = { + min: buildSubmitReportObject(makeBuildReportInput()), + 'field-roles': buildSubmitReportObject( + makeBuildReportInput({ + reportParams: { + reportedUser: { + id: 'user-1', + typeId: 'user-type-1', + displayName: 'Jane Doe', + profilePicture: 'https://cdn.example/jane.png', + ipAddress: '203.0.113.7', + email: 'jane@example.com', + }, + }, + }), + ), + max: buildSubmitReportObject( + makeBuildReportInput({ + reportParams: { + reportedUser: { + id: 'user-1', + typeId: 'user-type-1', + displayName: 'Jane Doe', + profilePicture: 'https://cdn.example/jane.png', + ipAddress: '203.0.113.7', + email: 'jane@example.com', + }, + escalateToHighPriority: 'immediate risk', + additionalInfo: 'top-level note', + }, + userAdditionalInfo: { + screenName: 'jane123', + email: [ + { + _text: 'jane@example.com', + _attributes: { type: 'Home', verified: true }, + }, + ], + ipCaptureEvent: [ + { + eventName: NCMECEvent.Login, + dateTime: INCIDENT_DATE_TIME, + ipAddress: '203.0.113.7', + possibleProxy: true, + port: 443, + }, + ], + }, + orgSettings: { + companyTemplate: 'AcmeESP', + legalURL: 'https://acme.example/legal', + reportingPersonEmail: 'reporter@acme.example', + contactPersonEmail: 'contact@acme.example', + contactPersonFirstName: 'Cmp', + contactPersonLastName: 'Last', + contactPersonPhone: '+15555550100', + termsOfService: 'do not be evil', + defaultInternetDetailType: 'WEB_PAGE', + moreInfoUrl: 'https://acme.example/info', + }, + priorCTReports: [123, 456], + }), + ), + }; + + type ScenarioKey = keyof typeof scenarios; + + const has = (path: (r: AnyReport) => unknown, key: ScenarioKey): boolean => + path(scenarios[key]) !== undefined; + + describe('presence table', () => { + for (const row of presenceTable) { + it(`emits ${row.field} per scenario expectations`, () => { + for (const key of ['min', 'field-roles', 'max'] as ScenarioKey[]) { + expect(has(row.path, key)).toBe(row.expected[key]); + } + }); + } + }); + + it('renders all three scenarios to well-formed XML with scenario-specific markers', () => { + // The presence table checks the object tree; this is the only test that + // exercises the js2xml serialization path, so assert meaningful structural + // and field markers per scenario rather than just non-empty output. + const markers: Record< + ScenarioKey, + { present: string[]; absent: string[] } + > = { + // min: only org-setting-backed required fields — no user data, no + // escalation, no top-level additionalInfo. personOrUserReported is + // always emitted (espIdentifier/espService) but carries no IP event, + // display name, or prior reports. + min: { + present: [ + '', + '', + '', + '', + '', + '', + '', + ], + absent: [ + '', + '', + '', + '', + ], + }, + // field-roles: user data via roles populates personOrUserReported + IP + // event, but still no escalation or top-level additionalInfo. + 'field-roles': { + present: [ + '', + '', + '', + 'jane@example.com', + ], + absent: ['', ''], + }, + // max: everything wired — escalation, additionalInfo, priorCTReports, + // and contactPerson all present. + max: { + present: [ + '', + '', + '', + '', + ], + absent: [], + }, + }; + for (const key of ['min', 'field-roles', 'max'] as ScenarioKey[]) { + const xml = renderXml(scenarios[key]); + expect(xml.length).toBeGreaterThan(0); + for (const marker of markers[key].present) { + expect(xml).toContain(marker); + } + for (const marker of markers[key].absent) { + expect(xml).not.toContain(marker); + } + } + }); + + // xml-js emits child elements in object-key insertion order, and NCMEC's + // XSD requires a specific sequence — out-of-order children are rejected. + // These locks fail if the builder ever emits keys in the wrong order. + describe('XSD ordering locks', () => { + const orderOf = (emitted: string[], xsdSequence: string[]): string[] => { + const idx = new Map(xsdSequence.map((k, i) => [k, i] as const)); + return emitted + .filter((k) => idx.has(k)) + .sort((a, b) => idx.get(a)! - idx.get(b)!); + }; + + const XSD = { + incidentSummary: [ + 'incidentType', + 'platform', + 'escalateToHighPriority', + 'reportAnnotations', + 'incidentDateTime', + 'incidentDateTimeDescription', + ], + personOrUserReported: [ + 'personOrUserReportedPerson', + 'vehicleDescription', + 'espIdentifier', + 'espService', + 'compromisedAccount', + 'screenName', + 'displayName', + 'profileUrl', + 'profileBio', + 'ipCaptureEvent', + 'deviceId', + 'thirdPartyUserReported', + 'priorCTReports', + 'groupIdentifier', + 'accountTemporarilyDisabled', + 'accountPermanentlyDisabled', + 'estimatedLocation', + 'allEmailsReported', + 'associatedAccount', + 'additionalInfo', + ], + ipCaptureEvent: [ + 'ipAddress', + 'eventName', + 'dateTime', + 'possibleProxy', + 'port', + ], + }; + + it('incidentSummary children follow XSD sequence', () => { + const max = scenarios.max as AnyReport; + const emitted = Object.keys(max.report.incidentSummary); + expect(orderOf(emitted, XSD.incidentSummary)).toEqual( + emitted.filter((k) => XSD.incidentSummary.includes(k)), + ); + }); + + it('personOrUserReported children follow XSD sequence', () => { + const max = scenarios.max as AnyReport; + const emitted = Object.keys(max.report.personOrUserReported); + expect(orderOf(emitted, XSD.personOrUserReported)).toEqual( + emitted.filter((k) => XSD.personOrUserReported.includes(k)), + ); + }); + + it('ipCaptureEvent children follow XSD sequence', () => { + const max = scenarios.max as AnyReport; + const evs = max.report.personOrUserReported.ipCaptureEvent; + for (const ev of evs) { + const emitted = Object.keys(ev); + expect(orderOf(emitted, XSD.ipCaptureEvent)).toEqual(emitted); + } + }); + }); +}); diff --git a/server/services/ncmecService/index.ts b/server/services/ncmecService/index.ts index 8d28de1ab..bb9bf4e43 100644 --- a/server/services/ncmecService/index.ts +++ b/server/services/ncmecService/index.ts @@ -3,7 +3,13 @@ export { default as makeNcmecService, } from './ncmecService.js'; -export { NCMECIncidentType } from './ncmecReporting.js'; +export { + NCMECIncidentType, + NCMECFileAnnotation, + NCMECIndustryClassification, + type NCMECReportParams, + default as NcmecReporting, +} from './ncmecReporting.js'; export { summarizeNcmecErrorForReviewer } from './ncmecReviewerErrors.js'; export { filterDecisionsToFailedSubmissions } from './ncmecSubmissionFilters.js'; export { diff --git a/server/services/ncmecService/ncmecReporting.builders.test.ts b/server/services/ncmecService/ncmecReporting.builders.test.ts index 0f0582fa4..58ccab845 100644 --- a/server/services/ncmecService/ncmecReporting.builders.test.ts +++ b/server/services/ncmecService/ncmecReporting.builders.test.ts @@ -137,9 +137,9 @@ describe('buildFileDetailsObject', () => { 'publiclyAvailable', 'fileRelevance', 'fileAnnotations', - 'ipCaptureEvent', 'industryClassification', 'originalFileHash', + 'ipCaptureEvent', 'additionalInfo', ]); }); diff --git a/server/services/ncmecService/ncmecReporting.test.ts b/server/services/ncmecService/ncmecReporting.test.ts index 9ab504072..5daa0fd35 100644 --- a/server/services/ncmecService/ncmecReporting.test.ts +++ b/server/services/ncmecService/ncmecReporting.test.ts @@ -306,6 +306,34 @@ describe('NCMEC reporting', () => { expect(webhook).toHaveLength(1); expect(params).toHaveLength(1); }); + + it('canonicalises webhook/param event key order to the XSD sequence', () => { + // A webhook returning ipCaptureEvent with keys in non-XSD order must be + // rebuilt as { ipAddress, eventName, dateTime, possibleProxy?, port? } + // before serialisation, or xml-js emits out-of-order children and NCMEC + // rejects the report with responseCode=4100. + const nonCanonicalWebhook = { + eventName: NCMECEvent.Login, + dateTime: '2026-01-01T00:00:00.000Z', + ipAddress: '192.0.2.1', + port: 443, + possibleProxy: true, + }; + const result = mergeFieldRoleIpIntoEvents( + [nonCanonicalWebhook], + undefined, + undefined, + synth, + ); + expect(result).toEqual([nonCanonicalWebhook]); + expect(Object.keys(result![0])).toEqual([ + 'ipAddress', + 'eventName', + 'dateTime', + 'possibleProxy', + 'port', + ]); + }); }); describe('resolveReportedPersonEmail', () => { diff --git a/server/services/ncmecService/ncmecReporting.ts b/server/services/ncmecService/ncmecReporting.ts index 6ee5d2c49..a660b8161 100644 --- a/server/services/ncmecService/ncmecReporting.ts +++ b/server/services/ncmecService/ncmecReporting.ts @@ -132,9 +132,9 @@ type FileDetails = { publiclyAvailable?: boolean; fileRelevance?: 'Reported' | 'Supplemental Reported'; fileAnnotations?: FileAnnotations; - ipCaptureEvent?: IPNCMECEvent[]; industryClassification?: NCMECIndustryClassificationType; originalFileHash?: OriginalFileHash[]; + ipCaptureEvent?: IPNCMECEvent[]; deviceId?: DeviceId[]; details?: Detail[]; additionalInfo?: string[]; @@ -525,6 +525,25 @@ export function clampIncidentDateTimeToPast( }; } +/** Rebuild an ipCaptureEvent object with keys in NCMEC XSD `xs:sequence` + * order (ipAddress, eventName, dateTime, possibleProxy, port) so xml-js + * serialises the children in the order NCMEC's validator requires. Webhook + * and caller-supplied events arrive in arbitrary key order; without this, + * a non-canonical webhook event produces out-of-order XML and NCMEC rejects + * the report with responseCode=4100. Absent keys + * are omitted so optional fields stay optional. */ +function canonicaliseIpEvent(event: IPNCMECEvent): IPNCMECEvent { + const out: IPNCMECEvent = { + ipAddress: event.ipAddress, + eventName: event.eventName, + dateTime: event.dateTime, + }; + if (event.possibleProxy !== undefined) + out.possibleProxy = event.possibleProxy; + if (event.port !== undefined) out.port = event.port; + return out; +} + /** Build the `ipCaptureEvent` array for an NCMEC person or media block: * webhook events + caller-supplied events + role-IP-synthesised event, in * that order. Returns `undefined` when all sources are empty. */ @@ -546,8 +565,8 @@ export function mergeFieldRoleIpIntoEvents( const trimmedRoleIp = typeof roleIpAddress === 'string' ? roleIpAddress.trim() : ''; const events: IPNCMECEvent[] = [ - ...(webhookEvents ?? []), - ...paramEventsArray, + ...(webhookEvents ?? []).map(canonicaliseIpEvent), + ...paramEventsArray.map(canonicaliseIpEvent), ...(trimmedRoleIp !== '' ? [ { @@ -912,6 +931,10 @@ export function buildFileDetailsObject( : {}), fileRelevance, ...(fileAnnotations ? { fileAnnotations } : {}), + industryClassification: media.industryClassification, + ...(originalFileHash && originalFileHash.length > 0 + ? { originalFileHash: [...originalFileHash] } + : {}), ...(additionalInfo.ipCaptureEvent && additionalInfo.ipCaptureEvent.length > 0 ? { @@ -924,10 +947,6 @@ export function buildFileDetailsObject( })), } : {}), - industryClassification: media.industryClassification, - ...(originalFileHash && originalFileHash.length > 0 - ? { originalFileHash: [...originalFileHash] } - : {}), ...(additionalInfo.additionalInfo ? { additionalInfo: additionalInfo.additionalInfo } : {}), @@ -1329,10 +1348,7 @@ export type NcmecMessagesReport = { }; type NcmecReportResult = - | 'ALL_MEDIA_MISSING' - | 'SUCCESS' - | 'UNSUPPORTED_ORG' - | 'FAILURE'; + 'ALL_MEDIA_MISSING' | 'SUCCESS' | 'UNSUPPORTED_ORG' | 'FAILURE'; const actionsOnReportCreationAndPoliciesSelection = [ 'actions_to_run_upon_report_creation as actionsToRunIds', @@ -1375,8 +1391,7 @@ export default class NcmecReporting { .where('org_id', '=', orgId) .executeTakeFirst(); return row as - | NcmecReportingServicePg['ncmec_reporting.ncmec_org_settings'] - | undefined; + NcmecReportingServicePg['ncmec_reporting.ncmec_org_settings'] | undefined; } async getNCMECActionsToRunAndPolicies( @@ -1780,31 +1795,12 @@ export default class NcmecReporting { // failure, since we can't guarantee all traces with exceptions are // sampled in DD try { - // These are test accounts that we send to prospective users, and - // they should be able to click "Send to NCMEC" in the UI, but no - // NCMEC report should actually be created. - const testOrgs = ['4def6a77d6a', 'acc701627cb']; - if (!(await this.hasNCMECReportingEnabled(reportParams.orgId))) { throw new Error( `NCMEC reports are not enabled for org ${reportParams.orgId}`, ); } - if (testOrgs.includes(reportParams.orgId)) { - if (reportParams.jobId !== undefined) { - await this.#recordSubmissionError({ - jobId: reportParams.jobId, - userId: reportParams.reportedUser.id, - userTypeId: reportParams.reportedUser.typeId, - status: 'PERMANENT_ERROR', - error: - 'Org is on the NCMEC test allowlist; reports are suppressed.', - }); - } - return 'UNSUPPORTED_ORG'; - } - if (reportParams.media.length === 0) { throw new Error('No media in report'); } diff --git a/server/services/networkingService/index.ts b/server/services/networkingService/index.ts index 8d6417dc2..781be0271 100644 --- a/server/services/networkingService/index.ts +++ b/server/services/networkingService/index.ts @@ -34,7 +34,7 @@ type ResponseBodyMappings = { discard: undefined; }; -type HandleResponseBody = keyof ResponseBodyMappings; +export type HandleResponseBody = keyof ResponseBodyMappings; // A symbol that we can stick on FormDataLikeWithStreams objects to // unambiguously identify those objects as FormDataLikeWithStreams. diff --git a/server/services/notificationsService/notificationsService.ts b/server/services/notificationsService/notificationsService.ts index a8f0999fd..d221ac6e6 100644 --- a/server/services/notificationsService/notificationsService.ts +++ b/server/services/notificationsService/notificationsService.ts @@ -61,8 +61,7 @@ export type Notification = { * specifies a delivery channel. Consider emails like oncall-sre@example.com */ type Recipient = - | { type: 'user_id'; value: string } - | { type: 'email_address'; value: string }; + { type: 'user_id'; value: string } | { type: 'email_address'; value: string }; type CreateNotificationInput = Pick< Notification, diff --git a/server/services/orgAwareSignalExecutionService/signalExecutionService.ts b/server/services/orgAwareSignalExecutionService/signalExecutionService.ts index 4872a13f9..c5c5141bb 100644 --- a/server/services/orgAwareSignalExecutionService/signalExecutionService.ts +++ b/server/services/orgAwareSignalExecutionService/signalExecutionService.ts @@ -200,9 +200,7 @@ async function runSignal( matchingValues?.strings ?? matchingValues?.locations ?? []; let matchingValuesFromBanks: readonly ( - | string - | ReadonlyDeep - | HashBank + string | ReadonlyDeep | HashBank )[] = []; if (textBankIds?.length) { matchingValuesFromBanks = await textBanksStringsLoader( diff --git a/server/services/reportingService/ReportingRules.ts b/server/services/reportingService/ReportingRules.ts index 951afc05f..b4f398afc 100644 --- a/server/services/reportingService/ReportingRules.ts +++ b/server/services/reportingService/ReportingRules.ts @@ -461,8 +461,7 @@ export default class ReportingRules { } export type ReportingRuleErrorType = - | 'ReportingRuleNameExistsError' - | 'NotFoundError'; + 'ReportingRuleNameExistsError' | 'NotFoundError'; function isReportingRuleNameExistsError(error: unknown) { return ( diff --git a/server/services/reportingService/reportingService.ts b/server/services/reportingService/reportingService.ts index c672fcff7..a2108d570 100644 --- a/server/services/reportingService/reportingService.ts +++ b/server/services/reportingService/reportingService.ts @@ -34,8 +34,7 @@ import ReportingRules, { export type ReporterKind = 'rule' | 'user'; export type Reporter = - | { kind: 'rule'; id: string } - | { kind: 'user'; typeId: string; id: string }; + { kind: 'rule'; id: string } | { kind: 'user'; typeId: string; id: string }; export type Appealer = { typeId: string; id: string }; diff --git a/server/services/ruleAnomalyDetectionService/getRuleAlarmStatus.test.ts b/server/services/ruleAnomalyDetectionService/getRuleAlarmStatus.test.ts index c068a33f7..0232efae1 100644 --- a/server/services/ruleAnomalyDetectionService/getRuleAlarmStatus.test.ts +++ b/server/services/ruleAnomalyDetectionService/getRuleAlarmStatus.test.ts @@ -1,7 +1,7 @@ import { readFileSync } from 'fs'; import { dirname, join } from 'path'; import fc from 'fast-check'; -import yaml from 'js-yaml'; +import { load } from 'js-yaml'; import _ from 'lodash'; import { RuleAlarmStatus } from '../moderationConfigService/index.js'; @@ -17,7 +17,7 @@ const samplesPassRate = (samples: { passes: number; runs: number }[]) => sum(samples.map((it) => it.passes)) / sum(samples.map((it) => it.runs)); const __dirname = dirname(new URL(import.meta.url).pathname); -const tableDump = yaml.load( +const tableDump = load( // eslint-disable-next-line security/detect-non-literal-fs-filename readFileSync( join(__dirname, '../../test/stubs/rule_pass_sample_data.yaml'), diff --git a/server/services/sendEmailService/sendEmailService.ts b/server/services/sendEmailService/sendEmailService.ts index d880fa30a..764896ad4 100644 --- a/server/services/sendEmailService/sendEmailService.ts +++ b/server/services/sendEmailService/sendEmailService.ts @@ -11,8 +11,7 @@ export type CoopEmailAddress = (typeof CoopEmailAddress)[keyof typeof CoopEmailAddress]; type Content = - | { text: string; html?: string } - | { html: string; text?: string }; + { text: string; html?: string } | { html: string; text?: string }; export type Message = Content & { to: string | string[]; diff --git a/server/services/signalsService/helpers/instantiateBuiltInSignals.ts b/server/services/signalsService/helpers/instantiateBuiltInSignals.ts index d7a67280f..ec236dd8f 100644 --- a/server/services/signalsService/helpers/instantiateBuiltInSignals.ts +++ b/server/services/signalsService/helpers/instantiateBuiltInSignals.ts @@ -28,6 +28,10 @@ import OpenAiGraphicViolenceTextSignal from '../signals/third_party_signals/open import OpenAiHateTextSignal from '../signals/third_party_signals/open_ai/moderation/OpenAiHateTextSignal.js'; import OpenAiHateThreateningTextSignal from '../signals/third_party_signals/open_ai/moderation/OpenAiHateThreateningTextSignal.js'; import OpenAiSelfHarmImageSignal from '../signals/third_party_signals/open_ai/moderation/OpenAiSelfHarmImageSignal.js'; +import OpenAiSelfHarmInstructionsImageSignal from '../signals/third_party_signals/open_ai/moderation/OpenAiSelfHarmInstructionsImageSignal.js'; +import OpenAiSelfHarmInstructionsTextSignal from '../signals/third_party_signals/open_ai/moderation/OpenAiSelfHarmInstructionsTextSignal.js'; +import OpenAiSelfHarmIntentImageSignal from '../signals/third_party_signals/open_ai/moderation/OpenAiSelfHarmIntentImageSignal.js'; +import OpenAiSelfHarmIntentTextSignal from '../signals/third_party_signals/open_ai/moderation/OpenAiSelfHarmIntentTextSignal.js'; import OpenAiSelfHarmTextSignal from '../signals/third_party_signals/open_ai/moderation/OpenAiSelfHarmTextSignal.js'; import OpenAiSexualImageSignal from '../signals/third_party_signals/open_ai/moderation/OpenAiSexualImageSignal.js'; import OpenAiSexualMinorsTextSignal from '../signals/third_party_signals/open_ai/moderation/OpenAiSexualMinorsTextSignal.js'; @@ -108,6 +112,26 @@ export function instantiateBuiltInSignals( credentialGetters.OPEN_AI, getOpenAiScores, ), + [SignalType.OPEN_AI_SELF_HARM_INSTRUCTIONS_IMAGE_MODEL]: + new OpenAiSelfHarmInstructionsImageSignal( + credentialGetters.OPEN_AI, + getOpenAiScores, + ), + [SignalType.OPEN_AI_SELF_HARM_INSTRUCTIONS_TEXT_MODEL]: + new OpenAiSelfHarmInstructionsTextSignal( + credentialGetters.OPEN_AI, + getOpenAiScores, + ), + [SignalType.OPEN_AI_SELF_HARM_INTENT_IMAGE_MODEL]: + new OpenAiSelfHarmIntentImageSignal( + credentialGetters.OPEN_AI, + getOpenAiScores, + ), + [SignalType.OPEN_AI_SELF_HARM_INTENT_TEXT_MODEL]: + new OpenAiSelfHarmIntentTextSignal( + credentialGetters.OPEN_AI, + getOpenAiScores, + ), [SignalType.OPEN_AI_SELF_HARM_TEXT_MODEL]: new OpenAiSelfHarmTextSignal( credentialGetters.OPEN_AI, getOpenAiScores, diff --git a/server/services/signalsService/signals/third_party_signals/open_ai/moderation/OpenAiGraphicViolenceImageSignal.ts b/server/services/signalsService/signals/third_party_signals/open_ai/moderation/OpenAiGraphicViolenceImageSignal.ts index 0aa1f5ff1..168061ec6 100644 --- a/server/services/signalsService/signals/third_party_signals/open_ai/moderation/OpenAiGraphicViolenceImageSignal.ts +++ b/server/services/signalsService/signals/third_party_signals/open_ai/moderation/OpenAiGraphicViolenceImageSignal.ts @@ -1,5 +1,5 @@ import { SignalType } from '../../../../types/SignalType.js'; -import { makeOpenAiImageModerationSignal } from './openAIModerationUtils.js'; +import { makeOpenAiImageModerationSignal } from './openAiModerationSignalFactory.js'; /** * OpenAI image-moderation signal scoring whether an image depicts death, diff --git a/server/services/signalsService/signals/third_party_signals/open_ai/moderation/OpenAiSelfHarmImageSignal.ts b/server/services/signalsService/signals/third_party_signals/open_ai/moderation/OpenAiSelfHarmImageSignal.ts index 14d3e7e5f..b7cb56d22 100644 --- a/server/services/signalsService/signals/third_party_signals/open_ai/moderation/OpenAiSelfHarmImageSignal.ts +++ b/server/services/signalsService/signals/third_party_signals/open_ai/moderation/OpenAiSelfHarmImageSignal.ts @@ -1,5 +1,5 @@ import { SignalType } from '../../../../types/SignalType.js'; -import { makeOpenAiImageModerationSignal } from './openAIModerationUtils.js'; +import { makeOpenAiImageModerationSignal } from './openAiModerationSignalFactory.js'; /** * OpenAI image-moderation signal scoring whether an image promotes, diff --git a/server/services/signalsService/signals/third_party_signals/open_ai/moderation/OpenAiSelfHarmInstructionsImageSignal.ts b/server/services/signalsService/signals/third_party_signals/open_ai/moderation/OpenAiSelfHarmInstructionsImageSignal.ts new file mode 100644 index 000000000..5bc0cc9d3 --- /dev/null +++ b/server/services/signalsService/signals/third_party_signals/open_ai/moderation/OpenAiSelfHarmInstructionsImageSignal.ts @@ -0,0 +1,19 @@ +import { SignalType } from '../../../../types/SignalType.js'; +import { makeOpenAiImageModerationSignal } from './openAiModerationSignalFactory.js'; + +/** + * OpenAI image-moderation signal scoring whether an image encourages or + * provides instructions for self-harm. Routes through omni-moderation-latest's + * multimodal endpoint and returns the `self-harm/instructions` category score + * (0..1). + */ +const OpenAiSelfHarmInstructionsImageSignal = makeOpenAiImageModerationSignal({ + type: SignalType.OPEN_AI_SELF_HARM_INSTRUCTIONS_IMAGE_MODEL, + displayName: 'OpenAI Self-Harm Instructions Image score', + description: `OpenAI's model that detects content that encourages performing acts of self-harm, such as suicide, cutting, and eating disorders, or that gives instructions or advice on how to commit such acts. Scored against the image. + + This model produces a confidence score between 0 and 1, indicating the model's confidence that the image contains self-harm instructions.`, + modelName: 'self-harm/instructions', +}); + +export default OpenAiSelfHarmInstructionsImageSignal; diff --git a/server/services/signalsService/signals/third_party_signals/open_ai/moderation/OpenAiSelfHarmInstructionsTextSignal.ts b/server/services/signalsService/signals/third_party_signals/open_ai/moderation/OpenAiSelfHarmInstructionsTextSignal.ts new file mode 100644 index 000000000..0ff2fe197 --- /dev/null +++ b/server/services/signalsService/signals/third_party_signals/open_ai/moderation/OpenAiSelfHarmInstructionsTextSignal.ts @@ -0,0 +1,19 @@ +import { SignalType } from '../../../../types/SignalType.js'; +import { makeOpenAiTextModerationSignal } from './openAiModerationSignalFactory.js'; + +/** + * OpenAI text-moderation signal scoring whether text encourages or provides + * instructions for self-harm (suicide, cutting, eating disorders, etc.). + * Routes through omni-moderation-latest and returns the + * `self-harm/instructions` category score (0..1). + */ +const OpenAiSelfHarmInstructionsTextSignal = makeOpenAiTextModerationSignal({ + type: SignalType.OPEN_AI_SELF_HARM_INSTRUCTIONS_TEXT_MODEL, + displayName: 'OpenAI Self-Harm Instructions Text score', + description: `OpenAI's model that detects content that encourages performing acts of self-harm, such as suicide, cutting, and eating disorders, or that gives instructions or advice on how to commit such acts. + + This model produces a confidence score between 0 and 1, indicating the model's confidence that the content contains self-harm instructions.`, + modelName: 'self-harm/instructions', +}); + +export default OpenAiSelfHarmInstructionsTextSignal; diff --git a/server/services/signalsService/signals/third_party_signals/open_ai/moderation/OpenAiSelfHarmIntentImageSignal.ts b/server/services/signalsService/signals/third_party_signals/open_ai/moderation/OpenAiSelfHarmIntentImageSignal.ts new file mode 100644 index 000000000..3ec08f378 --- /dev/null +++ b/server/services/signalsService/signals/third_party_signals/open_ai/moderation/OpenAiSelfHarmIntentImageSignal.ts @@ -0,0 +1,19 @@ +import { SignalType } from '../../../../types/SignalType.js'; +import { makeOpenAiImageModerationSignal } from './openAiModerationSignalFactory.js'; + +/** + * OpenAI image-moderation signal scoring whether an image expresses the + * speaker's intent to engage in acts of self-harm. Routes through + * omni-moderation-latest's multimodal endpoint and returns the + * `self-harm/intent` category score (0..1). + */ +const OpenAiSelfHarmIntentImageSignal = makeOpenAiImageModerationSignal({ + type: SignalType.OPEN_AI_SELF_HARM_INTENT_IMAGE_MODEL, + displayName: 'OpenAI Self-Harm Intent Image score', + description: `OpenAI's model that detects self-harm intent, which is defined as content where the speaker expresses that they are engaging or intend to engage in acts of self-harm, such as suicide, cutting, and eating disorders. Scored against the image. + + This model produces a confidence score between 0 and 1, indicating the model's confidence that the image expresses self-harm intent.`, + modelName: 'self-harm/intent', +}); + +export default OpenAiSelfHarmIntentImageSignal; diff --git a/server/services/signalsService/signals/third_party_signals/open_ai/moderation/OpenAiSelfHarmIntentTextSignal.ts b/server/services/signalsService/signals/third_party_signals/open_ai/moderation/OpenAiSelfHarmIntentTextSignal.ts new file mode 100644 index 000000000..62acec33f --- /dev/null +++ b/server/services/signalsService/signals/third_party_signals/open_ai/moderation/OpenAiSelfHarmIntentTextSignal.ts @@ -0,0 +1,19 @@ +import { SignalType } from '../../../../types/SignalType.js'; +import { makeOpenAiTextModerationSignal } from './openAiModerationSignalFactory.js'; + +/** + * OpenAI text-moderation signal scoring whether text expresses the speaker's + * intent to engage in acts of self-harm (suicide, cutting, eating disorders, + * etc.). Routes through omni-moderation-latest and returns the + * `self-harm/intent` category score (0..1). + */ +const OpenAiSelfHarmIntentTextSignal = makeOpenAiTextModerationSignal({ + type: SignalType.OPEN_AI_SELF_HARM_INTENT_TEXT_MODEL, + displayName: 'OpenAI Self-Harm Intent Text score', + description: `OpenAI's model that detects self-harm intent, which is defined as content where the speaker expresses that they are engaging or intend to engage in acts of self-harm, such as suicide, cutting, and eating disorders. + + This model produces a confidence score between 0 and 1, indicating the model's confidence that the content expresses self-harm intent.`, + modelName: 'self-harm/intent', +}); + +export default OpenAiSelfHarmIntentTextSignal; diff --git a/server/services/signalsService/signals/third_party_signals/open_ai/moderation/OpenAiSexualImageSignal.ts b/server/services/signalsService/signals/third_party_signals/open_ai/moderation/OpenAiSexualImageSignal.ts index f2bdee747..22067d4e2 100644 --- a/server/services/signalsService/signals/third_party_signals/open_ai/moderation/OpenAiSexualImageSignal.ts +++ b/server/services/signalsService/signals/third_party_signals/open_ai/moderation/OpenAiSexualImageSignal.ts @@ -1,5 +1,5 @@ import { SignalType } from '../../../../types/SignalType.js'; -import { makeOpenAiImageModerationSignal } from './openAIModerationUtils.js'; +import { makeOpenAiImageModerationSignal } from './openAiModerationSignalFactory.js'; /** * OpenAI image-moderation signal scoring whether an image is meant to arouse diff --git a/server/services/signalsService/signals/third_party_signals/open_ai/moderation/OpenAiViolenceImageSignal.ts b/server/services/signalsService/signals/third_party_signals/open_ai/moderation/OpenAiViolenceImageSignal.ts index f251fc39f..1946d377b 100644 --- a/server/services/signalsService/signals/third_party_signals/open_ai/moderation/OpenAiViolenceImageSignal.ts +++ b/server/services/signalsService/signals/third_party_signals/open_ai/moderation/OpenAiViolenceImageSignal.ts @@ -1,5 +1,5 @@ import { SignalType } from '../../../../types/SignalType.js'; -import { makeOpenAiImageModerationSignal } from './openAIModerationUtils.js'; +import { makeOpenAiImageModerationSignal } from './openAiModerationSignalFactory.js'; /** * OpenAI image-moderation signal scoring whether an image promotes, diff --git a/server/services/signalsService/signals/third_party_signals/open_ai/moderation/openAIModerationUtils.ts b/server/services/signalsService/signals/third_party_signals/open_ai/moderation/openAIModerationUtils.ts index 0ea1e8cb2..d8b4d8a3e 100644 --- a/server/services/signalsService/signals/third_party_signals/open_ai/moderation/openAIModerationUtils.ts +++ b/server/services/signalsService/signals/third_party_signals/open_ai/moderation/openAIModerationUtils.ts @@ -12,8 +12,7 @@ import { type CachedGetCredentials } from '../../../../../signalAuthService/sign import { Integration } from '../../../../types/Integration.js'; import { type RecommendedThresholds } from '../../../../types/RecommendedThresholds.js'; import { SignalPricingStructure } from '../../../../types/SignalPricingStructure.js'; -import { type SignalType } from '../../../../types/SignalType.js'; -import SignalBase, { +import { type SignalDisabledInfo, type SignalInput, } from '../../../SignalBase.js'; @@ -24,6 +23,8 @@ export type OpenAiModelName = | 'hate' | 'hate/threatening' | 'self-harm' + | 'self-harm/instructions' + | 'self-harm/intent' | 'sexual' | 'sexual/minors' | 'violence' @@ -38,7 +39,12 @@ export type OpenAiModelName = */ export type OpenAiImageModelName = Extract< OpenAiModelName, - 'self-harm' | 'sexual' | 'violence' | 'violence/graphic' + | 'self-harm' + | 'self-harm/instructions' + | 'self-harm/intent' + | 'sexual' + | 'violence' + | 'violence/graphic' >; const OPEN_AI_MODERATION_MODEL = 'omni-moderation-latest'; @@ -285,115 +291,3 @@ export async function getOpenAiModerationScores( throw e; } } - -/** - * Factory for OpenAI image-moderation signals. All four image signals - * (`violence`, `violence/graphic`, `self-harm`, `sexual`) share identical - * boilerplate: same integration, pricing, language coverage, eligible inputs, - * etc. — the only per-signal config is the SignalType id, display name, - * description, and the model category to read. - * - * Returns a `SignalBase` subclass that the IoC container instantiates with - * `(credentials, scores)` like any other signal class, preserving the - * existing registration pattern in `instantiateBuiltInSignals.ts`. - */ -export function makeOpenAiImageModerationSignal(config: { - type: SignalType; - displayName: string; - description: string; - modelName: OpenAiImageModelName; -}) { - return class OpenAiImageModerationSignal extends SignalBase< - ScalarTypes['IMAGE'], - { scalarType: ScalarTypes['NUMBER'] } - > { - constructor( - protected readonly getOpenAiCredentials: CachedGetCredentials<'OPEN_AI'>, - protected readonly getOpenAiScores: FetchOpenAiModerationScores, - ) { - super(); - } - - override get id() { - return { type: config.type }; - } - - override get displayName() { - return config.displayName; - } - - override get description() { - return config.description; - } - - override get docsUrl() { - return openAiModerationDocsUrl(); - } - - override get integration() { - return openAiModerationIntegration(); - } - - override get pricingStructure() { - return openAiModerationPricingStructure(); - } - - override get recommendedThresholds() { - return openAiModerationRecommendedThresholds(); - } - - override get supportedLanguages() { - return openAiModerationSupportedLanguages(); - } - - override get eligibleSubcategories() { - return openAiModerationEligibleSubcategories(); - } - - override get needsActionPenalties() { - return openAiModerationNeedsActionPenalties(); - } - - override get needsMatchingValues() { - return openAiModerationNeedsMatchingValues(); - } - - override async getDisabledInfo(orgId: string) { - return openAiModerationGetDisabledInfo(orgId, this.getOpenAiCredentials); - } - - override get eligibleInputs() { - return [ScalarTypes.IMAGE]; - } - - override get outputType() { - return { scalarType: ScalarTypes.NUMBER }; - } - - // Inherits the placeholder cost convention from the existing OpenAI text - // signals (see OpenAiViolenceTextSignal etc.). Cost units are unitless - // ordering hints used by the engine to prefer cheaper signals; the - // current ~20 baseline reflects that OpenAI moderation is a paid - // remote-API call (vs. local heuristics) — not a calibrated value. - override getCost() { - return 20; - } - - override get allowedInAutomatedRules() { - return true; - } - - /** - * Fetches the omni-moderation `${config.modelName}` score for the image - * and returns it as a number between 0 and 1. - */ - async run(input: SignalInput) { - return runOpenAiModerationImageImpl( - this.getOpenAiCredentials, - input, - this.getOpenAiScores, - config.modelName, - ); - } - }; -} diff --git a/server/services/signalsService/signals/third_party_signals/open_ai/moderation/openAiModerationSignalFactory.ts b/server/services/signalsService/signals/third_party_signals/open_ai/moderation/openAiModerationSignalFactory.ts new file mode 100644 index 000000000..b6c527fef --- /dev/null +++ b/server/services/signalsService/signals/third_party_signals/open_ai/moderation/openAiModerationSignalFactory.ts @@ -0,0 +1,191 @@ +/** + * Factories for OpenAI moderation signals. Collapses the boilerplate that + * was duplicated across every per-category signal class — id, displayName, + * description, integration, pricing, languages, cost, etc. The two public + * exports (`makeOpenAiImageModerationSignal`, + * `makeOpenAiTextModerationSignal`) are thin wrappers around the private + * `makeOpenAiModerationSignal` so the class body lives in one place. + * + * The IoC container instantiates the returned class with + * `(credentials, scores)` like any other signal, preserving the existing + * registration pattern in `instantiateBuiltInSignals.ts`. + */ +import { ScalarTypes } from '@roostorg/coop-types'; + +import { type CachedGetCredentials } from '../../../../../signalAuthService/signalAuthService.js'; +import { type SignalType } from '../../../../types/SignalType.js'; +import SignalBase, { + type SignalInput, + type SignalInputType, +} from '../../../SignalBase.js'; +import { + openAiModerationDocsUrl, + openAiModerationEligibleSubcategories, + openAiModerationGetDisabledInfo, + openAiModerationIntegration, + openAiModerationNeedsActionPenalties, + openAiModerationNeedsMatchingValues, + openAiModerationPricingStructure, + openAiModerationRecommendedThresholds, + openAiModerationSupportedLanguages, + runOpenAiModerationImageImpl, + runOpenAiModerationImpl, + type FetchOpenAiModerationScores, + type OpenAiImageModelName, + type OpenAiModelName, +} from './openAIModerationUtils.js'; + +type SignalRunResult = { + score: number; + outputType: { scalarType: ScalarTypes['NUMBER'] }; +}; + +type ModerationMode = { + /** ScalarType key returned by `eligibleInputs` (e.g. `ScalarTypes.IMAGE`). */ + inputScalar: InputScalar; + /** Routes to either `runOpenAiModerationImpl` or `runOpenAiModerationImageImpl`. */ + runImpl: ( + getOpenAiCredentials: CachedGetCredentials<'OPEN_AI'>, + input: SignalInput, + getOpenAiScores: FetchOpenAiModerationScores, + modelName: ModelName, + ) => Promise; +}; + +type SignalConfig = { + type: SignalType; + displayName: string; + description: string; + modelName: ModelName; +}; + +function makeOpenAiModerationSignal< + InputScalar extends SignalInputType, + ModelName, +>(config: SignalConfig & ModerationMode) { + return class OpenAiModerationSignal extends SignalBase< + InputScalar, + { scalarType: ScalarTypes['NUMBER'] } + > { + constructor( + protected readonly getOpenAiCredentials: CachedGetCredentials<'OPEN_AI'>, + protected readonly getOpenAiScores: FetchOpenAiModerationScores, + ) { + super(); + } + + override get id() { + return { type: config.type }; + } + + override get displayName() { + return config.displayName; + } + + override get description() { + return config.description; + } + + override get docsUrl() { + return openAiModerationDocsUrl(); + } + + override get integration() { + return openAiModerationIntegration(); + } + + override get pricingStructure() { + return openAiModerationPricingStructure(); + } + + override get recommendedThresholds() { + return openAiModerationRecommendedThresholds(); + } + + override get supportedLanguages() { + return openAiModerationSupportedLanguages(); + } + + override get eligibleSubcategories() { + return openAiModerationEligibleSubcategories(); + } + + override get needsActionPenalties() { + return openAiModerationNeedsActionPenalties(); + } + + override get needsMatchingValues() { + return openAiModerationNeedsMatchingValues(); + } + + override async getDisabledInfo(orgId: string) { + return openAiModerationGetDisabledInfo(orgId, this.getOpenAiCredentials); + } + + override get eligibleInputs() { + return [config.inputScalar]; + } + + override get outputType() { + return { scalarType: ScalarTypes.NUMBER }; + } + + // Matches the legacy OpenAI text-signal baseline (`getCost: 20`). Values + // here are unitless ordering hints used by the rule engine to prefer + // cheaper signals — they're not calibrated against latency or $ across + // the codebase. Re-calibrating is tracked separately; perpetuating the + // existing baseline keeps new signals consistent with their text peers. + override getCost() { + return 20; + } + + override get allowedInAutomatedRules() { + return true; + } + + /** + * Fetches the omni-moderation `${config.modelName}` score for the input + * and returns it as a number between 0 and 1. + */ + async run(input: SignalInput) { + return config.runImpl( + this.getOpenAiCredentials, + input, + this.getOpenAiScores, + config.modelName, + ); + } + }; +} + +/** + * Factory for image-input OpenAI moderation signals. The `modelName` + * parameter is constrained to {@link OpenAiImageModelName} so callers can't + * request a category OpenAI only scores against text. + */ +export function makeOpenAiImageModerationSignal( + config: SignalConfig, +) { + return makeOpenAiModerationSignal( + { + ...config, + inputScalar: ScalarTypes.IMAGE, + runImpl: runOpenAiModerationImageImpl, + }, + ); +} + +/** + * Factory for text-input OpenAI moderation signals. Accepts the full + * {@link OpenAiModelName} since omni-moderation scores every category on + * text. + */ +export function makeOpenAiTextModerationSignal( + config: SignalConfig, +) { + return makeOpenAiModerationSignal({ + ...config, + inputScalar: ScalarTypes.STRING, + runImpl: runOpenAiModerationImpl, + }); +} diff --git a/server/services/signalsService/types/SignalArgsByType.ts b/server/services/signalsService/types/SignalArgsByType.ts index 44c0b0d0c..83ac4cec4 100644 --- a/server/services/signalsService/types/SignalArgsByType.ts +++ b/server/services/signalsService/types/SignalArgsByType.ts @@ -29,6 +29,10 @@ export type SignalArgsByType = Satisfies< [SignalType.OPEN_AI_HATE_TEXT_MODEL]: undefined; [SignalType.OPEN_AI_HATE_THREATENING_TEXT_MODEL]: undefined; [SignalType.OPEN_AI_SELF_HARM_IMAGE_MODEL]: undefined; + [SignalType.OPEN_AI_SELF_HARM_INSTRUCTIONS_IMAGE_MODEL]: undefined; + [SignalType.OPEN_AI_SELF_HARM_INSTRUCTIONS_TEXT_MODEL]: undefined; + [SignalType.OPEN_AI_SELF_HARM_INTENT_IMAGE_MODEL]: undefined; + [SignalType.OPEN_AI_SELF_HARM_INTENT_TEXT_MODEL]: undefined; [SignalType.OPEN_AI_SELF_HARM_TEXT_MODEL]: undefined; [SignalType.OPEN_AI_SEXUAL_IMAGE_MODEL]: undefined; [SignalType.OPEN_AI_SEXUAL_MINORS_TEXT_MODEL]: undefined; @@ -67,6 +71,10 @@ export type RuntimeSignalArgsByType = Satisfies< [SignalType.OPEN_AI_HATE_TEXT_MODEL]: undefined; [SignalType.OPEN_AI_HATE_THREATENING_TEXT_MODEL]: undefined; [SignalType.OPEN_AI_SELF_HARM_IMAGE_MODEL]: undefined; + [SignalType.OPEN_AI_SELF_HARM_INSTRUCTIONS_IMAGE_MODEL]: undefined; + [SignalType.OPEN_AI_SELF_HARM_INSTRUCTIONS_TEXT_MODEL]: undefined; + [SignalType.OPEN_AI_SELF_HARM_INTENT_IMAGE_MODEL]: undefined; + [SignalType.OPEN_AI_SELF_HARM_INTENT_TEXT_MODEL]: undefined; [SignalType.OPEN_AI_SELF_HARM_TEXT_MODEL]: undefined; [SignalType.OPEN_AI_SEXUAL_IMAGE_MODEL]: undefined; [SignalType.OPEN_AI_SEXUAL_MINORS_TEXT_MODEL]: undefined; diff --git a/server/services/signalsService/types/SignalType.ts b/server/services/signalsService/types/SignalType.ts index 2c3c8074d..f6570ecb9 100644 --- a/server/services/signalsService/types/SignalType.ts +++ b/server/services/signalsService/types/SignalType.ts @@ -44,6 +44,10 @@ export const BuiltInThirdPartySignalType = makeEnumLike([ 'OPEN_AI_HATE_TEXT_MODEL', 'OPEN_AI_HATE_THREATENING_TEXT_MODEL', 'OPEN_AI_SELF_HARM_IMAGE_MODEL', + 'OPEN_AI_SELF_HARM_INSTRUCTIONS_IMAGE_MODEL', + 'OPEN_AI_SELF_HARM_INSTRUCTIONS_TEXT_MODEL', + 'OPEN_AI_SELF_HARM_INTENT_IMAGE_MODEL', + 'OPEN_AI_SELF_HARM_INTENT_TEXT_MODEL', 'OPEN_AI_SELF_HARM_TEXT_MODEL', 'OPEN_AI_SEXUAL_IMAGE_MODEL', 'OPEN_AI_SEXUAL_MINORS_TEXT_MODEL', @@ -98,6 +102,10 @@ export function integrationForSignalType(type: SignalType | string) { case 'OPEN_AI_HATE_TEXT_MODEL': case 'OPEN_AI_HATE_THREATENING_TEXT_MODEL': case 'OPEN_AI_SELF_HARM_IMAGE_MODEL': + case 'OPEN_AI_SELF_HARM_INSTRUCTIONS_IMAGE_MODEL': + case 'OPEN_AI_SELF_HARM_INSTRUCTIONS_TEXT_MODEL': + case 'OPEN_AI_SELF_HARM_INTENT_IMAGE_MODEL': + case 'OPEN_AI_SELF_HARM_INTENT_TEXT_MODEL': case 'OPEN_AI_SELF_HARM_TEXT_MODEL': case 'OPEN_AI_SEXUAL_IMAGE_MODEL': case 'OPEN_AI_SEXUAL_MINORS_TEXT_MODEL': diff --git a/server/services/userManagementService/dbTypes.ts b/server/services/userManagementService/dbTypes.ts index 707d31304..97753f36f 100644 --- a/server/services/userManagementService/dbTypes.ts +++ b/server/services/userManagementService/dbTypes.ts @@ -27,6 +27,7 @@ export type UserManagementPg = { user_id: string; moderator_safety_mute_video: boolean | null; moderator_safety_grayscale: boolean | null; + moderator_safety_sepia: boolean | null; moderator_safety_blur_level: number | null; mrt_chart_configurations: MrtChartConfig[] | null; }; @@ -45,6 +46,11 @@ export type UserManagementPg = { boolean | undefined, boolean | undefined >; + moderator_safety_sepia: ColumnType< + boolean, + boolean | undefined, + boolean | undefined + >; moderator_safety_blur_level: ColumnType< number, number | undefined, diff --git a/server/services/userManagementService/userManagementService.ts b/server/services/userManagementService/userManagementService.ts index b04b6e2e4..fa617c841 100644 --- a/server/services/userManagementService/userManagementService.ts +++ b/server/services/userManagementService/userManagementService.ts @@ -1,3 +1,4 @@ +/* eslint-disable max-lines */ import crypto from 'node:crypto'; import { type Kysely } from 'kysely'; @@ -54,15 +55,17 @@ class UserManagementService { if ( row && - row.moderator_safety_grayscale && - row.moderator_safety_blur_level && - row.moderator_safety_mute_video + row.moderator_safety_grayscale != null && + row.moderator_safety_blur_level != null && + row.moderator_safety_mute_video != null && + row.moderator_safety_sepia != null ) { // If all the user's settings have been set, just return them return { moderatorSafetyGrayscale: row.moderator_safety_grayscale, moderatorSafetyBlurLevel: row.moderator_safety_blur_level, moderatorSafetyMuteVideo: row.moderator_safety_mute_video, + moderatorSafetySepia: row.moderator_safety_sepia, mrtChartConfigurations: row.mrt_chart_configurations ?? [], }; } @@ -73,6 +76,8 @@ class UserManagementService { return { moderatorSafetyGrayscale: row?.moderator_safety_grayscale ?? orgDefaults.moderatorSafetyGrayscale, + moderatorSafetySepia: + row?.moderator_safety_sepia ?? orgDefaults.moderatorSafetySepia, moderatorSafetyBlurLevel: row?.moderator_safety_blur_level ?? orgDefaults.moderatorSafetyBlurLevel, @@ -163,6 +168,7 @@ class UserManagementService { moderatorSafetyMuteVideo: boolean; moderatorSafetyGrayscale: boolean; moderatorSafetyBlurLevel: number; + moderatorSafetySepia: boolean; }; mrtChartConfigurations?: readonly MrtChartConfig[]; }; @@ -176,6 +182,8 @@ class UserManagementService { ? { moderator_safety_grayscale: moderatorSafetySettings.moderatorSafetyGrayscale, + moderator_safety_sepia: + moderatorSafetySettings.moderatorSafetySepia, moderator_safety_blur_level: moderatorSafetySettings.moderatorSafetyBlurLevel, moderator_safety_mute_video: @@ -221,6 +229,7 @@ class UserManagementService { return { moderatorSafetyGrayscale: row.moderator_safety_grayscale, + moderatorSafetySepia: row.moderator_safety_sepia, moderatorSafetyBlurLevel: row.moderator_safety_blur_level, moderatorSafetyMuteVideo: row.moderator_safety_mute_video, }; @@ -231,12 +240,14 @@ class UserManagementService { // If you don't provide these values, they will be set to the default values // configured on the pg table definition moderatorSafetyGrayscale?: boolean; + moderatorSafetySepia?: boolean; moderatorSafetyBlurLevel?: number; moderatorSafetyMuteVideo?: boolean; }) { const { orgId, moderatorSafetyGrayscale, + moderatorSafetySepia, moderatorSafetyBlurLevel, moderatorSafetyMuteVideo, } = opts; @@ -244,6 +255,9 @@ class UserManagementService { ...(moderatorSafetyGrayscale !== undefined ? { moderator_safety_grayscale: moderatorSafetyGrayscale } : {}), + ...(moderatorSafetySepia !== undefined + ? { moderator_safety_sepia: moderatorSafetySepia } + : {}), ...(moderatorSafetyBlurLevel !== undefined ? { moderator_safety_blur_level: moderatorSafetyBlurLevel } : {}), @@ -258,6 +272,7 @@ class UserManagementService { { org_id: orgId, moderator_safety_grayscale: moderatorSafetyGrayscale, + moderator_safety_sepia: moderatorSafetySepia, moderator_safety_blur_level: moderatorSafetyBlurLevel, moderator_safety_mute_video: moderatorSafetyMuteVideo, }, diff --git a/server/services/userStrikeService/userStrikeService.test.ts b/server/services/userStrikeService/userStrikeService.test.ts index 3dbb5cbab..c82aeddb3 100644 --- a/server/services/userStrikeService/userStrikeService.test.ts +++ b/server/services/userStrikeService/userStrikeService.test.ts @@ -1,237 +1,241 @@ import { uid } from 'uid'; -import getBottle, { type Dependencies } from '../../iocContainer/index.js'; -import { type UserStrikeService } from './index.js'; +import { makeTransactionalTestWithFixture } from '../../test/harness/transactionalTest.js'; -describe('Item Investigation Service', () => { - let container: Dependencies; - let userStrikeService: UserStrikeService; +describe('User Strike Service', () => { + const testWithStrikes = makeTransactionalTestWithFixture( + async ({ deps }) => ({ + userStrikeService: deps.UserStrikeService, + }), + ); - beforeAll(async () => { - // The mutation should be ok here since this is initial setup in a - // beforeAll; it doesn't involve reset state for each test in the suite + testWithStrikes( + 'Should properly calculate strike counts for a given user', + async ({ userStrikeService }) => { + const fakeUserId = { id: uid(), typeId: uid() }; + const fakeOrgId = uid(); + await userStrikeService.applyUserStrike( + fakeOrgId, + fakeUserId, + 'fakePolicyId', + 1, + ); + const strikeCount1 = await userStrikeService.getUserStrikeValue( + fakeOrgId, + fakeUserId, + ); + expect(strikeCount1).toEqual(1); + await userStrikeService.applyUserStrike( + fakeOrgId, + fakeUserId, + 'fakePolicyId1', + 1, + ); + const strikeCount2 = await userStrikeService.getUserStrikeValue( + fakeOrgId, + fakeUserId, + ); + expect(strikeCount2).toEqual(2); + await userStrikeService.applyUserStrike( + fakeOrgId, + fakeUserId, + 'fakePolicyId2', + 10, + ); + const strikeCount3 = await userStrikeService.getUserStrikeValue( + fakeOrgId, + fakeUserId, + ); + expect(strikeCount3).toEqual(12); + }, + ); - ({ container } = await getBottle()); - userStrikeService = container.UserStrikeService; - }); - afterAll(async () => { - await container.closeSharedResourcesForShutdown(); - }); - - test('Should properly calculate strike counts for a given user', async () => { - const fakeUserId = { id: uid(), typeId: uid() }; - const fakeOrgId = uid(); - await userStrikeService.applyUserStrike( - fakeOrgId, - fakeUserId, - 'fakePolicyId', - 1, - ); - const strikeCount1 = await userStrikeService.getUserStrikeValue( - fakeOrgId, - fakeUserId, - ); - expect(strikeCount1).toEqual(1); - await userStrikeService.applyUserStrike( - fakeOrgId, - fakeUserId, - 'fakePolicyId1', - 1, - ); - const strikeCount2 = await userStrikeService.getUserStrikeValue( - fakeOrgId, - fakeUserId, - ); - expect(strikeCount2).toEqual(2); - await userStrikeService.applyUserStrike( - fakeOrgId, - fakeUserId, - 'fakePolicyId2', - 10, - ); - const strikeCount3 = await userStrikeService.getUserStrikeValue( - fakeOrgId, - fakeUserId, - ); - expect(strikeCount3).toEqual(12); - }); - - test('Should only apply strike for most severe policy violation', async () => { - const testActions = [ - { - orgId: 'fakeOrgId', - action: { - id: 'fakeActionId1', - name: 'testAction1', - description: null, - applyUserStrikes: true, + testWithStrikes( + 'Should only apply strike for most severe policy violation', + async ({ userStrikeService }) => { + const testActions = [ + { orgId: 'fakeOrgId', - penalty: 'NONE' as const, - callbackUrl: 'fakeCallbackUrl1', - callbackUrlHeaders: null, - callbackUrlBody: null, - customMrtApiParams: null, - actionType: 'CUSTOM_ACTION' as const, - }, - targetItem: { itemId: 'fakeItemId1', itemType: 'fakeItemType1' }, - matchingRules: undefined, - ruleEnvironment: undefined, - policies: [ - { - id: 'fakePolicyId1', - name: 'testPolicy1', - userStrikeCount: 1, - penalty: 'LOW' as const, + action: { + id: 'fakeActionId1', + name: 'testAction1', + description: null, + applyUserStrikes: true, + orgId: 'fakeOrgId', + penalty: 'NONE' as const, + callbackUrl: 'fakeCallbackUrl1', + callbackUrlHeaders: null, + callbackUrlBody: null, + customMrtApiParams: null, + actionType: 'CUSTOM_ACTION' as const, }, - { - id: 'severePolicyId', - name: 'testPolicy2', - userStrikeCount: 2, - penalty: 'LOW' as const, - }, - ], - }, - ]; - const mostSeverePolicyViolation = - userStrikeService.findMostSeverePolicyViolationFromActions(testActions); - if (mostSeverePolicyViolation === undefined) { - throw new Error('mostSeverePolicyViolation is undefined'); - } - expect(mostSeverePolicyViolation.id).toEqual('severePolicyId'); - }); - test('findMostSeverePolicyViolationFromActions should return undefined if no actions apply user strikes', async () => { - const testActions = [ - { - orgId: 'fakeOrgId', - action: { - id: 'fakeActionId1', - name: 'testAction1', - description: null, - applyUserStrikes: false, - orgId: 'fakeOrgId', - penalty: 'NONE' as const, - callbackUrl: 'fakeCallbackUrl1', - callbackUrlHeaders: null, - callbackUrlBody: null, - customMrtApiParams: null, - actionType: 'CUSTOM_ACTION' as const, + targetItem: { itemId: 'fakeItemId1', itemType: 'fakeItemType1' }, + matchingRules: undefined, + ruleEnvironment: undefined, + policies: [ + { + id: 'fakePolicyId1', + name: 'testPolicy1', + userStrikeCount: 1, + penalty: 'LOW' as const, + }, + { + id: 'severePolicyId', + name: 'testPolicy2', + userStrikeCount: 2, + penalty: 'LOW' as const, + }, + ], }, - targetItem: { itemId: 'fakeItemId1', itemType: 'fakeItemType1' }, - matchingRules: undefined, - ruleEnvironment: undefined, - policies: [ - { - id: 'fakePolicyId1', - name: 'testPolicy1', - userStrikeCount: 1, - penalty: 'LOW' as const, - }, - { - id: 'severePolicyId', - name: 'testPolicy2', - userStrikeCount: 2, - penalty: 'LOW' as const, - }, - ], - }, - { - orgId: 'fakeOrgId', - action: { - id: 'fakeActionId1', - name: 'testAction1', - description: null, - applyUserStrikes: false, + ]; + const mostSeverePolicyViolation = + userStrikeService.findMostSeverePolicyViolationFromActions(testActions); + if (mostSeverePolicyViolation === undefined) { + throw new Error('mostSeverePolicyViolation is undefined'); + } + expect(mostSeverePolicyViolation.id).toEqual('severePolicyId'); + }, + ); + + testWithStrikes( + 'findMostSeverePolicyViolationFromActions should return undefined if no actions apply user strikes', + async ({ userStrikeService }) => { + const testActions = [ + { orgId: 'fakeOrgId', - penalty: 'NONE' as const, - callbackUrl: 'fakeCallbackUrl1', - callbackUrlHeaders: null, - callbackUrlBody: null, - customMrtApiParams: null, - actionType: 'CUSTOM_ACTION' as const, - }, - targetItem: { itemId: 'fakeItemId1', itemType: 'fakeItemType1' }, - matchingRules: undefined, - ruleEnvironment: undefined, - policies: [ - { - id: 'fakePolicyId1', - name: 'testPolicy1', - userStrikeCount: 1, - penalty: 'LOW' as const, + action: { + id: 'fakeActionId1', + name: 'testAction1', + description: null, + applyUserStrikes: false, + orgId: 'fakeOrgId', + penalty: 'NONE' as const, + callbackUrl: 'fakeCallbackUrl1', + callbackUrlHeaders: null, + callbackUrlBody: null, + customMrtApiParams: null, + actionType: 'CUSTOM_ACTION' as const, }, - { - id: 'severePolicyId', - name: 'testPolicy2', - userStrikeCount: 2, - penalty: 'LOW' as const, + targetItem: { itemId: 'fakeItemId1', itemType: 'fakeItemType1' }, + matchingRules: undefined, + ruleEnvironment: undefined, + policies: [ + { + id: 'fakePolicyId1', + name: 'testPolicy1', + userStrikeCount: 1, + penalty: 'LOW' as const, + }, + { + id: 'severePolicyId', + name: 'testPolicy2', + userStrikeCount: 2, + penalty: 'LOW' as const, + }, + ], + }, + { + orgId: 'fakeOrgId', + action: { + id: 'fakeActionId1', + name: 'testAction1', + description: null, + applyUserStrikes: false, + orgId: 'fakeOrgId', + penalty: 'NONE' as const, + callbackUrl: 'fakeCallbackUrl1', + callbackUrlHeaders: null, + callbackUrlBody: null, + customMrtApiParams: null, + actionType: 'CUSTOM_ACTION' as const, }, - ], - }, - ]; - const mostSeverePolicyViolation = - userStrikeService.findMostSeverePolicyViolationFromActions(testActions); - expect(mostSeverePolicyViolation).toBeUndefined(); - }); + targetItem: { itemId: 'fakeItemId1', itemType: 'fakeItemType1' }, + matchingRules: undefined, + ruleEnvironment: undefined, + policies: [ + { + id: 'fakePolicyId1', + name: 'testPolicy1', + userStrikeCount: 1, + penalty: 'LOW' as const, + }, + { + id: 'severePolicyId', + name: 'testPolicy2', + userStrikeCount: 2, + penalty: 'LOW' as const, + }, + ], + }, + ]; + const mostSeverePolicyViolation = + userStrikeService.findMostSeverePolicyViolationFromActions(testActions); + expect(mostSeverePolicyViolation).toBeUndefined(); + }, + ); - test('Should properly calculate strike values using getAllUserStrikeCountsForOrg', async () => { - const fakeTypeId = uid(); - const fakeUserId1 = { id: uid(), typeId: fakeTypeId }; - const fakeUserId2 = { id: uid(), typeId: fakeTypeId }; - const fakeUserId3 = { id: uid(), typeId: fakeTypeId }; - const fakeOrgId = uid(); - // 1 strike for user 1 - await userStrikeService.applyUserStrike( - fakeOrgId, - fakeUserId1, - 'fakePolicyId', - 1, - ); - // 2 strikes for user 2 - await userStrikeService.applyUserStrike( - fakeOrgId, - fakeUserId2, - 'fakePolicyId', - 1, - ); - await userStrikeService.applyUserStrike( - fakeOrgId, - fakeUserId2, - 'fakePolicyId1', - 1, - ); - // 3 strikes for user 3 - await userStrikeService.applyUserStrike( - fakeOrgId, - fakeUserId3, - 'fakePolicyId1', - 1, - ); - await userStrikeService.applyUserStrike( - fakeOrgId, - fakeUserId3, - 'fakePolicyId2', - 1, - ); - await userStrikeService.applyUserStrike( - fakeOrgId, - fakeUserId3, - 'fakePolicyId3', - 1, - ); + testWithStrikes( + 'Should properly calculate strike values using getAllUserStrikeCountsForOrg', + async ({ userStrikeService }) => { + const fakeTypeId = uid(); + const fakeUserId1 = { id: uid(), typeId: fakeTypeId }; + const fakeUserId2 = { id: uid(), typeId: fakeTypeId }; + const fakeUserId3 = { id: uid(), typeId: fakeTypeId }; + const fakeOrgId = uid(); + // 1 strike for user 1 + await userStrikeService.applyUserStrike( + fakeOrgId, + fakeUserId1, + 'fakePolicyId', + 1, + ); + // 2 strikes for user 2 + await userStrikeService.applyUserStrike( + fakeOrgId, + fakeUserId2, + 'fakePolicyId', + 1, + ); + await userStrikeService.applyUserStrike( + fakeOrgId, + fakeUserId2, + 'fakePolicyId1', + 1, + ); + // 3 strikes for user 3 + await userStrikeService.applyUserStrike( + fakeOrgId, + fakeUserId3, + 'fakePolicyId1', + 1, + ); + await userStrikeService.applyUserStrike( + fakeOrgId, + fakeUserId3, + 'fakePolicyId2', + 1, + ); + await userStrikeService.applyUserStrike( + fakeOrgId, + fakeUserId3, + 'fakePolicyId3', + 1, + ); - const userStrikesForOrg = - await userStrikeService.getAllUserStrikeCountsForOrg(fakeOrgId); - const user1 = userStrikesForOrg.find( - (it) => it.user_identifier.id === fakeUserId1.id, - ); - expect(user1?.strike_count).toEqual(1); - const user2 = userStrikesForOrg.find( - (it) => it.user_identifier.id === fakeUserId2.id, - ); - expect(user2?.strike_count).toEqual(2); - const user3 = userStrikesForOrg.find( - (it) => it.user_identifier.id === fakeUserId3.id, - ); - expect(user3?.strike_count).toEqual(3); - }); + const userStrikesForOrg = + await userStrikeService.getAllUserStrikeCountsForOrg(fakeOrgId); + const user1 = userStrikesForOrg.find( + (it) => it.user_identifier.id === fakeUserId1.id, + ); + expect(user1?.strike_count).toEqual(1); + const user2 = userStrikesForOrg.find( + (it) => it.user_identifier.id === fakeUserId2.id, + ); + expect(user2?.strike_count).toEqual(2); + const user3 = userStrikesForOrg.find( + (it) => it.user_identifier.id === fakeUserId3.id, + ); + expect(user3?.strike_count).toEqual(3); + }, + ); }); diff --git a/server/services/userStrikeService/userStrikeService.ts b/server/services/userStrikeService/userStrikeService.ts index 5307abd8c..49e687e7d 100644 --- a/server/services/userStrikeService/userStrikeService.ts +++ b/server/services/userStrikeService/userStrikeService.ts @@ -33,6 +33,7 @@ export class UserStrikeService { private readonly getUserStrikeTTLinDays: Dependencies['getUserStrikeTTLInDaysEventuallyConsistent'], private readonly actionExecutionsAdapter: IActionExecutionsAdapter, private readonly publishActions: Dependencies['ActionPublisher']['publishActions'], + private readonly enabled: boolean = true, ) { this.scylla = scylla; this.moderationConfigService = moderationConfigService; @@ -97,6 +98,10 @@ export class UserStrikeService { actorEmail?: string; }, ) { + if (!this.enabled) { + return; + } + const targetUser = getUserFromActionTargetItem(executionContext.targetItem); const mostSeverePolicy = this.findMostSeverePolicyViolationFromActions(triggeredActions); diff --git a/server/storage/dataWarehouse/DataWarehouseFactory.ts b/server/storage/dataWarehouse/DataWarehouseFactory.ts index fd2799a2f..5f85fd1ba 100644 --- a/server/storage/dataWarehouse/DataWarehouseFactory.ts +++ b/server/storage/dataWarehouse/DataWarehouseFactory.ts @@ -325,8 +325,7 @@ export class DataWarehouseFactory { password: process.env.CLICKHOUSE_PASSWORD ?? '', database: process.env.CLICKHOUSE_DATABASE ?? 'default', protocol: (process.env.CLICKHOUSE_PROTOCOL ?? 'http') as - | 'http' - | 'https', + 'http' | 'https', }, pool: { max: process.env.CLICKHOUSE_POOL_SIZE diff --git a/server/storage/dataWarehouse/warehouseSchema.ts b/server/storage/dataWarehouse/warehouseSchema.ts index edc88b1e3..023bbc61f 100644 --- a/server/storage/dataWarehouse/warehouseSchema.ts +++ b/server/storage/dataWarehouse/warehouseSchema.ts @@ -43,7 +43,9 @@ export type { // Each table is registered both as `PUBLIC.TABLE_NAME` and just `TABLE_NAME`, // so queries can reference it with or without the schema prefix. export type DataWarehousePublicSchema = UnprefixedPublicTables & { - [K in keyof UnprefixedPublicTables as `PUBLIC.${K}`]: UnprefixedPublicTables[K]; + [ + K in keyof UnprefixedPublicTables as `PUBLIC.${K}` + ]: UnprefixedPublicTables[K]; }; /** Convert a warehouse driver date to a standard Date object. */ @@ -109,14 +111,9 @@ export type UnsafeBulkWriteType< | ManualReviewToolServiceWarehouseSchema[keyof ManualReviewToolServiceWarehouseSchema] | ReportingServiceWarehouseSchema[keyof ReportingServiceWarehouseSchema], > = { - [K in Exclude< - keyof T & string, - NullableKeysOf - > as Lowercase]: T[K] extends ColumnType< - unknown, - infer InsertType, - unknown - > + [ + K in Exclude> as Lowercase + ]: T[K] extends ColumnType ? InsertType : T[K]; } & { diff --git a/server/test/arbitraries/ContentType.ts b/server/test/arbitraries/ContentType.ts index 70fb29b78..7f78774d6 100644 --- a/server/test/arbitraries/ContentType.ts +++ b/server/test/arbitraries/ContentType.ts @@ -162,18 +162,18 @@ export const MapFieldArbitrary = fc fc.string(), fc.boolean(), ) - .map< - Field - >(([valueScalarType, keyScalarType, name, required]) => ({ - name, - required, - type: ContainerTypes.MAP, - container: { - containerType: ContainerTypes.MAP, - keyScalarType, - valueScalarType, - }, - })); + .map>( + ([valueScalarType, keyScalarType, name, required]) => ({ + name, + required, + type: ContainerTypes.MAP, + container: { + containerType: ContainerTypes.MAP, + keyScalarType, + valueScalarType, + }, + }), + ); export const MapFieldWithValueArbitrary = MapFieldArbitrary.chain((field) => fc.tuple( diff --git a/server/test/fixtureHelpers/makeDummyMrtJobPayload.ts b/server/test/fixtureHelpers/makeDummyMrtJobPayload.ts new file mode 100644 index 000000000..d4853a381 --- /dev/null +++ b/server/test/fixtureHelpers/makeDummyMrtJobPayload.ts @@ -0,0 +1,41 @@ +import { v1 as uuidv1 } from 'uuid'; + +import { + makeSubmissionId, + type ItemSubmissionWithTypeIdentifier, + type NormalizedItemData, +} from '../../services/itemProcessingService/index.js'; +import { type ReportHistory } from '../../services/manualReviewToolService/index.js'; +import { instantiateOpaqueType } from '../../utils/typescript-types.js'; + +/** + * Builds a minimal DEFAULT-kind manual review job payload suitable for + * QueueOperations.addJob in tests. Every id is freshly generated, so each + * call produces a distinct item (and therefore a distinct Bull job). + */ +export default function makeDummyMrtJobPayload(opts?: { createdAt?: Date }) { + return { + createdAt: opts?.createdAt ?? new Date(), + policyIds: [] as string[], + payload: { + kind: 'DEFAULT', + reportHistory: [] as ReportHistory, + item: instantiateOpaqueType({ + submissionId: makeSubmissionId(), + submissionTime: new Date(), + data: instantiateOpaqueType({}), + itemTypeIdentifier: { + id: uuidv1(), + version: new Date().toISOString(), + schemaVariant: 'original', + }, + creator: { + id: uuidv1(), + typeId: uuidv1(), + }, + itemId: uuidv1(), + }), + enqueueSourceInfo: { kind: 'REPORT' }, + }, + } as const; +} diff --git a/server/test/harness/transactionalPgPool.integ.test.ts b/server/test/harness/transactionalPgPool.integ.test.ts index 9009b6b04..d8b299b72 100644 --- a/server/test/harness/transactionalPgPool.integ.test.ts +++ b/server/test/harness/transactionalPgPool.integ.test.ts @@ -4,8 +4,6 @@ * Proves that `createTransactionalTestDb` lets us wrap a whole test in a single * Postgres transaction that is rolled back at the end. */ -import 'dotenv/config'; - import { Kysely, PostgresDialect, sql } from 'kysely'; import pg from 'pg'; diff --git a/server/test/integ/ncmec-submission.integ.test.ts b/server/test/integ/ncmec-submission.integ.test.ts new file mode 100644 index 000000000..fc797948b --- /dev/null +++ b/server/test/integ/ncmec-submission.integ.test.ts @@ -0,0 +1,237 @@ +import { uid } from 'uid'; +import { Headers } from 'undici'; + +import { + NCMECFileAnnotation, + NCMECIncidentType, + NCMECIndustryClassification, + NcmecReporting, + type NCMECReportParams, +} from '../../services/ncmecService/index.js'; +import { + type CoopRequestQuery, + type CoopResponse, + type FetchHTTP, + type HandleResponseBody, +} from '../../services/networkingService/index.js'; +import createOrg from '../fixtureHelpers/createOrg.js'; +import { makeTransactionalTestWithFixture } from '../harness/transactionalTest.js'; + +const MEDIA_URL = 'https://cdn.example/sample.jpg'; +const PRESERVATION_URL = 'https://preserve.example/req'; + +/** Shape of one recorded outgoing fetchHTTP call. */ +type RecordedCall = { + url: string; + method: string; + body: unknown; + headers?: Record>; +}; + +/** Records every outgoing fetchHTTP call and returns canned CyberTip + * responses. */ +function makeStubFetchHTTP( + reportId: string, + fileId: string, +): { + fetchHTTP: FetchHTTP; + calls: RecordedCall[]; +} { + const calls: RecordedCall[] = []; + const ok = (body: unknown): CoopResponse => + // eslint-disable-next-line @typescript-eslint/consistent-type-assertions -- the stub returns a canned body through a slot typed by the caller's T. + ({ + status: 200, + ok: true, + headers: new Headers(), + body, + }) as CoopResponse; + const fetchHTTP: FetchHTTP = async ( + query: CoopRequestQuery, + ): Promise> => { + const { url, method, body, headers } = query; + // eslint-disable-next-line functional/immutable-data -- request recorder mutates by design + calls.push({ url, method, body, headers }); + + // media download for #upload + if (method === 'get') { + const stream = new ReadableStream({ + start(ctr) { + ctr.enqueue(new TextEncoder().encode('fake-media-bytes')); + ctr.close(); + }, + }); + return ok(stream); + } + // NCMEC CyberTip protocol — every XML endpoint returns responseCode=0. + // /submit, /upload, /fileinfo use `reportResponse`; /finish uses + // `reportDoneResponse`. + if ( + url.endsWith('/ispws/submit') || + url.endsWith('/ispws/upload') || + url.endsWith('/ispws/fileinfo') + ) { + const isSubmit = url.endsWith('/ispws/submit'); + const isUpload = url.endsWith('/ispws/upload'); + return ok({ + reportResponse: { + responseCode: { _text: '0' }, + ...(isSubmit ? { reportId: { _text: reportId } } : {}), + ...(isUpload ? { fileId: { _text: fileId } } : {}), + }, + }); + } + if (url.endsWith('/ispws/finish')) { + return ok({ + reportDoneResponse: { + responseCode: { _text: '0' }, + reportId: { _text: reportId }, + files: [{ fileId: { _text: fileId } }], + }, + }); + } + if (url === PRESERVATION_URL) { + return ok(undefined); + } + throw new Error(`stub fetchHTTP: unexpected request ${method} ${url}`); + }; + return { fetchHTTP, calls }; +} + +describe('NCMEC submitReport (integration)', () => { + const testWithFixture = makeTransactionalTestWithFixture(async ({ deps }) => { + const orgId = uid(); + const reportId = uid(); + const fileId = 'f1'; + + const orgFixture = await createOrg( + { + KyselyPg: deps.KyselyPg, + ModerationConfigService: deps.ModerationConfigService, + ApiKeyService: deps.ApiKeyService, + }, + orgId, + ); + + await deps.NcmecService.updateNcmecOrgSettings({ + orgId, + username: 'espuser', + password: 'esppass', + contactEmail: 'reporter@example.com', + moreInfoUrl: null, + companyTemplate: 'AcmeESP', + legalUrl: 'https://acme.example/legal', + ncmecPreservationEndpoint: PRESERVATION_URL, + ncmecAdditionalInfoEndpoint: null, + defaultNcmecQueueId: null, + defaultInternetDetailType: 'WEB_PAGE', + termsOfService: null, + contactPersonEmail: null, + contactPersonFirstName: null, + contactPersonLastName: null, + contactPersonPhone: null, + mediaReviewRequirement: 'ALL', + minMediaToReview: null, + }); + + const stub = makeStubFetchHTTP(reportId, fileId); + const ncmecReporting = new NcmecReporting( + deps.KyselyPg, + deps.KyselyPgReadReplica, + stub.fetchHTTP, + deps.SigningKeyPairService, + deps.ModerationConfigService, + deps.getItemTypeEventuallyConsistent, + deps.Tracer, + ); + + return { + orgId, + reportId, + stub, + ncmecReporting, + userItemTypeId: orgFixture.defaultUserItemType.id, + }; + }); + + testWithFixture( + 'submitReport returns SUCCESS, persists a row, and runs submit→upload→fileinfo→finish', + async ({ deps, ncmecReporting, orgId, reportId, stub, userItemTypeId }) => { + const reportedUserId = uid(); + + const reportParams: NCMECReportParams = { + orgId, + reviewerId: 'reviewer-1', + reportedUser: { + id: reportedUserId, + typeId: userItemTypeId, + displayName: 'Jane Doe', + profilePicture: 'https://cdn.example/jane.png', + ipAddress: '203.0.113.7', + email: 'jane@example.com', + }, + media: [ + { + id: 'media-1', + typeId: userItemTypeId, + url: MEDIA_URL, + createdAt: '2026-06-30T12:00:00.000Z', + industryClassification: NCMECIndustryClassification.A1, + fileAnnotations: [NCMECFileAnnotation.GENERATIVE_AI], + hashes: { + md5: 'd41d8cd98f00b204e9800998ecf8427e', + pdq: 'pdqhash', + }, + }, + ], + threads: [], + incidentType: + NCMECIncidentType[ + 'Child Pornography (possession, manufacture, and distribution)' + ], + jobId: 'job-1', + }; + + const result = await ncmecReporting.submitReport(reportParams, false); + expect(result).toBe('SUCCESS'); + + // protocol sequence — the full NCMEC submit flow + const routes = stub.calls + .filter((c) => c.url.includes('cybertip.org')) + .map((c) => c.url.replace(/^.*\/ispws/, '')); + expect(routes).toEqual(['/submit', '/upload', '/fileinfo', '/finish']); + + // preservation fired (isTest=false + endpoint set) + expect(stub.calls.some((c) => c.url === PRESERVATION_URL)).toBe(true); + + // outgoing /submit request shape — proves the field-role-resolved email, + // the incidentType, and the espIdentifier made it into the XML, and that + // #sendCyberTipRequest set a Basic Authorization header. + const submitCall = stub.calls.find( + (c) => c.url.endsWith('/ispws/submit') && typeof c.body === 'string', + ); + if (!submitCall) { + throw new Error( + 'expected a /ispws/submit request with a string body, but none was recorded', + ); + } + const submitXml = String(submitCall.body); + expect(submitXml).toContain(''); + expect(submitXml).toContain('jane@example.com'); + expect(submitCall.headers?.Authorization).toMatch(/^Basic /); + + // persisted row + const row = await deps.KyselyPg.selectFrom( + 'ncmec_reporting.ncmec_reports', + ) + .select(['report_id', 'is_test', 'report_xml']) + .where('org_id', '=', orgId) + .where('report_id', '=', reportId) + .executeTakeFirst(); + expect(row).toBeDefined(); + expect(row?.is_test).toBe(false); + expect(String(row?.report_xml)).toContain('jane@example.com'); + }, + 60_000, + ); +}); diff --git a/server/test/integ/setupIntegrationServer.ts b/server/test/integ/setupIntegrationServer.ts index 79d0bca66..fcf887546 100644 --- a/server/test/integ/setupIntegrationServer.ts +++ b/server/test/integ/setupIntegrationServer.ts @@ -6,10 +6,6 @@ * Requires the docker-compose stack from `npm run up` and migrations applied * via `npm run db:update`. */ -// Load .env before any module that reads process.env (notably the IoC -// container). The unit-test `npm test` path goes through dotenv via its -// NODE_OPTIONS; `test:integ` does not, so we do it here. -import 'dotenv/config'; import * as superTest from 'supertest'; diff --git a/server/test/stubs/getRuleAnomalyDetectionStatistics.ts b/server/test/stubs/getRuleAnomalyDetectionStatistics.ts index 1ebc36e14..f756e75dc 100644 --- a/server/test/stubs/getRuleAnomalyDetectionStatistics.ts +++ b/server/test/stubs/getRuleAnomalyDetectionStatistics.ts @@ -1,11 +1,11 @@ import { readFileSync } from 'fs'; import { dirname, join } from 'path'; -import yaml from 'js-yaml'; +import { load } from 'js-yaml'; import { type GetRuleAnomalyDetectionStatistics } from '../../services/ruleAnomalyDetectionService/index.js'; const __dirname = dirname(new URL(import.meta.url).pathname); -const tableDump = yaml.load( +const tableDump = load( // eslint-disable-next-line security/detect-non-literal-fs-filename readFileSync(join(__dirname, './rule_pass_sample_data.yaml'), 'utf-8'), ) as { diff --git a/server/utils/apiKeyMiddleware.ts b/server/utils/apiKeyMiddleware.ts index 20a96ee29..17d27fc72 100644 --- a/server/utils/apiKeyMiddleware.ts +++ b/server/utils/apiKeyMiddleware.ts @@ -20,8 +20,7 @@ export interface RequestWithOrgId { export function createApiKeyMiddleware< ReqBody extends JsonObject = JsonObject, ResBody extends ReadonlyDeep | undefined = - | ReadonlyDeep - | undefined, + ReadonlyDeep | undefined, >({ ApiKeyService, }: Pick): RequestHandlerWithBodies< diff --git a/server/utils/encoding.ts b/server/utils/encoding.ts index 2414c1e87..fef31842d 100644 --- a/server/utils/encoding.ts +++ b/server/utils/encoding.ts @@ -1,7 +1,7 @@ import stringify from 'safe-stable-stringify'; import { type Opaque } from 'type-fest'; -import { JSON } from './json-schema-types.js'; +import { type JSON } from './json-schema-types.js'; /** * This function accepts any JS string and encodes it in base64, using a UTF8 diff --git a/server/utils/json-schema-types.ts b/server/utils/json-schema-types.ts index b195fb2b9..7a1b78419 100644 --- a/server/utils/json-schema-types.ts +++ b/server/utils/json-schema-types.ts @@ -43,7 +43,7 @@ interface StringKeywords { type UncheckedJSONSchemaType = ( | // these two unions allow arbitrary unions of types - { + { anyOf: readonly UncheckedJSONSchemaType[]; } | { @@ -113,8 +113,7 @@ type UncheckedJSONSchemaType = ( // "patternProperties" and can be only used with interfaces that have string index type: JSONType<'object', IsPartial>; additionalProperties?: - | boolean - | UncheckedJSONSchemaType; + boolean | UncheckedJSONSchemaType; properties?: IsPartial extends true ? Partial> : PropertiesSchema; @@ -151,17 +150,11 @@ type UncheckedJSONSchemaType = ( export type JSONSchemaV4 = UncheckedJSONSchemaType; export type JSON = - | { [key: string]: JSON } - | JSON[] - | number - | string - | boolean - | null; + { [key: string]: JSON } | JSON[] | number | string | boolean | null; export type PropertiesSchema = { [K in keyof T]-?: - | (UncheckedJSONSchemaType & Nullable) - | { $ref: string }; + (UncheckedJSONSchemaType & Nullable) | { $ref: string }; }; export type RequiredMembers = { diff --git a/server/utils/kyselyTransactionWithRetry.ts b/server/utils/kyselyTransactionWithRetry.ts index bb4e5f3a3..fb2262258 100644 --- a/server/utils/kyselyTransactionWithRetry.ts +++ b/server/utils/kyselyTransactionWithRetry.ts @@ -32,8 +32,7 @@ export function makeKyselyTransactionWithRetry(kysely: Kysely) { ): Promise; async function transactionWithRetry( optionsOrCallback: - | TransactionWithRetryOptions - | ((trx: Transaction) => Promise), + TransactionWithRetryOptions | ((trx: Transaction) => Promise), maybeCallback?: (trx: Transaction) => Promise, ): Promise { const [options, callback] = diff --git a/server/utils/typescript-types.ts b/server/utils/typescript-types.ts index adfe91c59..e383a00ce 100644 --- a/server/utils/typescript-types.ts +++ b/server/utils/typescript-types.ts @@ -22,8 +22,7 @@ export type { * recursively transforms the keys of objects in array/tuple types. */ export type SnakeCasedPropertiesDeepWithArrays = T extends - | readonly [] - | readonly [...never[]] + readonly [] | readonly [...never[]] ? readonly [] : T extends readonly [infer U, ...infer V] ? readonly [ @@ -39,9 +38,9 @@ export type SnakeCasedPropertiesDeepWithArrays = T extends ? ReadonlyArray> : T extends object ? { - [K in keyof T as SnakeCase]: SnakeCasedPropertiesDeepWithArrays< - T[K] - >; + [ + K in keyof T as SnakeCase + ]: SnakeCasedPropertiesDeepWithArrays; } : T; diff --git a/server/workers_jobs/RetryFailedNcmecDecisionsJob.test.ts b/server/workers_jobs/RetryFailedNcmecDecisionsJob.test.ts new file mode 100644 index 000000000..48a3de45a --- /dev/null +++ b/server/workers_jobs/RetryFailedNcmecDecisionsJob.test.ts @@ -0,0 +1,277 @@ +import { v1 as uuidv1 } from 'uuid'; + +import { + makeSubmissionId, + type ItemSubmissionWithTypeIdentifier, + type NormalizedItemData, +} from '../services/itemProcessingService/index.js'; +import { type ManualReviewToolService } from '../services/manualReviewToolService/index.js'; +import { instantiateOpaqueType } from '../utils/typescript-types.js'; +import makeRetryFailedNcmecDecisionsJob from './RetryFailedNcmecDecisionsJob.js'; + +/** + * Minimal shape of a row returned by + * `ManualReviewToolService.getNcmecDecisions`. Only the fields read by + * `processDecisionRetry` are populated; everything else is omitted and the + * cast satisfies TypeScript at the injection boundary. + */ +type NcmecDecisionRow = Awaited< + ReturnType +>[number]; + +function makeNcmecDecisionRow(orgId: string): NcmecDecisionRow { + const userItemTypeId = uuidv1(); + const itemId = uuidv1(); + return { + org_id: orgId, + id: uuidv1(), + queue_id: uuidv1(), + reviewer_id: 'reviewer-1', + decision_components: [ + { + type: 'SUBMIT_NCMEC_REPORT', + reportedMedia: [], + reportedMessages: [], + incidentType: + 'Child Pornography (possession, manufacture, and distribution)', + }, + ], + job_payload: { + id: uuidv1(), + orgId, + createdAt: new Date(), + policyIds: [], + payload: { + kind: 'NCMEC', + reportHistory: [], + allMediaItems: [], + item: instantiateOpaqueType({ + submissionId: makeSubmissionId(), + submissionTime: new Date(), + // eslint-disable-next-line @typescript-eslint/consistent-type-assertions + data: {} as NormalizedItemData, + itemTypeIdentifier: { + id: userItemTypeId, + version: new Date().toISOString(), + schemaVariant: 'original', + }, + creator: { + id: itemId, + typeId: userItemTypeId, + }, + itemId, + }), + enqueueSourceInfo: { kind: 'REPORT' }, + }, + // The full `ManualReviewJob` union has many fields the retry job never + // reads; cast through `unknown` to avoid mirroring the entire type. + } as unknown as NcmecDecisionRow['job_payload'], + } as unknown as NcmecDecisionRow; +} + +/** The USER item type returned by `getItemTypeEventuallyConsistent`. Only the + * fields read by the retry job / buildSubmitReportParamsFromDecision are set. */ +function makeUserItemType(id: string) { + return { + id, + kind: 'USER' as const, + name: 'Test User Type', + description: null, + version: new Date().toISOString(), + schemaVariant: 'original' as const, + orgId: 'org-1', + isDefaultUserType: false, + schema: [ + { + name: 'displayName', + type: 'TEXT', + optional: true, + }, + ], + schemaFieldRoles: {}, + }; +} + +/** Builds the 7 dependencies injected into the retry job. Each test overrides + * only what it needs via `overrides`. Methods that should never be called in a + * given test throw, so unexpected calls surface as failures. */ +function makeDeps( + overrides: Partial<{ + submitReport: jest.Mock; + publishActions: jest.Mock; + getNCMECActionsToRunAndPolicies: jest.Mock; + decisions: NcmecDecisionRow[]; + }> = {}, +) { + const ncmecService = { + submitReport: overrides.submitReport ?? jest.fn(async () => 'SUCCESS'), + getUsersWithNcmecDecision: jest.fn(async () => []), + getNcmecErrorsForJobIds: jest.fn(async () => []), + insertOrUpdateNcmecReportError: jest.fn(async () => undefined), + getNCMECActionsToRunAndPolicies: + overrides.getNCMECActionsToRunAndPolicies ?? + jest.fn(async () => undefined), + }; + const manualReviewToolService = { + getNcmecDecisions: jest.fn(async () => overrides.decisions ?? []), + }; + const getItemTypeEventuallyConsistent = jest.fn(async () => + makeUserItemType('user-type-1'), + ); + const actionPublisher = { + publishActions: overrides.publishActions ?? jest.fn(async () => []), + }; + const moderationConfigService = { + getActions: jest.fn(async () => []), + getPolicies: jest.fn(async () => []), + }; + const userManagementService = { + getUsersForOrg: jest.fn(async () => []), + }; + return { + ncmecService, + manualReviewToolService, + getItemTypeEventuallyConsistent, + actionPublisher, + moderationConfigService, + userManagementService, + }; +} + +describe('RetryFailedNcmecDecisionsJob', () => { + const ORG_ID = 'org-1'; + + /** Snapshot of NCMEC_ENV across the suite so each test can mutate it + * freely and we restore the original value in afterEach. */ + let originalNcmecEnv: string | undefined; + + beforeEach(() => { + originalNcmecEnv = process.env.NCMEC_ENV; + }); + afterEach(() => { + if (originalNcmecEnv === undefined) { + delete process.env.NCMEC_ENV; + } else { + process.env.NCMEC_ENV = originalNcmecEnv; + } + }); + + it('passes isTest=true to submitReport when NCMEC_ENV is unset', async () => { + delete process.env.NCMEC_ENV; + const deps = makeDeps({ + decisions: [makeNcmecDecisionRow(ORG_ID)], + }); + const job = makeRetryFailedNcmecDecisionsJob( + jest.fn() as never, // closeSharedResourcesForShutdown (unused by run) + deps.manualReviewToolService as never, + deps.ncmecService as never, + deps.getItemTypeEventuallyConsistent as never, + deps.actionPublisher as never, + deps.moderationConfigService as never, + deps.userManagementService as never, + ); + + await job.run(); + + expect(deps.ncmecService.submitReport).toHaveBeenCalledTimes(1); + const [, isTest] = deps.ncmecService.submitReport.mock.calls[0]; + expect(isTest).toBe(true); + }); + + it('passes isTest=true to submitReport when NCMEC_ENV is "test"', async () => { + process.env.NCMEC_ENV = 'test'; + const deps = makeDeps({ + decisions: [makeNcmecDecisionRow(ORG_ID)], + }); + const job = makeRetryFailedNcmecDecisionsJob( + jest.fn() as never, + deps.manualReviewToolService as never, + deps.ncmecService as never, + deps.getItemTypeEventuallyConsistent as never, + deps.actionPublisher as never, + deps.moderationConfigService as never, + deps.userManagementService as never, + ); + + await job.run(); + + expect(deps.ncmecService.submitReport).toHaveBeenCalledTimes(1); + const [, isTest] = deps.ncmecService.submitReport.mock.calls[0]; + expect(isTest).toBe(true); + }); + + it('passes isTest=false to submitReport when NCMEC_ENV=production', async () => { + process.env.NCMEC_ENV = 'production'; + const deps = makeDeps({ + decisions: [makeNcmecDecisionRow(ORG_ID)], + }); + const job = makeRetryFailedNcmecDecisionsJob( + jest.fn() as never, + deps.manualReviewToolService as never, + deps.ncmecService as never, + deps.getItemTypeEventuallyConsistent as never, + deps.actionPublisher as never, + deps.moderationConfigService as never, + deps.userManagementService as never, + ); + + await job.run(); + + expect(deps.ncmecService.submitReport).toHaveBeenCalledTimes(1); + const [, isTest] = deps.ncmecService.submitReport.mock.calls[0]; + expect(isTest).toBe(false); + }); + + it('does not publish actions when NCMEC_ENV is unset (isTest=true)', async () => { + delete process.env.NCMEC_ENV; + const deps = makeDeps({ + decisions: [makeNcmecDecisionRow(ORG_ID)], + // Simulate an org that has actions configured to run on NCMEC report + // creation. In production mode these would publish; in test mode they + // must be suppressed. + getNCMECActionsToRunAndPolicies: jest.fn(async () => ({ + actionsToRunIds: ['action-1'], + policyIds: ['policy-1'], + })), + }); + const job = makeRetryFailedNcmecDecisionsJob( + jest.fn() as never, + deps.manualReviewToolService as never, + deps.ncmecService as never, + deps.getItemTypeEventuallyConsistent as never, + deps.actionPublisher as never, + deps.moderationConfigService as never, + deps.userManagementService as never, + ); + + await job.run(); + + expect(deps.ncmecService.submitReport).toHaveBeenCalledTimes(1); + expect(deps.actionPublisher.publishActions).not.toHaveBeenCalled(); + }); + + it('publishes actions when NCMEC_ENV=production (isTest=false)', async () => { + process.env.NCMEC_ENV = 'production'; + const deps = makeDeps({ + decisions: [makeNcmecDecisionRow(ORG_ID)], + getNCMECActionsToRunAndPolicies: jest.fn(async () => ({ + actionsToRunIds: ['action-1'], + policyIds: ['policy-1'], + })), + }); + const job = makeRetryFailedNcmecDecisionsJob( + jest.fn() as never, + deps.manualReviewToolService as never, + deps.ncmecService as never, + deps.getItemTypeEventuallyConsistent as never, + deps.actionPublisher as never, + deps.moderationConfigService as never, + deps.userManagementService as never, + ); + + await job.run(); + + expect(deps.ncmecService.submitReport).toHaveBeenCalledTimes(1); + expect(deps.actionPublisher.publishActions).toHaveBeenCalledTimes(1); + }); +}); diff --git a/server/workers_jobs/RetryFailedNcmecDecisionsJob.ts b/server/workers_jobs/RetryFailedNcmecDecisionsJob.ts index 18d9df63d..0cdb7d232 100644 --- a/server/workers_jobs/RetryFailedNcmecDecisionsJob.ts +++ b/server/workers_jobs/RetryFailedNcmecDecisionsJob.ts @@ -118,9 +118,10 @@ export default inject( getItemTypeEventuallyConsistent, }); submitReportInvoked = true; + const isTest = process.env.NCMEC_ENV !== 'production'; const reportResult = await ncmecService.submitReport( reportParams, - false, + isTest, ); if ( reportResult === 'UNSUPPORTED_ORG' || @@ -133,7 +134,8 @@ export default inject( await ncmecService.getNCMECActionsToRunAndPolicies(orgId); if ( actionAndPolicy != null && - actionAndPolicy.actionsToRunIds != null + actionAndPolicy.actionsToRunIds != null && + !isTest ) { const actions = await moderationConfigService.getActions({ orgId, diff --git a/types/index.ts b/types/index.ts index e98fbb36b..f1a0a814a 100644 --- a/types/index.ts +++ b/types/index.ts @@ -197,9 +197,7 @@ export function getScalarType(it: Field) { } export type MediaKind = - | ScalarTypes['AUDIO'] - | ScalarTypes['IMAGE'] - | ScalarTypes['VIDEO']; + ScalarTypes['AUDIO'] | ScalarTypes['IMAGE'] | ScalarTypes['VIDEO']; export function isMediaType(it: ScalarType): boolean { return (