From 3b5986433367218eeae6e29579833df831e6460c Mon Sep 17 00:00:00 2001 From: wwanarif Date: Mon, 13 Apr 2026 01:50:44 +0000 Subject: [PATCH 1/9] fixed tgi depreciated issue Signed-off-by: wwanarif --- .gitignore | 1 + studio-backend/app/templates/microsvc-composes/tgi.yaml | 2 +- studio-backend/app/templates/microsvc-manifests/tgi.yaml | 2 +- studio-backend/tests/exporter-groundtruth/gt_app-compose.yaml | 2 +- .../tests/exporter-groundtruth/gt_app-manifest-with-nginx.yaml | 2 +- studio-backend/tests/exporter-groundtruth/gt_app-manifest.yaml | 2 +- 6 files changed, 6 insertions(+), 5 deletions(-) diff --git a/.gitignore b/.gitignore index 3546d91..159b34c 100644 --- a/.gitignore +++ b/.gitignore @@ -3,3 +3,4 @@ test-results/ **/report.html docker-compose **/node_modules/ +**.venv/ \ No newline at end of file diff --git a/studio-backend/app/templates/microsvc-composes/tgi.yaml b/studio-backend/app/templates/microsvc-composes/tgi.yaml index 25c2421..45c1638 100644 --- a/studio-backend/app/templates/microsvc-composes/tgi.yaml +++ b/studio-backend/app/templates/microsvc-composes/tgi.yaml @@ -1,5 +1,5 @@ "{{endpoint}}": - image: ghcr.io/huggingface/text-generation-inference:2.4.0-intel-cpu + image: ghcr.io/huggingface/text-generation-inference:2.6.0-intel-cpu container_name: "{{endpoint}}" ports: - "{{port_key}}:80" diff --git a/studio-backend/app/templates/microsvc-manifests/tgi.yaml b/studio-backend/app/templates/microsvc-manifests/tgi.yaml index e47d703..324128d 100644 --- a/studio-backend/app/templates/microsvc-manifests/tgi.yaml +++ b/studio-backend/app/templates/microsvc-manifests/tgi.yaml @@ -67,7 +67,7 @@ spec: name: config-{endpoint} securityContext: {} - image: "ghcr.io/huggingface/text-generation-inference:2.4.0-intel-cpu" + image: "ghcr.io/huggingface/text-generation-inference:2.6.0-intel-cpu" imagePullPolicy: IfNotPresent volumeMounts: - mountPath: /data diff --git a/studio-backend/tests/exporter-groundtruth/gt_app-compose.yaml b/studio-backend/tests/exporter-groundtruth/gt_app-compose.yaml index 9c856d0..431cbc1 100644 --- a/studio-backend/tests/exporter-groundtruth/gt_app-compose.yaml +++ b/studio-backend/tests/exporter-groundtruth/gt_app-compose.yaml @@ -22,7 +22,7 @@ services: https_proxy: ${https_proxy} command: --model-id BAAI/bge-large-en-v1.5 --auto-truncate tgi-0: - image: ghcr.io/huggingface/text-generation-inference:2.4.0-intel-cpu + image: ghcr.io/huggingface/text-generation-inference:2.6.0-intel-cpu container_name: tgi-0 ports: - 3081:80 diff --git a/studio-backend/tests/exporter-groundtruth/gt_app-manifest-with-nginx.yaml b/studio-backend/tests/exporter-groundtruth/gt_app-manifest-with-nginx.yaml index 342122e..eaab12e 100644 --- a/studio-backend/tests/exporter-groundtruth/gt_app-manifest-with-nginx.yaml +++ b/studio-backend/tests/exporter-groundtruth/gt_app-manifest-with-nginx.yaml @@ -248,7 +248,7 @@ spec: - configMapRef: name: config-tgi-0 securityContext: {} - image: ghcr.io/huggingface/text-generation-inference:2.4.0-intel-cpu + image: ghcr.io/huggingface/text-generation-inference:2.6.0-intel-cpu imagePullPolicy: IfNotPresent volumeMounts: - mountPath: /data diff --git a/studio-backend/tests/exporter-groundtruth/gt_app-manifest.yaml b/studio-backend/tests/exporter-groundtruth/gt_app-manifest.yaml index 317a09e..95daf9c 100644 --- a/studio-backend/tests/exporter-groundtruth/gt_app-manifest.yaml +++ b/studio-backend/tests/exporter-groundtruth/gt_app-manifest.yaml @@ -248,7 +248,7 @@ spec: - configMapRef: name: config-tgi-0 securityContext: {} - image: ghcr.io/huggingface/text-generation-inference:2.4.0-intel-cpu + image: ghcr.io/huggingface/text-generation-inference:2.6.0-intel-cpu imagePullPolicy: IfNotPresent volumeMounts: - mountPath: /data From e98ab37d9d78e606edf45de006ab366e67e6045c Mon Sep 17 00:00:00 2001 From: wwanarif Date: Wed, 15 Apr 2026 12:52:19 +0000 Subject: [PATCH 2/9] cleanup frontend and update tgi version Signed-off-by: wwanarif --- .gitignore | 3 +- .../app/templates/microsvc-composes/tgi.yaml | 2 +- .../app/templates/microsvc-manifests/tgi.yaml | 2 +- .../exporter-groundtruth/gt_app-compose.yaml | 2 +- .../gt_app-manifest-with-nginx.yaml | 2 +- .../exporter-groundtruth/gt_app-manifest.yaml | 2 +- studio-frontend/Dockerfile | 19 +- studio-frontend/docker-entrypoint.dev.sh | 17 + studio-frontend/package.json | 6 +- studio-frontend/packages/server/bin/run | 4 +- studio-frontend/packages/server/nodemon.json | 4 +- .../packages/server/nodemon.json.bak | 6 + studio-frontend/packages/server/package.json | 47 +- .../packages/server/src/ChatflowPool.ts | 3 +- .../packages/server/src/Interface.ts | 78 +- .../packages/server/src/NodesPool.ts | 26 +- .../packages/server/src/commands/start.ts | 48 +- .../src/controllers/assistants/index.ts | 87 - .../server/src/controllers/chatflows/index.ts | 48 - .../src/controllers/documentstore/index.ts | 366 - .../server/src/controllers/feedback/index.ts | 66 - .../src/controllers/fetch-links/index.ts | 33 - .../src/controllers/get-upload-file/index.ts | 28 +- .../src/controllers/get-upload-path/index.ts | 10 +- .../controllers/internal-predictions/index.ts | 46 - .../server/src/controllers/leads/index.ts | 40 - .../src/controllers/load-prompts/index.ts | 23 - .../src/controllers/marketplaces/index.ts | 60 - .../openai-assistants-vector-store/index.ts | 201 - .../controllers/openai-assistants/index.ts | 104 - .../src/controllers/predictions/index.ts | 102 - .../src/controllers/prompts-lists/index.ts | 16 - .../server/src/controllers/stats/index.ts | 71 - .../src/controllers/upsert-history/index.ts | 30 - .../server/src/controllers/vectors/index.ts | 36 - studio-frontend/packages/server/src/index.ts | 21 +- .../server/src/routes/assistants/index.ts | 19 - .../src/routes/chatflows-streaming/index.ts | 9 - .../src/routes/chatflows-uploads/index.ts | 9 - .../server/src/routes/documentstore/index.ts | 54 - .../server/src/routes/evaluation/index.ts | 94 + .../server/src/routes/feedback/index.ts | 14 - .../server/src/routes/fetch-links/index.ts | 8 - .../packages/server/src/routes/index.ts | 45 +- .../routes/internal-chat-messages/index.ts | 14 - .../src/routes/internal-predictions/index.ts | 8 - .../packages/server/src/routes/leads/index.ts | 11 - .../server/src/routes/load-prompts/index.ts | 8 - .../server/src/routes/marketplaces/index.ts | 16 - .../routes/openai-assistants-files/index.ts | 12 - .../openai-assistants-vector-store/index.ts | 30 - .../src/routes/openai-assistants/index.ts | 15 - .../server/src/routes/predictions/index.ts | 13 - .../server/src/routes/prompts-lists/index.ts | 8 - .../src/routes/public-chatbots/index.ts | 14 - .../src/routes/public-chatflows/index.ts | 14 - .../packages/server/src/routes/stats/index.ts | 9 - .../server/src/routes/upsert-history/index.ts | 15 - .../server/src/routes/vectors/index.ts | 19 - .../server/src/services/assistants/index.ts | 348 - .../src/services/chat-messages/index.ts | 9 - .../server/src/services/chatflows/index.ts | 127 +- .../src/services/documentstore/index.ts | 1275 - .../src/services/export-import/index.ts | 11 +- .../server/src/services/fetch-links/index.ts | 34 - .../server/src/services/leads/index.ts | 43 - .../server/src/services/load-prompts/index.ts | 28 - .../server/src/services/marketplaces/index.ts | 248 - .../server/src/services/nodes/index.ts | 11 +- .../openai-assistants-vector-store/index.ts | 258 - .../src/services/openai-assistants/index.ts | 120 - .../server/src/services/predictions/index.ts | 21 - .../src/services/prompts-lists/index.ts | 22 - .../server/src/services/stats/index.ts | 53 - .../server/src/services/telemetry/index.ts | 10 - .../server/src/services/tools/index.ts | 6 - .../src/services/upsert-history/index.ts | 75 - .../server/src/services/vectors/index.ts | 17 - .../packages/server/src/utils/SSEStreamer.ts | 325 - .../packages/server/src/utils/apiKey.ts | 2 +- .../server/src/utils/buildAgentGraph.ts | 1046 - .../server/src/utils/buildChatflow.ts | 571 - .../server/src/utils/fileRepository.ts | 97 - .../server/src/utils/getUploadsConfig.ts | 121 - .../packages/server/src/utils/index.ts | 63 +- .../packages/server/src/utils/telemetry.ts | 52 - .../packages/server/src/utils/upsertVector.ts | 182 - studio-frontend/packages/ui/package.json | 4 - .../packages/ui/src/api/evaluation.js | 47 + .../packages/ui/src/menu-items/dashboard.js | 13 +- .../packages/ui/src/routes/MainRoutes.jsx | 7 + .../src/views/evaluation/CreateRunModal.jsx | 232 + .../ui/src/views/evaluation/DatasetTab.jsx | 490 + .../ui/src/views/evaluation/ExecutionTab.jsx | 302 + .../ui/src/views/evaluation/index.jsx | 71 + studio-frontend/pnpm-lock.yaml | 23520 ++++++++++++++++ 96 files changed, 25002 insertions(+), 6976 deletions(-) create mode 100755 studio-frontend/docker-entrypoint.dev.sh create mode 100644 studio-frontend/packages/server/nodemon.json.bak delete mode 100644 studio-frontend/packages/server/src/controllers/assistants/index.ts delete mode 100644 studio-frontend/packages/server/src/controllers/documentstore/index.ts delete mode 100644 studio-frontend/packages/server/src/controllers/feedback/index.ts delete mode 100644 studio-frontend/packages/server/src/controllers/fetch-links/index.ts delete mode 100644 studio-frontend/packages/server/src/controllers/internal-predictions/index.ts delete mode 100644 studio-frontend/packages/server/src/controllers/leads/index.ts delete mode 100644 studio-frontend/packages/server/src/controllers/load-prompts/index.ts delete mode 100644 studio-frontend/packages/server/src/controllers/marketplaces/index.ts delete mode 100644 studio-frontend/packages/server/src/controllers/openai-assistants-vector-store/index.ts delete mode 100644 studio-frontend/packages/server/src/controllers/openai-assistants/index.ts delete mode 100644 studio-frontend/packages/server/src/controllers/predictions/index.ts delete mode 100644 studio-frontend/packages/server/src/controllers/prompts-lists/index.ts delete mode 100644 studio-frontend/packages/server/src/controllers/stats/index.ts delete mode 100644 studio-frontend/packages/server/src/controllers/upsert-history/index.ts delete mode 100644 studio-frontend/packages/server/src/controllers/vectors/index.ts delete mode 100644 studio-frontend/packages/server/src/routes/assistants/index.ts delete mode 100644 studio-frontend/packages/server/src/routes/chatflows-streaming/index.ts delete mode 100644 studio-frontend/packages/server/src/routes/chatflows-uploads/index.ts delete mode 100644 studio-frontend/packages/server/src/routes/documentstore/index.ts create mode 100644 studio-frontend/packages/server/src/routes/evaluation/index.ts delete mode 100644 studio-frontend/packages/server/src/routes/feedback/index.ts delete mode 100644 studio-frontend/packages/server/src/routes/fetch-links/index.ts delete mode 100644 studio-frontend/packages/server/src/routes/internal-chat-messages/index.ts delete mode 100644 studio-frontend/packages/server/src/routes/internal-predictions/index.ts delete mode 100644 studio-frontend/packages/server/src/routes/leads/index.ts delete mode 100644 studio-frontend/packages/server/src/routes/load-prompts/index.ts delete mode 100644 studio-frontend/packages/server/src/routes/marketplaces/index.ts delete mode 100644 studio-frontend/packages/server/src/routes/openai-assistants-files/index.ts delete mode 100644 studio-frontend/packages/server/src/routes/openai-assistants-vector-store/index.ts delete mode 100644 studio-frontend/packages/server/src/routes/openai-assistants/index.ts delete mode 100644 studio-frontend/packages/server/src/routes/predictions/index.ts delete mode 100644 studio-frontend/packages/server/src/routes/prompts-lists/index.ts delete mode 100644 studio-frontend/packages/server/src/routes/public-chatbots/index.ts delete mode 100644 studio-frontend/packages/server/src/routes/public-chatflows/index.ts delete mode 100644 studio-frontend/packages/server/src/routes/stats/index.ts delete mode 100644 studio-frontend/packages/server/src/routes/upsert-history/index.ts delete mode 100644 studio-frontend/packages/server/src/routes/vectors/index.ts delete mode 100644 studio-frontend/packages/server/src/services/assistants/index.ts delete mode 100644 studio-frontend/packages/server/src/services/documentstore/index.ts delete mode 100644 studio-frontend/packages/server/src/services/fetch-links/index.ts delete mode 100644 studio-frontend/packages/server/src/services/leads/index.ts delete mode 100644 studio-frontend/packages/server/src/services/load-prompts/index.ts delete mode 100644 studio-frontend/packages/server/src/services/marketplaces/index.ts delete mode 100644 studio-frontend/packages/server/src/services/openai-assistants-vector-store/index.ts delete mode 100644 studio-frontend/packages/server/src/services/openai-assistants/index.ts delete mode 100644 studio-frontend/packages/server/src/services/predictions/index.ts delete mode 100644 studio-frontend/packages/server/src/services/prompts-lists/index.ts delete mode 100644 studio-frontend/packages/server/src/services/stats/index.ts delete mode 100644 studio-frontend/packages/server/src/services/telemetry/index.ts delete mode 100644 studio-frontend/packages/server/src/services/upsert-history/index.ts delete mode 100644 studio-frontend/packages/server/src/services/vectors/index.ts delete mode 100644 studio-frontend/packages/server/src/utils/SSEStreamer.ts delete mode 100644 studio-frontend/packages/server/src/utils/buildAgentGraph.ts delete mode 100644 studio-frontend/packages/server/src/utils/buildChatflow.ts delete mode 100644 studio-frontend/packages/server/src/utils/fileRepository.ts delete mode 100644 studio-frontend/packages/server/src/utils/getUploadsConfig.ts delete mode 100644 studio-frontend/packages/server/src/utils/telemetry.ts delete mode 100644 studio-frontend/packages/server/src/utils/upsertVector.ts create mode 100644 studio-frontend/packages/ui/src/api/evaluation.js create mode 100644 studio-frontend/packages/ui/src/views/evaluation/CreateRunModal.jsx create mode 100644 studio-frontend/packages/ui/src/views/evaluation/DatasetTab.jsx create mode 100644 studio-frontend/packages/ui/src/views/evaluation/ExecutionTab.jsx create mode 100644 studio-frontend/packages/ui/src/views/evaluation/index.jsx create mode 100644 studio-frontend/pnpm-lock.yaml diff --git a/.gitignore b/.gitignore index 159b34c..cbbafb0 100644 --- a/.gitignore +++ b/.gitignore @@ -3,4 +3,5 @@ test-results/ **/report.html docker-compose **/node_modules/ -**.venv/ \ No newline at end of file +**.venv/ +**pycache**/ \ No newline at end of file diff --git a/studio-backend/app/templates/microsvc-composes/tgi.yaml b/studio-backend/app/templates/microsvc-composes/tgi.yaml index 45c1638..186d9e3 100644 --- a/studio-backend/app/templates/microsvc-composes/tgi.yaml +++ b/studio-backend/app/templates/microsvc-composes/tgi.yaml @@ -1,5 +1,5 @@ "{{endpoint}}": - image: ghcr.io/huggingface/text-generation-inference:2.6.0-intel-cpu + image: ghcr.io/huggingface/text-generation-inference:2.4.1-intel-cpu container_name: "{{endpoint}}" ports: - "{{port_key}}:80" diff --git a/studio-backend/app/templates/microsvc-manifests/tgi.yaml b/studio-backend/app/templates/microsvc-manifests/tgi.yaml index 324128d..00db4ff 100644 --- a/studio-backend/app/templates/microsvc-manifests/tgi.yaml +++ b/studio-backend/app/templates/microsvc-manifests/tgi.yaml @@ -67,7 +67,7 @@ spec: name: config-{endpoint} securityContext: {} - image: "ghcr.io/huggingface/text-generation-inference:2.6.0-intel-cpu" + image: "ghcr.io/huggingface/text-generation-inference:2.4.1-intel-cpu" imagePullPolicy: IfNotPresent volumeMounts: - mountPath: /data diff --git a/studio-backend/tests/exporter-groundtruth/gt_app-compose.yaml b/studio-backend/tests/exporter-groundtruth/gt_app-compose.yaml index 431cbc1..9fe5bbe 100644 --- a/studio-backend/tests/exporter-groundtruth/gt_app-compose.yaml +++ b/studio-backend/tests/exporter-groundtruth/gt_app-compose.yaml @@ -22,7 +22,7 @@ services: https_proxy: ${https_proxy} command: --model-id BAAI/bge-large-en-v1.5 --auto-truncate tgi-0: - image: ghcr.io/huggingface/text-generation-inference:2.6.0-intel-cpu + image: ghcr.io/huggingface/text-generation-inference:2.4.1-intel-cpu container_name: tgi-0 ports: - 3081:80 diff --git a/studio-backend/tests/exporter-groundtruth/gt_app-manifest-with-nginx.yaml b/studio-backend/tests/exporter-groundtruth/gt_app-manifest-with-nginx.yaml index eaab12e..e5a3b53 100644 --- a/studio-backend/tests/exporter-groundtruth/gt_app-manifest-with-nginx.yaml +++ b/studio-backend/tests/exporter-groundtruth/gt_app-manifest-with-nginx.yaml @@ -248,7 +248,7 @@ spec: - configMapRef: name: config-tgi-0 securityContext: {} - image: ghcr.io/huggingface/text-generation-inference:2.6.0-intel-cpu + image: ghcr.io/huggingface/text-generation-inference:2.4.1-intel-cpu imagePullPolicy: IfNotPresent volumeMounts: - mountPath: /data diff --git a/studio-backend/tests/exporter-groundtruth/gt_app-manifest.yaml b/studio-backend/tests/exporter-groundtruth/gt_app-manifest.yaml index 95daf9c..062d766 100644 --- a/studio-backend/tests/exporter-groundtruth/gt_app-manifest.yaml +++ b/studio-backend/tests/exporter-groundtruth/gt_app-manifest.yaml @@ -248,7 +248,7 @@ spec: - configMapRef: name: config-tgi-0 securityContext: {} - image: ghcr.io/huggingface/text-generation-inference:2.6.0-intel-cpu + image: ghcr.io/huggingface/text-generation-inference:2.4.1-intel-cpu imagePullPolicy: IfNotPresent volumeMounts: - mountPath: /data diff --git a/studio-frontend/Dockerfile b/studio-frontend/Dockerfile index 828818e..2d2331c 100644 --- a/studio-frontend/Dockerfile +++ b/studio-frontend/Dockerfile @@ -32,10 +32,13 @@ RUN pnpm install # Copy source code COPY . . -# Build the app and clean up -RUN pnpm build && \ - # Prune to production dependencies only - pnpm prune --prod && \ +# Build the app (separate stage target so dev compose stops here) +RUN pnpm build + +FROM builder AS pruned + +# Prune to production dependencies only +RUN pnpm prune --prod && \ rm -rf node_modules/.cache && \ rm -rf packages/*/node_modules/.cache @@ -63,10 +66,10 @@ ENV NODE_OPTIONS=--max-old-space-size=8192 WORKDIR /usr/src -# Copy only necessary files from builder -COPY --from=builder /usr/src/package.json /usr/src/pnpm-workspace.yaml ./ -COPY --from=builder /usr/src/packages ./packages -COPY --from=builder /usr/src/node_modules ./node_modules +# Copy only necessary files from pruned builder +COPY --from=pruned /usr/src/package.json /usr/src/pnpm-workspace.yaml ./ +COPY --from=pruned /usr/src/packages ./packages +COPY --from=pruned /usr/src/node_modules ./node_modules EXPOSE 3000 diff --git a/studio-frontend/docker-entrypoint.dev.sh b/studio-frontend/docker-entrypoint.dev.sh new file mode 100755 index 0000000..329b5d2 --- /dev/null +++ b/studio-frontend/docker-entrypoint.dev.sh @@ -0,0 +1,17 @@ +#!/bin/sh +echo '[studio-frontend] Installing dependencies...' + +# Background watchdog prints progress every 15s +( while true; do + sleep 15 + count=$(ls /usr/src/node_modules 2>/dev/null | wc -l) + echo "[studio-frontend] pnpm install in progress... (node_modules: ${count} packages)" +done ) & +watchdog=$! + +pnpm install --frozen-lockfile --config.confirmModulesPurge=false || pnpm install --config.confirmModulesPurge=false +kill "$watchdog" 2>/dev/null +wait "$watchdog" 2>/dev/null + +echo '[studio-frontend] Install complete. Starting dev server...' +exec pnpm dev diff --git a/studio-frontend/package.json b/studio-frontend/package.json index b6751a4..1f688b8 100644 --- a/studio-frontend/package.json +++ b/studio-frontend/package.json @@ -10,9 +10,9 @@ "build": "turbo run build", "build-force": "pnpm clean && turbo run build --force", "dev": "turbo run dev --parallel --no-cache", - "start": "run-script-os", - "start:windows": "cd packages/server/bin && run start", - "start:default": "cd packages/server/bin && ./run start", + "start": "cd packages/server && node bin/run", + "start:windows": "cd packages/server && node bin/run", + "start:default": "cd packages/server && node bin/run", "clean": "pnpm --filter \"./packages/**\" clean", "nuke": "pnpm --filter \"./packages/**\" nuke && rimraf node_modules .turbo", "format": "prettier --write \"**/*.{ts,tsx,md}\"", diff --git a/studio-frontend/packages/server/bin/run b/studio-frontend/packages/server/bin/run index a7635de..f937d55 100755 --- a/studio-frontend/packages/server/bin/run +++ b/studio-frontend/packages/server/bin/run @@ -1,5 +1,3 @@ #!/usr/bin/env node -const oclif = require('@oclif/core') - -oclif.run().then(require('@oclif/core/flush')).catch(require('@oclif/core/handle')) +require('../dist/index').start() diff --git a/studio-frontend/packages/server/nodemon.json b/studio-frontend/packages/server/nodemon.json index d4688a5..6897d08 100644 --- a/studio-frontend/packages/server/nodemon.json +++ b/studio-frontend/packages/server/nodemon.json @@ -1,6 +1,6 @@ { "ignore": ["**/*.spec.ts", ".git", "node_modules"], - "watch": ["commands", "index.ts", "src", "../components/nodes", "../components/src"], - "exec": "pnpm start", + "watch": ["src"], + "exec": "ts-node --transpile-only src/index.ts", "ext": "ts" } diff --git a/studio-frontend/packages/server/nodemon.json.bak b/studio-frontend/packages/server/nodemon.json.bak new file mode 100644 index 0000000..6897d08 --- /dev/null +++ b/studio-frontend/packages/server/nodemon.json.bak @@ -0,0 +1,6 @@ +{ + "ignore": ["**/*.spec.ts", ".git", "node_modules"], + "watch": ["src"], + "exec": "ts-node --transpile-only src/index.ts", + "ext": "ts" +} diff --git a/studio-frontend/packages/server/package.json b/studio-frontend/packages/server/package.json index f35f64f..1cb36b1 100644 --- a/studio-frontend/packages/server/package.json +++ b/studio-frontend/packages/server/package.json @@ -8,40 +8,19 @@ "flowise": "./bin/run" }, "files": [ - "bin", - "marketplaces", - "dist", - "npm-shrinkwrap.json", - "oclif.manifest.json", - "oauth2.html" + "dist" ], - "oclif": { - "bin": "flowise", - "commands": "./dist/commands" - }, "scripts": { "build": "tsc", - "start": "run-script-os", + "start": "node ./bin/run", "clean": "rimraf dist", "nuke": "rimraf dist node_modules .turbo", - "start:windows": "cd bin && run start", - "start:default": "cd bin && ./run start", "dev": "nodemon", - "oclif-dev": "run-script-os", - "oclif-dev:windows": "cd bin && dev start", - "oclif-dev:default": "cd bin && ./dev start", - "postpack": "shx rm -f oclif.manifest.json", - "prepack": "pnpm build && oclif manifest && oclif readme", "typeorm": "typeorm-ts-node-commonjs", "typeorm:migration-generate": "pnpm typeorm migration:generate -d ./src/utils/typeormDataSource.ts", "typeorm:migration-run": "pnpm typeorm migration:run -d ./src/utils/typeormDataSource.ts", "typeorm:migration-revert": "pnpm typeorm migration:revert -d ./src/utils/typeormDataSource.ts", "watch": "tsc --watch", - "version": "oclif readme && git add README.md", - "cypress:open": "cypress open", - "cypress:run": "cypress run", - "e2e": "start-server-and-test dev http://localhost:3000 cypress:run", - "cypress:ci": "START_SERVER_AND_TEST_INSECURE=1 start-server-and-test start https-get://localhost:3000 cypress:run", "test": "jest --runInBand --detectOpenHandles --forceExit" }, "keywords": [], @@ -55,9 +34,9 @@ }, "license": "SEE LICENSE IN LICENSE.md", "dependencies": { - "@oclif/core": "4.0.7", "@types/lodash": "^4.17.20", "@types/uuid": "^9.0.7", + "archiver": "^6.0.1", "async-mutex": "^0.4.0", "axios": "1.12.0", "content-disposition": "0.5.4", @@ -67,22 +46,17 @@ "express": "^4.17.3", "express-basic-auth": "^1.2.1", "express-rate-limit": "^6.9.0", - "flowise-components": "^3.0.8", "flowise-ui": "workspace:^", + "form-data": "^4.0.0", "http-errors": "^2.0.0", "http-status-codes": "^2.3.0", - "@langchain/core": "^0.2.0", - "@langchain/langgraph": "^0.0.15", - "langchainhub": "^0.0.11", + "https-proxy-agent": "^7.0.4", "lodash": "^4.17.21", "moment": "^2.29.3", "moment-timezone": "^0.5.34", "multer": "^1.4.5-lts.1", "mysql2": "^3.11.3", - "form-data": "^4.0.0", - "openai": "^4.96.0", "pg": "^8.11.1", - "posthog-node": "^3.5.0", "reflect-metadata": "^0.1.13", "sanitize-html": "^2.11.0", "socket.io": "^4.6.1", @@ -90,28 +64,25 @@ "typeorm": "^0.3.6", "uuid": "^9.0.1", "winston": "^3.9.0", - "https-proxy-agent": "^7.0.4", - "archiver": "^6.0.1", - "turndown": "^7.2.0" + "winston-daily-rotate-file": "^5.0.0" }, "devDependencies": { + "@types/archiver": "^6.0.2", "@types/content-disposition": "0.5.8", "@types/cors": "^2.8.12", - "@types/express": "^4.17.17", "@types/crypto-js": "^4.1.1", + "@types/express": "^4.17.17", "@types/multer": "^1.4.7", "@types/sanitize-html": "^2.9.5", "concurrently": "^7.1.0", "cypress": "^13.13.0", "nodemon": "^2.0.22", - "oclif": "^4.20.5", "rimraf": "^5.0.5", "run-script-os": "^1.1.6", "shx": "^0.3.3", "start-server-and-test": "^2.0.3", "ts-node": "^10.7.0", "tsc-watch": "^6.0.4", - "typescript": "^5.4.5", - "@types/archiver": "^6.0.2" + "typescript": "^5.4.5" } } diff --git a/studio-frontend/packages/server/src/ChatflowPool.ts b/studio-frontend/packages/server/src/ChatflowPool.ts index 5f1e3a8..38fe29f 100644 --- a/studio-frontend/packages/server/src/ChatflowPool.ts +++ b/studio-frontend/packages/server/src/ChatflowPool.ts @@ -1,5 +1,4 @@ -import { ICommonObject } from 'flowise-components' -import { IActiveChatflows, INodeData, IReactFlowNode } from './Interface' +import { ICommonObject, IActiveChatflows, INodeData, IReactFlowNode } from './Interface' import logger from './utils/logger' /** diff --git a/studio-frontend/packages/server/src/Interface.ts b/studio-frontend/packages/server/src/Interface.ts index 228ddc3..cc552a4 100644 --- a/studio-frontend/packages/server/src/Interface.ts +++ b/studio-frontend/packages/server/src/Interface.ts @@ -1,4 +1,80 @@ -import { IAction, ICommonObject, IFileUpload, INode, INodeData as INodeDataFromComponent, INodeParams } from 'flowise-components' +// Local type definitions (previously from flowise-components) +export interface ICommonObject { + [key: string]: any +} + +export interface IFileUpload { + data?: string + type: string + name: string + mime: string +} + +export interface IAction { + id?: string + elements?: ICommonObject[] + mapping?: ICommonObject +} + +export interface INodeParams { + label?: string + name: string + type: string + default?: any + optional?: boolean + hint?: string + acceptVariable?: boolean + options?: ICommonObject[] + rows?: number + list?: boolean + placeholder?: string + additionalParams?: boolean + credentialNames?: string[] + fileType?: string + id?: string + inputs?: INodeParams[] + [key: string]: any +} + +export interface INode { + label?: string + name: string + type?: string + icon?: string + category?: string + version?: number + description?: string + baseClasses?: string[] + inputs?: INodeParams[] + outputs?: INodeParams[] + outputAnchors?: INodeParams[] + credential?: ICommonObject + filePath?: string + badge?: string + tags?: string[] + author?: string + loadMethods?: ICommonObject + vectorStoreMethods?: ICommonObject + [key: string]: any +} + +export interface INodeDataFromComponent { + id: string + label: string + version?: number + name: string + type: string + category: string + icon?: string + description?: string + baseClasses?: string[] + inputs?: ICommonObject + outputs?: ICommonObject + instance?: any + loadMethod?: string + credential?: ICommonObject + [key: string]: any +} export type MessageType = 'apiMessage' | 'userMessage' diff --git a/studio-frontend/packages/server/src/NodesPool.ts b/studio-frontend/packages/server/src/NodesPool.ts index 62c5595..0f2c51e 100644 --- a/studio-frontend/packages/server/src/NodesPool.ts +++ b/studio-frontend/packages/server/src/NodesPool.ts @@ -1,9 +1,6 @@ -import { IComponentNodes, IComponentCredentials } from './Interface' +import { IComponentNodes, IComponentCredentials, ICommonObject } from './Interface' import path from 'path' -import { Dirent } from 'fs' -import { getNodeModulesPackagePath } from './utils' import { promises } from 'fs' -import { ICommonObject } from 'flowise-components' import logger from './utils/logger' import { appConfig } from './AppConfig' @@ -24,9 +21,7 @@ export class NodesPool { * Initialize nodes */ private async initializeNodes() { - // const packagePath = getNodeModulesPackagePath('flowise-components') - // const nodesPath = path.join(packagePath, 'dist', 'nodes') - const nodesPath = '../src/nodes' + const nodesPath = path.join(__dirname, '..', 'src', 'nodes') const nodeFiles = await this.getFiles(nodesPath) // const nodeFiles: Array =[] return Promise.all( @@ -83,21 +78,8 @@ export class NodesPool { * Initialize credentials */ private async initializeCredentials() { - const packagePath = getNodeModulesPackagePath('flowise-components') - const nodesPath = path.join(packagePath, 'dist', 'credentials') - const nodeFiles = await this.getFiles(nodesPath) - return Promise.all( - nodeFiles.map(async (file) => { - if (file.endsWith('.credential.js')) { - const credentialModule = await require(file) - if (credentialModule.credClass) { - const newCredInstance = new credentialModule.credClass() - newCredInstance.icon = this.credentialIconPath[newCredInstance.name] ?? '' - this.componentCredentials[newCredInstance.name] = newCredInstance - } - } - }) - ) + // flowise-components removed — credentials loaded from node icons only + return } /** diff --git a/studio-frontend/packages/server/src/commands/start.ts b/studio-frontend/packages/server/src/commands/start.ts index 579053a..2b403ce 100644 --- a/studio-frontend/packages/server/src/commands/start.ts +++ b/studio-frontend/packages/server/src/commands/start.ts @@ -1,4 +1,3 @@ -import { Command, Flags, Args } from '@oclif/core' import path from 'path' import * as Server from '../index' import * as DataSource from '../DataSource' @@ -13,50 +12,9 @@ enum EXIT_CODE { } let processExitCode = EXIT_CODE.SUCCESS -export default class Start extends Command { +export default class Start { static args = {} - static flags = { - FLOWISE_USERNAME: Flags.string(), - FLOWISE_PASSWORD: Flags.string(), - FLOWISE_FILE_SIZE_LIMIT: Flags.string(), - PORT: Flags.string(), - CORS_ORIGINS: Flags.string(), - IFRAME_ORIGINS: Flags.string(), - DEBUG: Flags.string(), - BLOB_STORAGE_PATH: Flags.string(), - APIKEY_STORAGE_TYPE: Flags.string(), - APIKEY_PATH: Flags.string(), - SECRETKEY_PATH: Flags.string(), - FLOWISE_SECRETKEY_OVERWRITE: Flags.string(), - LOG_PATH: Flags.string(), - LOG_LEVEL: Flags.string(), - TOOL_FUNCTION_BUILTIN_DEP: Flags.string(), - TOOL_FUNCTION_EXTERNAL_DEP: Flags.string(), - NUMBER_OF_PROXIES: Flags.string(), - DISABLE_CHATFLOW_REUSE: Flags.string(), - DATABASE_TYPE: Flags.string(), - DATABASE_PATH: Flags.string(), - DATABASE_PORT: Flags.string(), - DATABASE_HOST: Flags.string(), - DATABASE_NAME: Flags.string(), - DATABASE_USER: Flags.string(), - DATABASE_PASSWORD: Flags.string(), - DATABASE_SSL: Flags.string(), - DATABASE_SSL_KEY_BASE64: Flags.string(), - LANGCHAIN_TRACING_V2: Flags.string(), - LANGCHAIN_ENDPOINT: Flags.string(), - LANGCHAIN_API_KEY: Flags.string(), - LANGCHAIN_PROJECT: Flags.string(), - DISABLE_FLOWISE_TELEMETRY: Flags.string(), - MODEL_LIST_CONFIG_JSON: Flags.string(), - STORAGE_TYPE: Flags.string(), - S3_STORAGE_BUCKET_NAME: Flags.string(), - S3_STORAGE_ACCESS_KEY_ID: Flags.string(), - S3_STORAGE_SECRET_ACCESS_KEY: Flags.string(), - S3_STORAGE_REGION: Flags.string(), - S3_ENDPOINT_URL: Flags.string(), - SHOW_COMMUNITY_NODES: Flags.string() - } + static flags: Record = {} async stopProcess() { logger.info('Shutting down Flowise...') @@ -90,7 +48,7 @@ export default class Start extends Command { logger.error('unhandledRejection: ', err) }) - const { flags } = await this.parse(Start) + const flags: Record = {} if (flags.PORT) process.env.PORT = flags.PORT if (flags.CORS_ORIGINS) process.env.CORS_ORIGINS = flags.CORS_ORIGINS diff --git a/studio-frontend/packages/server/src/controllers/assistants/index.ts b/studio-frontend/packages/server/src/controllers/assistants/index.ts deleted file mode 100644 index c252dc5..0000000 --- a/studio-frontend/packages/server/src/controllers/assistants/index.ts +++ /dev/null @@ -1,87 +0,0 @@ -import { Request, Response, NextFunction } from 'express' -import assistantsService from '../../services/assistants' -import { InternalFlowiseError } from '../../errors/internalFlowiseError' -import { StatusCodes } from 'http-status-codes' - -const createAssistant = async (req: Request, res: Response, next: NextFunction) => { - try { - if (!req.body) { - throw new InternalFlowiseError( - StatusCodes.PRECONDITION_FAILED, - `Error: assistantsController.createAssistant - body not provided!` - ) - } - const apiResponse = await assistantsService.createAssistant(req.body) - return res.json(apiResponse) - } catch (error) { - next(error) - } -} - -const deleteAssistant = async (req: Request, res: Response, next: NextFunction) => { - try { - if (typeof req.params === 'undefined' || !req.params.id) { - throw new InternalFlowiseError( - StatusCodes.PRECONDITION_FAILED, - `Error: assistantsController.deleteAssistant - id not provided!` - ) - } - const apiResponse = await assistantsService.deleteAssistant(req.params.id, req.query.isDeleteBoth) - return res.json(apiResponse) - } catch (error) { - next(error) - } -} - -const getAllAssistants = async (req: Request, res: Response, next: NextFunction) => { - try { - const apiResponse = await assistantsService.getAllAssistants() - return res.json(apiResponse) - } catch (error) { - next(error) - } -} - -const getAssistantById = async (req: Request, res: Response, next: NextFunction) => { - try { - if (typeof req.params === 'undefined' || !req.params.id) { - throw new InternalFlowiseError( - StatusCodes.PRECONDITION_FAILED, - `Error: assistantsController.getAssistantById - id not provided!` - ) - } - const apiResponse = await assistantsService.getAssistantById(req.params.id) - return res.json(apiResponse) - } catch (error) { - next(error) - } -} - -const updateAssistant = async (req: Request, res: Response, next: NextFunction) => { - try { - if (typeof req.params === 'undefined' || !req.params.id) { - throw new InternalFlowiseError( - StatusCodes.PRECONDITION_FAILED, - `Error: assistantsController.updateAssistant - id not provided!` - ) - } - if (!req.body) { - throw new InternalFlowiseError( - StatusCodes.PRECONDITION_FAILED, - `Error: assistantsController.updateAssistant - body not provided!` - ) - } - const apiResponse = await assistantsService.updateAssistant(req.params.id, req.body) - return res.json(apiResponse) - } catch (error) { - next(error) - } -} - -export default { - createAssistant, - deleteAssistant, - getAllAssistants, - getAssistantById, - updateAssistant -} diff --git a/studio-frontend/packages/server/src/controllers/chatflows/index.ts b/studio-frontend/packages/server/src/controllers/chatflows/index.ts index fe13033..784e50d 100644 --- a/studio-frontend/packages/server/src/controllers/chatflows/index.ts +++ b/studio-frontend/packages/server/src/controllers/chatflows/index.ts @@ -7,36 +7,6 @@ import { InternalFlowiseError } from '../../errors/internalFlowiseError' import { ChatflowType } from '../../Interface' import chatflowsService from '../../services/chatflows' -const checkIfChatflowIsValidForStreaming = async (req: Request, res: Response, next: NextFunction) => { - try { - if (typeof req.params === 'undefined' || !req.params.id) { - throw new InternalFlowiseError( - StatusCodes.PRECONDITION_FAILED, - `Error: chatflowsRouter.checkIfChatflowIsValidForStreaming - id not provided!` - ) - } - const apiResponse = await chatflowsService.checkIfChatflowIsValidForStreaming(req.params.id) - return res.json(apiResponse) - } catch (error) { - next(error) - } -} - -const checkIfChatflowIsValidForUploads = async (req: Request, res: Response, next: NextFunction) => { - try { - if (typeof req.params === 'undefined' || !req.params.id) { - throw new InternalFlowiseError( - StatusCodes.PRECONDITION_FAILED, - `Error: chatflowsRouter.checkIfChatflowIsValidForUploads - id not provided!` - ) - } - const apiResponse = await chatflowsService.checkIfChatflowIsValidForUploads(req.params.id) - return res.json(apiResponse) - } catch (error) { - next(error) - } -} - const deleteChatflow = async (req: Request, res: Response, next: NextFunction) => { try { if (typeof req.params === 'undefined' || !req.params.id) { @@ -173,21 +143,6 @@ const getSinglePublicChatflow = async (req: Request, res: Response, next: NextFu } } -const getSinglePublicChatbotConfig = async (req: Request, res: Response, next: NextFunction) => { - try { - if (typeof req.params === 'undefined' || !req.params.id) { - throw new InternalFlowiseError( - StatusCodes.PRECONDITION_FAILED, - `Error: chatflowsRouter.getSinglePublicChatbotConfig - id not provided!` - ) - } - const apiResponse = await chatflowsService.getSinglePublicChatbotConfig(req.params.id) - return res.json(apiResponse) - } catch (error) { - next(error) - } -} - const deployChatflowSandbox = async (req: Request, res: Response, next: NextFunction) => { try { console.log('Deploying chatflow sandbox') @@ -350,8 +305,6 @@ const oneClickDeployment = async (req: Request, res: Response, next: NextFunctio } export default { - checkIfChatflowIsValidForStreaming, - checkIfChatflowIsValidForUploads, deleteChatflow, getAllChatflows, getAllChatflowsbyUserId, @@ -362,7 +315,6 @@ export default { importChatflows, updateChatflow, getSinglePublicChatflow, - getSinglePublicChatbotConfig, deployChatflowSandbox, stopChatflowSandbox, buildDeploymentPackage, diff --git a/studio-frontend/packages/server/src/controllers/documentstore/index.ts b/studio-frontend/packages/server/src/controllers/documentstore/index.ts deleted file mode 100644 index 1004135..0000000 --- a/studio-frontend/packages/server/src/controllers/documentstore/index.ts +++ /dev/null @@ -1,366 +0,0 @@ -import { NextFunction, Request, Response } from 'express' -import { StatusCodes } from 'http-status-codes' -import documentStoreService from '../../services/documentstore' -import { DocumentStore } from '../../database/entities/DocumentStore' -import { InternalFlowiseError } from '../../errors/internalFlowiseError' -import { DocumentStoreDTO } from '../../Interface' - -const createDocumentStore = async (req: Request, res: Response, next: NextFunction) => { - try { - if (typeof req.body === 'undefined') { - throw new InternalFlowiseError( - StatusCodes.PRECONDITION_FAILED, - `Error: documentStoreController.createDocumentStore - body not provided!` - ) - } - const body = req.body - const docStore = DocumentStoreDTO.toEntity(body) - const apiResponse = await documentStoreService.createDocumentStore(docStore) - return res.json(apiResponse) - } catch (error) { - next(error) - } -} - -const getAllDocumentStores = async (req: Request, res: Response, next: NextFunction) => { - try { - const apiResponse = await documentStoreService.getAllDocumentStores() - return res.json(DocumentStoreDTO.fromEntities(apiResponse)) - } catch (error) { - next(error) - } -} - -const deleteLoaderFromDocumentStore = async (req: Request, res: Response, next: NextFunction) => { - try { - const storeId = req.params.id - const loaderId = req.params.loaderId - - if (!storeId || !loaderId) { - throw new InternalFlowiseError( - StatusCodes.PRECONDITION_FAILED, - `Error: documentStoreController.deleteLoaderFromDocumentStore - missing storeId or loaderId.` - ) - } - const apiResponse = await documentStoreService.deleteLoaderFromDocumentStore(storeId, loaderId) - return res.json(DocumentStoreDTO.fromEntity(apiResponse)) - } catch (error) { - next(error) - } -} - -const getDocumentStoreById = async (req: Request, res: Response, next: NextFunction) => { - try { - if (typeof req.params.id === 'undefined' || req.params.id === '') { - throw new InternalFlowiseError( - StatusCodes.PRECONDITION_FAILED, - `Error: documentStoreController.getDocumentStoreById - id not provided!` - ) - } - const apiResponse = await documentStoreService.getDocumentStoreById(req.params.id) - if (apiResponse && apiResponse.whereUsed) { - apiResponse.whereUsed = JSON.stringify(await documentStoreService.getUsedChatflowNames(apiResponse)) - } - return res.json(DocumentStoreDTO.fromEntity(apiResponse)) - } catch (error) { - next(error) - } -} - -const getDocumentStoreFileChunks = async (req: Request, res: Response, next: NextFunction) => { - try { - if (typeof req.params.storeId === 'undefined' || req.params.storeId === '') { - throw new InternalFlowiseError( - StatusCodes.PRECONDITION_FAILED, - `Error: documentStoreController.getDocumentStoreFileChunks - storeId not provided!` - ) - } - if (typeof req.params.fileId === 'undefined' || req.params.fileId === '') { - throw new InternalFlowiseError( - StatusCodes.PRECONDITION_FAILED, - `Error: documentStoreController.getDocumentStoreFileChunks - fileId not provided!` - ) - } - const page = req.params.pageNo ? parseInt(req.params.pageNo) : 1 - const apiResponse = await documentStoreService.getDocumentStoreFileChunks(req.params.storeId, req.params.fileId, page) - return res.json(apiResponse) - } catch (error) { - next(error) - } -} - -const deleteDocumentStoreFileChunk = async (req: Request, res: Response, next: NextFunction) => { - try { - if (typeof req.params.storeId === 'undefined' || req.params.storeId === '') { - throw new InternalFlowiseError( - StatusCodes.PRECONDITION_FAILED, - `Error: documentStoreController.deleteDocumentStoreFileChunk - storeId not provided!` - ) - } - if (typeof req.params.loaderId === 'undefined' || req.params.loaderId === '') { - throw new InternalFlowiseError( - StatusCodes.PRECONDITION_FAILED, - `Error: documentStoreController.deleteDocumentStoreFileChunk - loaderId not provided!` - ) - } - if (typeof req.params.chunkId === 'undefined' || req.params.chunkId === '') { - throw new InternalFlowiseError( - StatusCodes.PRECONDITION_FAILED, - `Error: documentStoreController.deleteDocumentStoreFileChunk - chunkId not provided!` - ) - } - const apiResponse = await documentStoreService.deleteDocumentStoreFileChunk( - req.params.storeId, - req.params.loaderId, - req.params.chunkId - ) - return res.json(apiResponse) - } catch (error) { - next(error) - } -} - -const editDocumentStoreFileChunk = async (req: Request, res: Response, next: NextFunction) => { - try { - if (typeof req.params.storeId === 'undefined' || req.params.storeId === '') { - throw new InternalFlowiseError( - StatusCodes.PRECONDITION_FAILED, - `Error: documentStoreController.editDocumentStoreFileChunk - storeId not provided!` - ) - } - if (typeof req.params.loaderId === 'undefined' || req.params.loaderId === '') { - throw new InternalFlowiseError( - StatusCodes.PRECONDITION_FAILED, - `Error: documentStoreController.editDocumentStoreFileChunk - loaderId not provided!` - ) - } - if (typeof req.params.chunkId === 'undefined' || req.params.chunkId === '') { - throw new InternalFlowiseError( - StatusCodes.PRECONDITION_FAILED, - `Error: documentStoreController.editDocumentStoreFileChunk - chunkId not provided!` - ) - } - const body = req.body - if (typeof body === 'undefined') { - throw new InternalFlowiseError( - StatusCodes.PRECONDITION_FAILED, - `Error: documentStoreController.editDocumentStoreFileChunk - body not provided!` - ) - } - const apiResponse = await documentStoreService.editDocumentStoreFileChunk( - req.params.storeId, - req.params.loaderId, - req.params.chunkId, - body.pageContent, - body.metadata - ) - return res.json(apiResponse) - } catch (error) { - next(error) - } -} - -const processFileChunks = async (req: Request, res: Response, next: NextFunction) => { - try { - if (typeof req.body === 'undefined') { - throw new InternalFlowiseError( - StatusCodes.PRECONDITION_FAILED, - `Error: documentStoreController.processFileChunks - body not provided!` - ) - } - const body = req.body - const apiResponse = await documentStoreService.processAndSaveChunks(body) - return res.json(apiResponse) - } catch (error) { - next(error) - } -} - -const updateDocumentStore = async (req: Request, res: Response, next: NextFunction) => { - try { - if (typeof req.params.id === 'undefined' || req.params.id === '') { - throw new InternalFlowiseError( - StatusCodes.PRECONDITION_FAILED, - `Error: documentStoreController.updateDocumentStore - storeId not provided!` - ) - } - if (typeof req.body === 'undefined') { - throw new InternalFlowiseError( - StatusCodes.PRECONDITION_FAILED, - `Error: documentStoreController.updateDocumentStore - body not provided!` - ) - } - const store = await documentStoreService.getDocumentStoreById(req.params.id) - if (!store) { - throw new InternalFlowiseError( - StatusCodes.NOT_FOUND, - `Error: documentStoreController.updateDocumentStore - DocumentStore ${req.params.id} not found in the database` - ) - } - const body = req.body - const updateDocStore = new DocumentStore() - Object.assign(updateDocStore, body) - const apiResponse = await documentStoreService.updateDocumentStore(store, updateDocStore) - return res.json(DocumentStoreDTO.fromEntity(apiResponse)) - } catch (error) { - next(error) - } -} - -const deleteDocumentStore = async (req: Request, res: Response, next: NextFunction) => { - try { - if (typeof req.params.id === 'undefined' || req.params.id === '') { - throw new InternalFlowiseError( - StatusCodes.PRECONDITION_FAILED, - `Error: documentStoreController.deleteDocumentStore - storeId not provided!` - ) - } - const apiResponse = await documentStoreService.deleteDocumentStore(req.params.id) - return res.json(apiResponse) - } catch (error) { - next(error) - } -} - -const previewFileChunks = async (req: Request, res: Response, next: NextFunction) => { - try { - if (typeof req.body === 'undefined') { - throw new InternalFlowiseError( - StatusCodes.PRECONDITION_FAILED, - `Error: documentStoreController.previewFileChunks - body not provided!` - ) - } - const body = req.body - body.preview = true - const apiResponse = await documentStoreService.previewChunks(body) - return res.json(apiResponse) - } catch (error) { - next(error) - } -} - -const getDocumentLoaders = async (req: Request, res: Response, next: NextFunction) => { - try { - const apiResponse = await documentStoreService.getDocumentLoaders() - return res.json(apiResponse) - } catch (error) { - next(error) - } -} - -const insertIntoVectorStore = async (req: Request, res: Response, next: NextFunction) => { - try { - if (typeof req.body === 'undefined') { - throw new Error('Error: documentStoreController.insertIntoVectorStore - body not provided!') - } - const body = req.body - const apiResponse = await documentStoreService.insertIntoVectorStore(body) - return res.json(DocumentStoreDTO.fromEntity(apiResponse)) - } catch (error) { - next(error) - } -} - -const queryVectorStore = async (req: Request, res: Response, next: NextFunction) => { - try { - if (typeof req.body === 'undefined') { - throw new Error('Error: documentStoreController.queryVectorStore - body not provided!') - } - const body = req.body - const apiResponse = await documentStoreService.queryVectorStore(body) - return res.json(apiResponse) - } catch (error) { - next(error) - } -} - -const deleteVectorStoreFromStore = async (req: Request, res: Response, next: NextFunction) => { - try { - if (typeof req.params.storeId === 'undefined' || req.params.storeId === '') { - throw new InternalFlowiseError( - StatusCodes.PRECONDITION_FAILED, - `Error: documentStoreController.deleteVectorStoreFromStore - storeId not provided!` - ) - } - const apiResponse = await documentStoreService.deleteVectorStoreFromStore(req.params.storeId) - return res.json(apiResponse) - } catch (error) { - next(error) - } -} - -const saveVectorStoreConfig = async (req: Request, res: Response, next: NextFunction) => { - try { - if (typeof req.body === 'undefined') { - throw new Error('Error: documentStoreController.saveVectorStoreConfig - body not provided!') - } - const body = req.body - const apiResponse = await documentStoreService.saveVectorStoreConfig(body) - return res.json(apiResponse) - } catch (error) { - next(error) - } -} - -const updateVectorStoreConfigOnly = async (req: Request, res: Response, next: NextFunction) => { - try { - if (typeof req.body === 'undefined') { - throw new Error('Error: documentStoreController.updateVectorStoreConfigOnly - body not provided!') - } - const body = req.body - const apiResponse = await documentStoreService.updateVectorStoreConfigOnly(body) - return res.json(apiResponse) - } catch (error) { - next(error) - } -} - -const getEmbeddingProviders = async (req: Request, res: Response, next: NextFunction) => { - try { - const apiResponse = await documentStoreService.getEmbeddingProviders() - return res.json(apiResponse) - } catch (error) { - next(error) - } -} - -const getVectorStoreProviders = async (req: Request, res: Response, next: NextFunction) => { - try { - const apiResponse = await documentStoreService.getVectorStoreProviders() - return res.json(apiResponse) - } catch (error) { - next(error) - } -} - -const getRecordManagerProviders = async (req: Request, res: Response, next: NextFunction) => { - try { - const apiResponse = await documentStoreService.getRecordManagerProviders() - return res.json(apiResponse) - } catch (error) { - next(error) - } -} - -export default { - deleteDocumentStore, - createDocumentStore, - getAllDocumentStores, - deleteLoaderFromDocumentStore, - getDocumentStoreById, - getDocumentStoreFileChunks, - updateDocumentStore, - processFileChunks, - previewFileChunks, - getDocumentLoaders, - deleteDocumentStoreFileChunk, - editDocumentStoreFileChunk, - insertIntoVectorStore, - getEmbeddingProviders, - getVectorStoreProviders, - getRecordManagerProviders, - saveVectorStoreConfig, - queryVectorStore, - deleteVectorStoreFromStore, - updateVectorStoreConfigOnly -} diff --git a/studio-frontend/packages/server/src/controllers/feedback/index.ts b/studio-frontend/packages/server/src/controllers/feedback/index.ts deleted file mode 100644 index 936a3b8..0000000 --- a/studio-frontend/packages/server/src/controllers/feedback/index.ts +++ /dev/null @@ -1,66 +0,0 @@ -import { Request, Response, NextFunction } from 'express' -import feedbackService from '../../services/feedback' -import { InternalFlowiseError } from '../../errors/internalFlowiseError' -import { StatusCodes } from 'http-status-codes' - -const getAllChatMessageFeedback = async (req: Request, res: Response, next: NextFunction) => { - try { - if (typeof req.params === 'undefined' || !req.params.id) { - throw new InternalFlowiseError( - StatusCodes.PRECONDITION_FAILED, - `Error: feedbackController.getAllChatMessageFeedback - id not provided!` - ) - } - const chatflowid = req.params.id - const chatId = req.query?.chatId as string | undefined - const sortOrder = req.query?.order as string | undefined - const startDate = req.query?.startDate as string | undefined - const endDate = req.query?.endDate as string | undefined - const apiResponse = await feedbackService.getAllChatMessageFeedback(chatflowid, chatId, sortOrder, startDate, endDate) - return res.json(apiResponse) - } catch (error) { - next(error) - } -} - -const createChatMessageFeedbackForChatflow = async (req: Request, res: Response, next: NextFunction) => { - try { - if (!req.body) { - throw new InternalFlowiseError( - StatusCodes.PRECONDITION_FAILED, - `Error: feedbackController.createChatMessageFeedbackForChatflow - body not provided!` - ) - } - const apiResponse = await feedbackService.createChatMessageFeedbackForChatflow(req.body) - return res.json(apiResponse) - } catch (error) { - next(error) - } -} - -const updateChatMessageFeedbackForChatflow = async (req: Request, res: Response, next: NextFunction) => { - try { - if (!req.body) { - throw new InternalFlowiseError( - StatusCodes.PRECONDITION_FAILED, - `Error: feedbackController.updateChatMessageFeedbackForChatflow - body not provided!` - ) - } - if (typeof req.params === 'undefined' || !req.params.id) { - throw new InternalFlowiseError( - StatusCodes.PRECONDITION_FAILED, - `Error: feedbackController.updateChatMessageFeedbackForChatflow - id not provided!` - ) - } - const apiResponse = await feedbackService.updateChatMessageFeedbackForChatflow(req.params.id, req.body) - return res.json(apiResponse) - } catch (error) { - next(error) - } -} - -export default { - getAllChatMessageFeedback, - createChatMessageFeedbackForChatflow, - updateChatMessageFeedbackForChatflow -} diff --git a/studio-frontend/packages/server/src/controllers/fetch-links/index.ts b/studio-frontend/packages/server/src/controllers/fetch-links/index.ts deleted file mode 100644 index f3acce5..0000000 --- a/studio-frontend/packages/server/src/controllers/fetch-links/index.ts +++ /dev/null @@ -1,33 +0,0 @@ -import { Request, Response, NextFunction } from 'express' -import fetchLinksService from '../../services/fetch-links' -import { InternalFlowiseError } from '../../errors/internalFlowiseError' -import { StatusCodes } from 'http-status-codes' - -const getAllLinks = async (req: Request, res: Response, next: NextFunction) => { - try { - if (typeof req.query === 'undefined' || !req.query.url) { - throw new InternalFlowiseError(StatusCodes.PRECONDITION_FAILED, `Error: fetchLinksController.getAllLinks - url not provided!`) - } - if (typeof req.query === 'undefined' || !req.query.relativeLinksMethod) { - throw new InternalFlowiseError( - StatusCodes.PRECONDITION_FAILED, - `Error: fetchLinksController.getAllLinks - relativeLinksMethod not provided!` - ) - } - if (typeof req.query === 'undefined' || !req.query.limit) { - throw new InternalFlowiseError(StatusCodes.PRECONDITION_FAILED, `Error: fetchLinksController.getAllLinks - limit not provided!`) - } - const apiResponse = await fetchLinksService.getAllLinks( - req.query.url as string, - req.query.relativeLinksMethod as string, - req.query.limit as string - ) - return res.json(apiResponse) - } catch (error) { - next(error) - } -} - -export default { - getAllLinks -} diff --git a/studio-frontend/packages/server/src/controllers/get-upload-file/index.ts b/studio-frontend/packages/server/src/controllers/get-upload-file/index.ts index 09a8784..04018a5 100644 --- a/studio-frontend/packages/server/src/controllers/get-upload-file/index.ts +++ b/studio-frontend/packages/server/src/controllers/get-upload-file/index.ts @@ -1,10 +1,20 @@ import { Request, Response, NextFunction } from 'express' import fs from 'fs' +import path from 'path' import contentDisposition from 'content-disposition' -import { streamStorageFile } from 'flowise-components' import { StatusCodes } from 'http-status-codes' import { InternalFlowiseError } from '../../errors/internalFlowiseError' +const getStoragePath = (): string => { + return process.env.BLOB_STORAGE_PATH + ? path.join(process.env.BLOB_STORAGE_PATH) + : path.join(getUserHome(), '.flowise', 'storage') +} + +const getUserHome = (): string => { + return process.env.HOME || process.cwd() +} + interface AuthenticatedRequest extends Request { user?: { activeOrganizationId?: string @@ -19,17 +29,13 @@ const streamUploadedFile = async (req: Request, res: Response, next: NextFunctio const chatflowId = req.query.chatflowId as string const chatId = req.query.chatId as string const fileName = req.query.fileName as string - const orgId = (req as AuthenticatedRequest).user?.activeOrganizationId || '' - res.setHeader('Content-Disposition', contentDisposition(fileName)) - const fileStream = await streamStorageFile(chatflowId, chatId, fileName, orgId) - - if (!fileStream) throw new InternalFlowiseError(StatusCodes.INTERNAL_SERVER_ERROR, `Error: streamStorageFile`) - - if (fileStream instanceof fs.ReadStream && fileStream?.pipe) { - fileStream.pipe(res) - } else { - res.send(fileStream) + const filePath = path.join(getStoragePath(), chatflowId, chatId, fileName) + if (!fs.existsSync(filePath)) { + throw new InternalFlowiseError(StatusCodes.NOT_FOUND, `File not found`) } + res.setHeader('Content-Disposition', contentDisposition(fileName)) + const fileStream = fs.createReadStream(filePath) + fileStream.pipe(res) } catch (error) { next(error) } diff --git a/studio-frontend/packages/server/src/controllers/get-upload-path/index.ts b/studio-frontend/packages/server/src/controllers/get-upload-path/index.ts index 05e7659..44dfd25 100644 --- a/studio-frontend/packages/server/src/controllers/get-upload-path/index.ts +++ b/studio-frontend/packages/server/src/controllers/get-upload-path/index.ts @@ -1,5 +1,13 @@ import { Request, Response, NextFunction } from 'express' -import { getStoragePath } from 'flowise-components' +import path from 'path' + +const getUserHome = (): string => process.env.HOME || process.cwd() + +const getStoragePath = (): string => { + return process.env.BLOB_STORAGE_PATH + ? path.join(process.env.BLOB_STORAGE_PATH) + : path.join(getUserHome(), '.flowise', 'storage') +} const getPathForUploads = async (req: Request, res: Response, next: NextFunction) => { try { diff --git a/studio-frontend/packages/server/src/controllers/internal-predictions/index.ts b/studio-frontend/packages/server/src/controllers/internal-predictions/index.ts deleted file mode 100644 index b251eec..0000000 --- a/studio-frontend/packages/server/src/controllers/internal-predictions/index.ts +++ /dev/null @@ -1,46 +0,0 @@ -import { Request, Response, NextFunction } from 'express' -import { utilBuildChatflow } from '../../utils/buildChatflow' -import { getRunningExpressApp } from '../../utils/getRunningExpressApp' -import { getErrorMessage } from '../../errors/utils' - -// Send input message and get prediction result (Internal) -const createInternalPrediction = async (req: Request, res: Response, next: NextFunction) => { - try { - if (req.body.streaming || req.body.streaming === 'true') { - createAndStreamInternalPrediction(req, res, next) - return - } else { - const apiResponse = await utilBuildChatflow(req, true) - return res.json(apiResponse) - } - } catch (error) { - next(error) - } -} - -// Send input message and stream prediction result using SSE (Internal) -const createAndStreamInternalPrediction = async (req: Request, res: Response, next: NextFunction) => { - const chatId = req.body.chatId - const sseStreamer = getRunningExpressApp().sseStreamer - try { - sseStreamer.addClient(chatId, res) - res.setHeader('Content-Type', 'text/event-stream') - res.setHeader('Cache-Control', 'no-cache') - res.setHeader('Connection', 'keep-alive') - res.setHeader('X-Accel-Buffering', 'no') //nginx config: https://serverfault.com/a/801629 - res.flushHeaders() - - const apiResponse = await utilBuildChatflow(req, true) - sseStreamer.streamMetadataEvent(apiResponse.chatId, apiResponse) - } catch (error) { - if (chatId) { - sseStreamer.streamErrorEvent(chatId, getErrorMessage(error)) - } - next(error) - } finally { - sseStreamer.removeClient(chatId) - } -} -export default { - createInternalPrediction -} diff --git a/studio-frontend/packages/server/src/controllers/leads/index.ts b/studio-frontend/packages/server/src/controllers/leads/index.ts deleted file mode 100644 index db403a0..0000000 --- a/studio-frontend/packages/server/src/controllers/leads/index.ts +++ /dev/null @@ -1,40 +0,0 @@ -import { Request, Response, NextFunction } from 'express' -import leadsService from '../../services/leads' -import { StatusCodes } from 'http-status-codes' -import { InternalFlowiseError } from '../../errors/internalFlowiseError' - -const getAllLeadsForChatflow = async (req: Request, res: Response, next: NextFunction) => { - try { - if (typeof req.params.id === 'undefined' || req.params.id === '') { - throw new InternalFlowiseError( - StatusCodes.PRECONDITION_FAILED, - `Error: leadsController.getAllLeadsForChatflow - id not provided!` - ) - } - const chatflowid = req.params.id - const apiResponse = await leadsService.getAllLeads(chatflowid) - return res.json(apiResponse) - } catch (error) { - next(error) - } -} - -const createLeadInChatflow = async (req: Request, res: Response, next: NextFunction) => { - try { - if (typeof req.body === 'undefined' || req.body === '') { - throw new InternalFlowiseError( - StatusCodes.PRECONDITION_FAILED, - `Error: leadsController.createLeadInChatflow - body not provided!` - ) - } - const apiResponse = await leadsService.createLead(req.body) - return res.json(apiResponse) - } catch (error) { - next(error) - } -} - -export default { - createLeadInChatflow, - getAllLeadsForChatflow -} diff --git a/studio-frontend/packages/server/src/controllers/load-prompts/index.ts b/studio-frontend/packages/server/src/controllers/load-prompts/index.ts deleted file mode 100644 index 6437e49..0000000 --- a/studio-frontend/packages/server/src/controllers/load-prompts/index.ts +++ /dev/null @@ -1,23 +0,0 @@ -import { Request, Response, NextFunction } from 'express' -import loadPromptsService from '../../services/load-prompts' -import { InternalFlowiseError } from '../../errors/internalFlowiseError' -import { StatusCodes } from 'http-status-codes' - -const createPrompt = async (req: Request, res: Response, next: NextFunction) => { - try { - if (typeof req.body === 'undefined' || !req.body.promptName) { - throw new InternalFlowiseError( - StatusCodes.PRECONDITION_FAILED, - `Error: loadPromptsController.createPrompt - promptName not provided!` - ) - } - const apiResponse = await loadPromptsService.createPrompt(req.body.promptName as string) - return res.json(apiResponse) - } catch (error) { - next(error) - } -} - -export default { - createPrompt -} diff --git a/studio-frontend/packages/server/src/controllers/marketplaces/index.ts b/studio-frontend/packages/server/src/controllers/marketplaces/index.ts deleted file mode 100644 index db94715..0000000 --- a/studio-frontend/packages/server/src/controllers/marketplaces/index.ts +++ /dev/null @@ -1,60 +0,0 @@ -import { Request, Response, NextFunction } from 'express' -import marketplacesService from '../../services/marketplaces' -import { InternalFlowiseError } from '../../errors/internalFlowiseError' -import { StatusCodes } from 'http-status-codes' - -// Get all templates for marketplaces -const getAllTemplates = async (req: Request, res: Response, next: NextFunction) => { - try { - const apiResponse = await marketplacesService.getAllTemplates() - return res.json(apiResponse) - } catch (error) { - next(error) - } -} - -const deleteCustomTemplate = async (req: Request, res: Response, next: NextFunction) => { - try { - if (typeof req.params === 'undefined' || !req.params.id) { - throw new InternalFlowiseError( - StatusCodes.PRECONDITION_FAILED, - `Error: marketplacesService.deleteCustomTemplate - id not provided!` - ) - } - const apiResponse = await marketplacesService.deleteCustomTemplate(req.params.id) - return res.json(apiResponse) - } catch (error) { - next(error) - } -} - -const getAllCustomTemplates = async (req: Request, res: Response, next: NextFunction) => { - try { - const apiResponse = await marketplacesService.getAllCustomTemplates() - return res.json(apiResponse) - } catch (error) { - next(error) - } -} - -const saveCustomTemplate = async (req: Request, res: Response, next: NextFunction) => { - try { - if ((!req.body && !(req.body.chatflowId || req.body.tool)) || !req.body.name) { - throw new InternalFlowiseError( - StatusCodes.PRECONDITION_FAILED, - `Error: marketplacesService.saveCustomTemplate - body not provided!` - ) - } - const apiResponse = await marketplacesService.saveCustomTemplate(req.body) - return res.json(apiResponse) - } catch (error) { - next(error) - } -} - -export default { - getAllTemplates, - getAllCustomTemplates, - saveCustomTemplate, - deleteCustomTemplate -} diff --git a/studio-frontend/packages/server/src/controllers/openai-assistants-vector-store/index.ts b/studio-frontend/packages/server/src/controllers/openai-assistants-vector-store/index.ts deleted file mode 100644 index 55a6d15..0000000 --- a/studio-frontend/packages/server/src/controllers/openai-assistants-vector-store/index.ts +++ /dev/null @@ -1,201 +0,0 @@ -import { Request, Response, NextFunction } from 'express' -import { StatusCodes } from 'http-status-codes' -import { InternalFlowiseError } from '../../errors/internalFlowiseError' -import openAIAssistantVectorStoreService from '../../services/openai-assistants-vector-store' - -const getAssistantVectorStore = async (req: Request, res: Response, next: NextFunction) => { - try { - if (typeof req.params === 'undefined' || !req.params.id) { - throw new InternalFlowiseError( - StatusCodes.PRECONDITION_FAILED, - `Error: openaiAssistantsVectorStoreController.getAssistantVectorStore - id not provided!` - ) - } - if (typeof req.query === 'undefined' || !req.query.credential) { - throw new InternalFlowiseError( - StatusCodes.PRECONDITION_FAILED, - `Error: openaiAssistantsVectorStoreController.getAssistantVectorStore - credential not provided!` - ) - } - const apiResponse = await openAIAssistantVectorStoreService.getAssistantVectorStore(req.query.credential as string, req.params.id) - return res.json(apiResponse) - } catch (error) { - next(error) - } -} - -const listAssistantVectorStore = async (req: Request, res: Response, next: NextFunction) => { - try { - if (typeof req.query === 'undefined' || !req.query.credential) { - throw new InternalFlowiseError( - StatusCodes.PRECONDITION_FAILED, - `Error: openaiAssistantsVectorStoreController.listAssistantVectorStore - credential not provided!` - ) - } - const apiResponse = await openAIAssistantVectorStoreService.listAssistantVectorStore(req.query.credential as string) - return res.json(apiResponse) - } catch (error) { - next(error) - } -} - -const createAssistantVectorStore = async (req: Request, res: Response, next: NextFunction) => { - try { - if (!req.body) { - throw new InternalFlowiseError( - StatusCodes.PRECONDITION_FAILED, - `Error: openaiAssistantsVectorStoreController.createAssistantVectorStore - body not provided!` - ) - } - if (typeof req.query === 'undefined' || !req.query.credential) { - throw new InternalFlowiseError( - StatusCodes.PRECONDITION_FAILED, - `Error: openaiAssistantsVectorStoreController.createAssistantVectorStore - credential not provided!` - ) - } - const apiResponse = await openAIAssistantVectorStoreService.createAssistantVectorStore(req.query.credential as string, req.body) - return res.json(apiResponse) - } catch (error) { - next(error) - } -} - -const updateAssistantVectorStore = async (req: Request, res: Response, next: NextFunction) => { - try { - if (typeof req.params === 'undefined' || !req.params.id) { - throw new InternalFlowiseError( - StatusCodes.PRECONDITION_FAILED, - `Error: openaiAssistantsVectorStoreController.updateAssistantVectorStore - id not provided!` - ) - } - if (typeof req.query === 'undefined' || !req.query.credential) { - throw new InternalFlowiseError( - StatusCodes.PRECONDITION_FAILED, - `Error: openaiAssistantsVectorStoreController.updateAssistantVectorStore - credential not provided!` - ) - } - if (!req.body) { - throw new InternalFlowiseError( - StatusCodes.PRECONDITION_FAILED, - `Error: openaiAssistantsVectorStoreController.updateAssistantVectorStore - body not provided!` - ) - } - const apiResponse = await openAIAssistantVectorStoreService.updateAssistantVectorStore( - req.query.credential as string, - req.params.id, - req.body - ) - return res.json(apiResponse) - } catch (error) { - next(error) - } -} - -const deleteAssistantVectorStore = async (req: Request, res: Response, next: NextFunction) => { - try { - if (typeof req.params === 'undefined' || !req.params.id) { - throw new InternalFlowiseError( - StatusCodes.PRECONDITION_FAILED, - `Error: openaiAssistantsVectorStoreController.deleteAssistantVectorStore - id not provided!` - ) - } - if (typeof req.query === 'undefined' || !req.query.credential) { - throw new InternalFlowiseError( - StatusCodes.PRECONDITION_FAILED, - `Error: openaiAssistantsVectorStoreController.updateAssistantVectorStore - credential not provided!` - ) - } - const apiResponse = await openAIAssistantVectorStoreService.deleteAssistantVectorStore( - req.query.credential as string, - req.params.id as string - ) - return res.json(apiResponse) - } catch (error) { - next(error) - } -} - -const uploadFilesToAssistantVectorStore = async (req: Request, res: Response, next: NextFunction) => { - try { - if (!req.body) { - throw new InternalFlowiseError( - StatusCodes.PRECONDITION_FAILED, - `Error: openaiAssistantsVectorStoreController.uploadFilesToAssistantVectorStore - body not provided!` - ) - } - if (typeof req.params === 'undefined' || !req.params.id) { - throw new InternalFlowiseError( - StatusCodes.PRECONDITION_FAILED, - `Error: openaiAssistantsVectorStoreController.uploadFilesToAssistantVectorStore - id not provided!` - ) - } - if (typeof req.query === 'undefined' || !req.query.credential) { - throw new InternalFlowiseError( - StatusCodes.PRECONDITION_FAILED, - `Error: openaiAssistantsVectorStoreController.uploadFilesToAssistantVectorStore - credential not provided!` - ) - } - const files = req.files ?? [] - const uploadFiles: { filePath: string; fileName: string }[] = [] - - if (Array.isArray(files)) { - for (const file of files) { - uploadFiles.push({ - filePath: file.path, - fileName: file.originalname - }) - } - } - - const apiResponse = await openAIAssistantVectorStoreService.uploadFilesToAssistantVectorStore( - req.query.credential as string, - req.params.id as string, - uploadFiles - ) - return res.json(apiResponse) - } catch (error) { - next(error) - } -} - -const deleteFilesFromAssistantVectorStore = async (req: Request, res: Response, next: NextFunction) => { - try { - if (!req.body) { - throw new InternalFlowiseError( - StatusCodes.PRECONDITION_FAILED, - `Error: openaiAssistantsVectorStoreController.deleteFilesFromAssistantVectorStore - body not provided!` - ) - } - if (typeof req.params === 'undefined' || !req.params.id) { - throw new InternalFlowiseError( - StatusCodes.PRECONDITION_FAILED, - `Error: openaiAssistantsVectorStoreController.deleteFilesFromAssistantVectorStore - id not provided!` - ) - } - if (typeof req.query === 'undefined' || !req.query.credential) { - throw new InternalFlowiseError( - StatusCodes.PRECONDITION_FAILED, - `Error: openaiAssistantsVectorStoreController.deleteFilesFromAssistantVectorStore - credential not provided!` - ) - } - - const apiResponse = await openAIAssistantVectorStoreService.deleteFilesFromAssistantVectorStore( - req.query.credential as string, - req.params.id as string, - req.body.file_ids - ) - return res.json(apiResponse) - } catch (error) { - next(error) - } -} - -export default { - getAssistantVectorStore, - listAssistantVectorStore, - createAssistantVectorStore, - updateAssistantVectorStore, - deleteAssistantVectorStore, - uploadFilesToAssistantVectorStore, - deleteFilesFromAssistantVectorStore -} diff --git a/studio-frontend/packages/server/src/controllers/openai-assistants/index.ts b/studio-frontend/packages/server/src/controllers/openai-assistants/index.ts deleted file mode 100644 index 8c4175e..0000000 --- a/studio-frontend/packages/server/src/controllers/openai-assistants/index.ts +++ /dev/null @@ -1,104 +0,0 @@ -import { Request, Response, NextFunction } from 'express' -import * as fs from 'fs' -import openaiAssistantsService from '../../services/openai-assistants' -import contentDisposition from 'content-disposition' -import { InternalFlowiseError } from '../../errors/internalFlowiseError' -import { StatusCodes } from 'http-status-codes' -import { streamStorageFile } from 'flowise-components' - -// List available assistants -const getAllOpenaiAssistants = async (req: Request, res: Response, next: NextFunction) => { - try { - if (typeof req.query === 'undefined' || !req.query.credential) { - throw new InternalFlowiseError( - StatusCodes.PRECONDITION_FAILED, - `Error: openaiAssistantsController.getAllOpenaiAssistants - credential not provided!` - ) - } - const apiResponse = await openaiAssistantsService.getAllOpenaiAssistants(req.query.credential as string) - return res.json(apiResponse) - } catch (error) { - next(error) - } -} - -// Get assistant object -const getSingleOpenaiAssistant = async (req: Request, res: Response, next: NextFunction) => { - try { - if (typeof req.params === 'undefined' || !req.params.id) { - throw new InternalFlowiseError( - StatusCodes.PRECONDITION_FAILED, - `Error: openaiAssistantsController.getSingleOpenaiAssistant - id not provided!` - ) - } - if (typeof req.query === 'undefined' || !req.query.credential) { - throw new InternalFlowiseError( - StatusCodes.PRECONDITION_FAILED, - `Error: openaiAssistantsController.getSingleOpenaiAssistant - credential not provided!` - ) - } - const apiResponse = await openaiAssistantsService.getSingleOpenaiAssistant(req.query.credential as string, req.params.id) - return res.json(apiResponse) - } catch (error) { - next(error) - } -} - -// Download file from assistant -const getFileFromAssistant = async (req: Request, res: Response, next: NextFunction) => { - try { - if (!req.body.chatflowId || !req.body.chatId || !req.body.fileName) { - return res.status(500).send(`Invalid file path`) - } - const chatflowId = req.body.chatflowId as string - const chatId = req.body.chatId as string - const fileName = req.body.fileName as string - const orgId = '' // Organization ID not required in this context - res.setHeader('Content-Disposition', contentDisposition(fileName)) - const fileStream = await streamStorageFile(chatflowId, chatId, fileName, orgId) - - if (!fileStream) throw new InternalFlowiseError(StatusCodes.INTERNAL_SERVER_ERROR, `Error: getFileFromAssistant`) - - if (fileStream instanceof fs.ReadStream && fileStream?.pipe) { - fileStream.pipe(res) - } else { - res.send(fileStream) - } - } catch (error) { - next(error) - } -} - -const uploadAssistantFiles = async (req: Request, res: Response, next: NextFunction) => { - try { - if (typeof req.query === 'undefined' || !req.query.credential) { - throw new InternalFlowiseError( - StatusCodes.PRECONDITION_FAILED, - `Error: openaiAssistantsVectorStoreController.uploadFilesToAssistantVectorStore - credential not provided!` - ) - } - const files = req.files ?? [] - const uploadFiles: { filePath: string; fileName: string }[] = [] - - if (Array.isArray(files)) { - for (const file of files) { - uploadFiles.push({ - filePath: file.path, - fileName: file.originalname - }) - } - } - - const apiResponse = await openaiAssistantsService.uploadFilesToAssistant(req.query.credential as string, uploadFiles) - return res.json(apiResponse) - } catch (error) { - next(error) - } -} - -export default { - getAllOpenaiAssistants, - getSingleOpenaiAssistant, - getFileFromAssistant, - uploadAssistantFiles -} diff --git a/studio-frontend/packages/server/src/controllers/predictions/index.ts b/studio-frontend/packages/server/src/controllers/predictions/index.ts deleted file mode 100644 index 504d651..0000000 --- a/studio-frontend/packages/server/src/controllers/predictions/index.ts +++ /dev/null @@ -1,102 +0,0 @@ -import { Request, Response, NextFunction } from 'express' -import { getRateLimiter } from '../../utils/rateLimit' -import chatflowsService from '../../services/chatflows' -import logger from '../../utils/logger' -import predictionsServices from '../../services/predictions' -import { InternalFlowiseError } from '../../errors/internalFlowiseError' -import { StatusCodes } from 'http-status-codes' -import { getRunningExpressApp } from '../../utils/getRunningExpressApp' -import { v4 as uuidv4 } from 'uuid' -import { getErrorMessage } from '../../errors/utils' - -// Send input message and get prediction result (External) -const createPrediction = async (req: Request, res: Response, next: NextFunction) => { - try { - if (typeof req.params === 'undefined' || !req.params.id) { - throw new InternalFlowiseError( - StatusCodes.PRECONDITION_FAILED, - `Error: predictionsController.createPrediction - id not provided!` - ) - } - if (!req.body) { - throw new InternalFlowiseError( - StatusCodes.PRECONDITION_FAILED, - `Error: predictionsController.createPrediction - body not provided!` - ) - } - const chatflow = await chatflowsService.getChatflowById(req.params.id) - if (!chatflow) { - throw new InternalFlowiseError(StatusCodes.NOT_FOUND, `Chatflow ${req.params.id} not found`) - } - let isDomainAllowed = true - logger.info(`[server]: Request originated from ${req.headers.origin || 'UNKNOWN ORIGIN'}`) - if (chatflow.chatbotConfig) { - const parsedConfig = JSON.parse(chatflow.chatbotConfig) - // check whether the first one is not empty. if it is empty that means the user set a value and then removed it. - const isValidAllowedOrigins = parsedConfig.allowedOrigins?.length && parsedConfig.allowedOrigins[0] !== '' - if (isValidAllowedOrigins && req.headers.origin) { - const originHeader = req.headers.origin - const origin = new URL(originHeader).host - isDomainAllowed = - parsedConfig.allowedOrigins.filter((domain: string) => { - try { - const allowedOrigin = new URL(domain).host - return origin === allowedOrigin - } catch (e) { - return false - } - }).length > 0 - } - } - if (isDomainAllowed) { - const streamable = await chatflowsService.checkIfChatflowIsValidForStreaming(req.params.id) - const isStreamingRequested = req.body.streaming === 'true' || req.body.streaming === true - if (streamable?.isStreaming && isStreamingRequested) { - const sseStreamer = getRunningExpressApp().sseStreamer - let chatId = req.body.chatId - if (!req.body.chatId) { - chatId = req.body.chatId ?? req.body.overrideConfig?.sessionId ?? uuidv4() - req.body.chatId = chatId - } - try { - sseStreamer.addExternalClient(chatId, res) - res.setHeader('Content-Type', 'text/event-stream') - res.setHeader('Cache-Control', 'no-cache') - res.setHeader('Connection', 'keep-alive') - res.setHeader('X-Accel-Buffering', 'no') //nginx config: https://serverfault.com/a/801629 - res.flushHeaders() - - const apiResponse = await predictionsServices.buildChatflow(req) - sseStreamer.streamMetadataEvent(apiResponse.chatId, apiResponse) - } catch (error) { - if (chatId) { - sseStreamer.streamErrorEvent(chatId, getErrorMessage(error)) - } - next(error) - } finally { - sseStreamer.removeClient(chatId) - } - } else { - const apiResponse = await predictionsServices.buildChatflow(req) - return res.json(apiResponse) - } - } else { - throw new InternalFlowiseError(StatusCodes.UNAUTHORIZED, `This site is not allowed to access this chatbot`) - } - } catch (error) { - next(error) - } -} - -const getRateLimiterMiddleware = async (req: Request, res: Response, next: NextFunction) => { - try { - return getRateLimiter(req, res, next) - } catch (error) { - next(error) - } -} - -export default { - createPrediction, - getRateLimiterMiddleware -} diff --git a/studio-frontend/packages/server/src/controllers/prompts-lists/index.ts b/studio-frontend/packages/server/src/controllers/prompts-lists/index.ts deleted file mode 100644 index 88a048c..0000000 --- a/studio-frontend/packages/server/src/controllers/prompts-lists/index.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { Request, Response, NextFunction } from 'express' -import promptsListsService from '../../services/prompts-lists' - -// Prompt from Hub -const createPromptsList = async (req: Request, res: Response, next: NextFunction) => { - try { - const apiResponse = await promptsListsService.createPromptsList(req.body) - return res.json(apiResponse) - } catch (error) { - next(error) - } -} - -export default { - createPromptsList -} diff --git a/studio-frontend/packages/server/src/controllers/stats/index.ts b/studio-frontend/packages/server/src/controllers/stats/index.ts deleted file mode 100644 index a964645..0000000 --- a/studio-frontend/packages/server/src/controllers/stats/index.ts +++ /dev/null @@ -1,71 +0,0 @@ -import { StatusCodes } from 'http-status-codes' -import { Request, Response, NextFunction } from 'express' -import statsService from '../../services/stats' -import { ChatMessageRatingType, chatType } from '../../Interface' -import { InternalFlowiseError } from '../../errors/internalFlowiseError' -import { getErrorMessage } from '../../errors/utils' - -const getChatflowStats = async (req: Request, res: Response, next: NextFunction) => { - try { - if (typeof req.params === 'undefined' || !req.params.id) { - throw new InternalFlowiseError(StatusCodes.PRECONDITION_FAILED, `Error: statsController.getChatflowStats - id not provided!`) - } - const chatflowid = req.params.id - let chatTypeFilter = req.query?.chatType as chatType | undefined - const startDate = req.query?.startDate as string | undefined - const endDate = req.query?.endDate as string | undefined - let feedbackTypeFilters = req.query?.feedbackType as ChatMessageRatingType[] | undefined - if (chatTypeFilter) { - try { - const chatTypeFilterArray = JSON.parse(chatTypeFilter) - if (chatTypeFilterArray.includes(chatType.EXTERNAL) && chatTypeFilterArray.includes(chatType.INTERNAL)) { - chatTypeFilter = undefined - } else if (chatTypeFilterArray.includes(chatType.EXTERNAL)) { - chatTypeFilter = chatType.EXTERNAL - } else if (chatTypeFilterArray.includes(chatType.INTERNAL)) { - chatTypeFilter = chatType.INTERNAL - } - } catch (e) { - throw new InternalFlowiseError( - StatusCodes.INTERNAL_SERVER_ERROR, - `Error: statsController.getChatflowStats - ${getErrorMessage(e)}` - ) - } - } - if (feedbackTypeFilters) { - try { - const feedbackTypeFilterArray = JSON.parse(JSON.stringify(feedbackTypeFilters)) - if ( - feedbackTypeFilterArray.includes(ChatMessageRatingType.THUMBS_UP) && - feedbackTypeFilterArray.includes(ChatMessageRatingType.THUMBS_DOWN) - ) { - feedbackTypeFilters = [ChatMessageRatingType.THUMBS_UP, ChatMessageRatingType.THUMBS_DOWN] - } else if (feedbackTypeFilterArray.includes(ChatMessageRatingType.THUMBS_UP)) { - feedbackTypeFilters = [ChatMessageRatingType.THUMBS_UP] - } else if (feedbackTypeFilterArray.includes(ChatMessageRatingType.THUMBS_DOWN)) { - feedbackTypeFilters = [ChatMessageRatingType.THUMBS_DOWN] - } else { - feedbackTypeFilters = undefined - } - } catch (e) { - return res.status(500).send(e) - } - } - const apiResponse = await statsService.getChatflowStats( - chatflowid, - chatTypeFilter, - startDate, - endDate, - '', - true, - feedbackTypeFilters - ) - return res.json(apiResponse) - } catch (error) { - next(error) - } -} - -export default { - getChatflowStats -} diff --git a/studio-frontend/packages/server/src/controllers/upsert-history/index.ts b/studio-frontend/packages/server/src/controllers/upsert-history/index.ts deleted file mode 100644 index 43f36d5..0000000 --- a/studio-frontend/packages/server/src/controllers/upsert-history/index.ts +++ /dev/null @@ -1,30 +0,0 @@ -import { Request, Response, NextFunction } from 'express' -import upsertHistoryService from '../../services/upsert-history' - -const getAllUpsertHistory = async (req: Request, res: Response, next: NextFunction) => { - try { - const sortOrder = req.query?.order as string | undefined - const chatflowid = req.params?.id as string | undefined - const startDate = req.query?.startDate as string | undefined - const endDate = req.query?.endDate as string | undefined - const apiResponse = await upsertHistoryService.getAllUpsertHistory(sortOrder, chatflowid, startDate, endDate) - return res.json(apiResponse) - } catch (error) { - next(error) - } -} - -const patchDeleteUpsertHistory = async (req: Request, res: Response, next: NextFunction) => { - try { - const ids = req.body.ids ?? [] - const apiResponse = await upsertHistoryService.patchDeleteUpsertHistory(ids) - return res.json(apiResponse) - } catch (error) { - next(error) - } -} - -export default { - getAllUpsertHistory, - patchDeleteUpsertHistory -} diff --git a/studio-frontend/packages/server/src/controllers/vectors/index.ts b/studio-frontend/packages/server/src/controllers/vectors/index.ts deleted file mode 100644 index 5d10bb6..0000000 --- a/studio-frontend/packages/server/src/controllers/vectors/index.ts +++ /dev/null @@ -1,36 +0,0 @@ -import { Request, Response, NextFunction } from 'express' -import vectorsService from '../../services/vectors' -import { getRateLimiter } from '../../utils/rateLimit' - -const getRateLimiterMiddleware = async (req: Request, res: Response, next: NextFunction) => { - try { - return getRateLimiter(req, res, next) - } catch (error) { - next(error) - } -} - -const upsertVectorMiddleware = async (req: Request, res: Response, next: NextFunction) => { - try { - const apiResponse = await vectorsService.upsertVectorMiddleware(req) - return res.json(apiResponse) - } catch (error) { - next(error) - } -} - -const createInternalUpsert = async (req: Request, res: Response, next: NextFunction) => { - try { - const isInternal = true - const apiResponse = await vectorsService.upsertVectorMiddleware(req, isInternal) - return res.json(apiResponse) - } catch (error) { - next(error) - } -} - -export default { - upsertVectorMiddleware, - createInternalUpsert, - getRateLimiterMiddleware -} diff --git a/studio-frontend/packages/server/src/index.ts b/studio-frontend/packages/server/src/index.ts index 06b236d..3939fcb 100644 --- a/studio-frontend/packages/server/src/index.ts +++ b/studio-frontend/packages/server/src/index.ts @@ -13,14 +13,11 @@ import { getDataSource } from './DataSource' import { NodesPool } from './NodesPool' import { ChatFlow } from './database/entities/ChatFlow' import { ChatflowPool } from './ChatflowPool' -import { CachePool } from './CachePool' import { initializeRateLimiter } from './utils/rateLimit' import { getAPIKeys } from './utils/apiKey' import { sanitizeMiddleware, getCorsOptions, getAllowedIframeOrigins } from './utils/XSS' -import { Telemetry } from './utils/telemetry' import flowiseApiV1Router from './routes' import errorHandlerMiddleware from './middlewares/errors' -import { SSEStreamer } from './utils/SSEStreamer' import { validateAPIKey } from './utils/validateKey' import { setupFineTuningDownloadHandlers } from './ws/finetuningDownload' import { setupFineTuningStatusHandlers } from './ws/finetuningStatus' @@ -37,10 +34,7 @@ export class App { app: express.Application nodesPool: NodesPool chatflowPool: ChatflowPool - cachePool: CachePool - telemetry: Telemetry AppDataSource: DataSource = getDataSource() - sseStreamer: SSEStreamer constructor() { this.app = express() @@ -78,11 +72,6 @@ export class App { const AllChatFlow: IChatFlow[] = await getAllChatFlow() await initializeRateLimiter(AllChatFlow) - // Initialize cache pool - this.cachePool = new CachePool() - - // Initialize telemetry - this.telemetry = new Telemetry() logger.info('📦 [server]: Data Source has been initialized!') } catch (error) { logger.error('❌ [server]: Error during Data Source initialization:', error) @@ -136,11 +125,6 @@ export class App { '/api/v1/vector/upsert/', '/api/v1/node-icon/', '/api/v1/components-credentials-icon/', - '/api/v1/chatflows-streaming', - '/api/v1/chatflows-uploads', - '/api/v1/openai-assistants-file/download', - '/api/v1/feedback', - '/api/v1/leads', '/api/v1/get-upload-file', '/api/v1/ip', '/api/v1/ping', @@ -211,7 +195,6 @@ export class App { } this.app.use('/api/v1', flowiseApiV1Router) - this.sseStreamer = new SSEStreamer(this.app) // ---------------------------------------- // Configure number of proxies in Host Environment @@ -267,9 +250,7 @@ export class App { async stopApp() { try { - const removePromises: any[] = [] - removePromises.push(this.telemetry.flush()) - await Promise.all(removePromises) + logger.info('[server]: Flowise Server shutting down...') } catch (e) { logger.error(`❌[server]: Flowise Server shut down error: ${e}`) } diff --git a/studio-frontend/packages/server/src/routes/assistants/index.ts b/studio-frontend/packages/server/src/routes/assistants/index.ts deleted file mode 100644 index 1499855..0000000 --- a/studio-frontend/packages/server/src/routes/assistants/index.ts +++ /dev/null @@ -1,19 +0,0 @@ -import express from 'express' -import assistantsController from '../../controllers/assistants' - -const router: express.Router = express.Router() - -// CREATE -router.post('/', assistantsController.createAssistant) - -// READ -router.get('/', assistantsController.getAllAssistants) -router.get(['/', '/:id'], assistantsController.getAssistantById) - -// UPDATE -router.put(['/', '/:id'], assistantsController.updateAssistant) - -// DELETE -router.delete(['/', '/:id'], assistantsController.deleteAssistant) - -export default router diff --git a/studio-frontend/packages/server/src/routes/chatflows-streaming/index.ts b/studio-frontend/packages/server/src/routes/chatflows-streaming/index.ts deleted file mode 100644 index 41ad33b..0000000 --- a/studio-frontend/packages/server/src/routes/chatflows-streaming/index.ts +++ /dev/null @@ -1,9 +0,0 @@ -import express from 'express' -import chatflowsController from '../../controllers/chatflows' - -const router: express.Router = express.Router() - -// READ -router.get(['/', '/:id'], chatflowsController.checkIfChatflowIsValidForStreaming) - -export default router diff --git a/studio-frontend/packages/server/src/routes/chatflows-uploads/index.ts b/studio-frontend/packages/server/src/routes/chatflows-uploads/index.ts deleted file mode 100644 index bcc55a8..0000000 --- a/studio-frontend/packages/server/src/routes/chatflows-uploads/index.ts +++ /dev/null @@ -1,9 +0,0 @@ -import express from 'express' -import chatflowsController from '../../controllers/chatflows' - -const router: express.Router = express.Router() - -// READ -router.get(['/', '/:id'], chatflowsController.checkIfChatflowIsValidForUploads) - -export default router diff --git a/studio-frontend/packages/server/src/routes/documentstore/index.ts b/studio-frontend/packages/server/src/routes/documentstore/index.ts deleted file mode 100644 index ddc774a..0000000 --- a/studio-frontend/packages/server/src/routes/documentstore/index.ts +++ /dev/null @@ -1,54 +0,0 @@ -import express from 'express' -import documentStoreController from '../../controllers/documentstore' -const router: express.Router = express.Router() - -/** Document Store Routes */ -// Create document store -router.post('/store', documentStoreController.createDocumentStore) -// List all stores -router.get('/store', documentStoreController.getAllDocumentStores) -// Get specific store -router.get('/store/:id', documentStoreController.getDocumentStoreById) -// Update documentStore -router.put('/store/:id', documentStoreController.updateDocumentStore) -// Delete documentStore -router.delete('/store/:id', documentStoreController.deleteDocumentStore) - -/** Component Nodes = Document Store - Loaders */ -// Get all loaders -router.get('/components/loaders', documentStoreController.getDocumentLoaders) - -// delete loader from document store -router.delete('/loader/:id/:loaderId', documentStoreController.deleteLoaderFromDocumentStore) -// chunking preview -router.post('/loader/preview', documentStoreController.previewFileChunks) -// chunking process -router.post('/loader/process', documentStoreController.processFileChunks) - -/** Document Store - Loaders - Chunks */ -// delete specific file chunk from the store -router.delete('/chunks/:storeId/:loaderId/:chunkId', documentStoreController.deleteDocumentStoreFileChunk) -// edit specific file chunk from the store -router.put('/chunks/:storeId/:loaderId/:chunkId', documentStoreController.editDocumentStoreFileChunk) -// Get all file chunks from the store -router.get('/chunks/:storeId/:fileId/:pageNo', documentStoreController.getDocumentStoreFileChunks) - -// add chunks to the selected vector store -router.post('/vectorstore/insert', documentStoreController.insertIntoVectorStore) -// save the selected vector store -router.post('/vectorstore/save', documentStoreController.saveVectorStoreConfig) -// delete data from the selected vector store -router.delete('/vectorstore/:storeId', documentStoreController.deleteVectorStoreFromStore) -// query the vector store -router.post('/vectorstore/query', documentStoreController.queryVectorStore) -// Get all embedding providers -router.get('/components/embeddings', documentStoreController.getEmbeddingProviders) -// Get all vector store providers -router.get('/components/vectorstore', documentStoreController.getVectorStoreProviders) -// Get all Record Manager providers -router.get('/components/recordmanager', documentStoreController.getRecordManagerProviders) - -// update the selected vector store from the playground -router.post('/vectorstore/update', documentStoreController.updateVectorStoreConfigOnly) - -export default router diff --git a/studio-frontend/packages/server/src/routes/evaluation/index.ts b/studio-frontend/packages/server/src/routes/evaluation/index.ts new file mode 100644 index 0000000..2fbbfc4 --- /dev/null +++ b/studio-frontend/packages/server/src/routes/evaluation/index.ts @@ -0,0 +1,94 @@ +import express from 'express' +import axios from 'axios' +import { Request, Response, NextFunction } from 'express' + +const router: express.Router = express.Router() + +const STUDIO_SERVER_URL = process.env.STUDIO_SERVER_URL || 'http://studio-backend.studio.svc.cluster.local:5000' + +/** + * Generic proxy helper – forwards a request to studio-backend and pipes the response back. + */ +const proxy = async ( + method: 'get' | 'post' | 'put' | 'delete' | 'patch', + targetPath: string, + req: Request, + res: Response, + next: NextFunction +) => { + try { + const url = `${STUDIO_SERVER_URL}/${targetPath}` + const response = await axios({ + method, + url, + data: ['post', 'put', 'patch'].includes(method) ? req.body : undefined, + params: req.query, + headers: { 'Content-Type': 'application/json' }, + timeout: 60_000 + }) + return res.status(response.status).json(response.data) + } catch (error: unknown) { + const err = error as any + if (err?.response?.status) { + return res.status(err.response.status).json(err.response.data) + } + next(error) + } +} + +// ── Sandboxes ───────────────────────────────────────────────────────────────── +// GET /api/v1/evaluation/sandboxes → studio-backend/sandbox/list +router.get('/sandboxes', (req: Request, res: Response, next: NextFunction) => + proxy('get', 'studio-backend/sandbox/list', req, res, next) +) + +// ── Models ──────────────────────────────────────────────────────────────────── +// GET /api/v1/evaluation/models → studio-backend/evaluation/models +router.get('/models', (req: Request, res: Response, next: NextFunction) => + proxy('get', 'studio-backend/evaluation/models', req, res, next) +) + +// POST /api/v1/evaluation/models/pull → studio-backend/evaluation/models/pull +router.post('/models/pull', (req: Request, res: Response, next: NextFunction) => + proxy('post', 'studio-backend/evaluation/models/pull', req, res, next) +) + +// ── Datasets ────────────────────────────────────────────────────────────────── +router.get('/datasets', (req: Request, res: Response, next: NextFunction) => + proxy('get', 'studio-backend/evaluation/datasets', req, res, next) +) + +router.get('/datasets/:id', (req: Request, res: Response, next: NextFunction) => + proxy('get', `studio-backend/evaluation/datasets/${req.params.id}`, req, res, next) +) + +router.post('/datasets', (req: Request, res: Response, next: NextFunction) => + proxy('post', 'studio-backend/evaluation/datasets', req, res, next) +) + +router.post('/datasets/synthesize', (req: Request, res: Response, next: NextFunction) => + proxy('post', 'studio-backend/evaluation/datasets/synthesize', req, res, next) +) + +router.delete('/datasets/:id', (req: Request, res: Response, next: NextFunction) => + proxy('delete', `studio-backend/evaluation/datasets/${req.params.id}`, req, res, next) +) + +// ── Runs ────────────────────────────────────────────────────────────────────── +router.get('/runs', (req: Request, res: Response, next: NextFunction) => + proxy('get', 'studio-backend/evaluation/runs', req, res, next) +) + +router.get('/runs/:id', (req: Request, res: Response, next: NextFunction) => + proxy('get', `studio-backend/evaluation/runs/${req.params.id}`, req, res, next) +) + +router.post('/runs', (req: Request, res: Response, next: NextFunction) => + proxy('post', 'studio-backend/evaluation/runs', req, res, next) +) + +router.delete('/runs/:id', (req: Request, res: Response, next: NextFunction) => + proxy('delete', `studio-backend/evaluation/runs/${req.params.id}`, req, res, next) +) + +export default router diff --git a/studio-frontend/packages/server/src/routes/feedback/index.ts b/studio-frontend/packages/server/src/routes/feedback/index.ts deleted file mode 100644 index b642535..0000000 --- a/studio-frontend/packages/server/src/routes/feedback/index.ts +++ /dev/null @@ -1,14 +0,0 @@ -import express from 'express' -import feedbackController from '../../controllers/feedback' -const router: express.Router = express.Router() - -// CREATE -router.post(['/', '/:id'], feedbackController.createChatMessageFeedbackForChatflow) - -// READ -router.get(['/', '/:id'], feedbackController.getAllChatMessageFeedback) - -// UPDATE -router.put(['/', '/:id'], feedbackController.updateChatMessageFeedbackForChatflow) - -export default router diff --git a/studio-frontend/packages/server/src/routes/fetch-links/index.ts b/studio-frontend/packages/server/src/routes/fetch-links/index.ts deleted file mode 100644 index 462296a..0000000 --- a/studio-frontend/packages/server/src/routes/fetch-links/index.ts +++ /dev/null @@ -1,8 +0,0 @@ -import express from 'express' -import fetchLinksController from '../../controllers/fetch-links' -const router: express.Router = express.Router() - -// READ -router.get('/', fetchLinksController.getAllLinks) - -export default router diff --git a/studio-frontend/packages/server/src/routes/index.ts b/studio-frontend/packages/server/src/routes/index.ts index bd57a14..ee0c879 100644 --- a/studio-frontend/packages/server/src/routes/index.ts +++ b/studio-frontend/packages/server/src/routes/index.ts @@ -1,45 +1,25 @@ import express from 'express' import apikeyRouter from './apikey' -import assistantsRouter from './assistants' import chatMessageRouter from './chat-messages' import chatflowsRouter from './chatflows' import chatflowsSandboxRouter from './chatflows-sandbox' -import chatflowsStreamingRouter from './chatflows-streaming' -import chatflowsUploadsRouter from './chatflows-uploads' import componentsCredentialsRouter from './components-credentials' import componentsCredentialsIconRouter from './components-credentials-icon' import credentialsRouter from './credentials' -import documentStoreRouter from './documentstore' import exportImportRouter from './export-import' -import feedbackRouter from './feedback' -import fetchLinksRouter from './fetch-links' import finetuningRouter from './finetuning' +import evaluationRouter from './evaluation' import flowConfigRouter from './flow-config' import getUploadFileRouter from './get-upload-file' import getUploadPathRouter from './get-upload-path' -import internalChatmessagesRouter from './internal-chat-messages' -import internalPredictionRouter from './internal-predictions' -import leadsRouter from './leads' -import loadPromptRouter from './load-prompts' -import marketplacesRouter from './marketplaces' import nodeConfigRouter from './node-configs' import nodeCustomFunctionRouter from './node-custom-functions' import nodeIconRouter from './node-icons' import nodeLoadMethodRouter from './node-load-methods' import nodesRouter from './nodes' -import openaiAssistantsRouter from './openai-assistants' -import openaiAssistantsFileRouter from './openai-assistants-files' -import openaiAssistantsVectorStoreRouter from './openai-assistants-vector-store' import pingRouter from './ping' -import predictionRouter from './predictions' -import promptListsRouter from './prompts-lists' -import publicChatbotRouter from './public-chatbots' -import publicChatflowsRouter from './public-chatflows' -import statsRouter from './stats' import toolsRouter from './tools' -import upsertHistoryRouter from './upsert-history' import variablesRouter from './variables' -import vectorRouter from './vectors' import verifyRouter from './verify' import versionRouter from './versions' @@ -47,46 +27,27 @@ const router: express.Router = express.Router() router.use('/ping', pingRouter) router.use('/apikey', apikeyRouter) -router.use('/assistants', assistantsRouter) router.use('/chatflows', chatflowsRouter) router.use('/chatflows-sandbox', chatflowsSandboxRouter) -router.use('/chatflows-streaming', chatflowsStreamingRouter) router.use('/chatmessage', chatMessageRouter) router.use('/components-credentials', componentsCredentialsRouter) router.use('/components-credentials-icon', componentsCredentialsIconRouter) -router.use('/chatflows-uploads', chatflowsUploadsRouter) router.use('/credentials', credentialsRouter) -router.use('/document-store', documentStoreRouter) router.use('/export-import', exportImportRouter) -router.use('/feedback', feedbackRouter) -router.use('/fetch-links', fetchLinksRouter) router.use('/finetuning', finetuningRouter) +router.use('/evaluation', evaluationRouter) router.use('/flow-config', flowConfigRouter) -router.use('/internal-chatmessage', internalChatmessagesRouter) -router.use('/internal-prediction', internalPredictionRouter) router.use('/get-upload-file', getUploadFileRouter) router.use('/get-upload-path', getUploadPathRouter) -router.use('/leads', leadsRouter) -router.use('/load-prompt', loadPromptRouter) -router.use('/marketplaces', marketplacesRouter) router.use('/node-config', nodeConfigRouter) router.use('/node-custom-function', nodeCustomFunctionRouter) router.use('/node-icon', nodeIconRouter) router.use('/node-load-method', nodeLoadMethodRouter) router.use('/nodes', nodesRouter) -router.use('/openai-assistants', openaiAssistantsRouter) -router.use('/openai-assistants-file', openaiAssistantsFileRouter) -router.use('/openai-assistants-vector-store', openaiAssistantsVectorStoreRouter) -router.use('/prediction', predictionRouter) -router.use('/prompts-list', promptListsRouter) -router.use('/public-chatbotConfig', publicChatbotRouter) -router.use('/public-chatflows', publicChatflowsRouter) -router.use('/stats', statsRouter) router.use('/tools', toolsRouter) router.use('/variables', variablesRouter) -router.use('/vector', vectorRouter) router.use('/verify', verifyRouter) router.use('/version', versionRouter) -router.use('/upsert-history', upsertHistoryRouter) export default router + diff --git a/studio-frontend/packages/server/src/routes/internal-chat-messages/index.ts b/studio-frontend/packages/server/src/routes/internal-chat-messages/index.ts deleted file mode 100644 index b5814c8..0000000 --- a/studio-frontend/packages/server/src/routes/internal-chat-messages/index.ts +++ /dev/null @@ -1,14 +0,0 @@ -import express from 'express' -import chatMessagesController from '../../controllers/chat-messages' -const router: express.Router = express.Router() - -// CREATE - -// READ -router.get(['/', '/:id'], chatMessagesController.getAllInternalChatMessages) - -// UPDATE - -// DELETE - -export default router diff --git a/studio-frontend/packages/server/src/routes/internal-predictions/index.ts b/studio-frontend/packages/server/src/routes/internal-predictions/index.ts deleted file mode 100644 index 80fb8e1..0000000 --- a/studio-frontend/packages/server/src/routes/internal-predictions/index.ts +++ /dev/null @@ -1,8 +0,0 @@ -import express from 'express' -import internalPredictionsController from '../../controllers/internal-predictions' -const router: express.Router = express.Router() - -// CREATE -router.post(['/', '/:id'], internalPredictionsController.createInternalPrediction) - -export default router diff --git a/studio-frontend/packages/server/src/routes/leads/index.ts b/studio-frontend/packages/server/src/routes/leads/index.ts deleted file mode 100644 index 8acdfc4..0000000 --- a/studio-frontend/packages/server/src/routes/leads/index.ts +++ /dev/null @@ -1,11 +0,0 @@ -import express from 'express' -import leadsController from '../../controllers/leads' -const router: express.Router = express.Router() - -// CREATE -router.post('/', leadsController.createLeadInChatflow) - -// READ -router.get(['/', '/:id'], leadsController.getAllLeadsForChatflow) - -export default router diff --git a/studio-frontend/packages/server/src/routes/load-prompts/index.ts b/studio-frontend/packages/server/src/routes/load-prompts/index.ts deleted file mode 100644 index b11e7c1..0000000 --- a/studio-frontend/packages/server/src/routes/load-prompts/index.ts +++ /dev/null @@ -1,8 +0,0 @@ -import express from 'express' -import loadPromptsController from '../../controllers/load-prompts' -const router: express.Router = express.Router() - -// CREATE -router.post('/', loadPromptsController.createPrompt) - -export default router diff --git a/studio-frontend/packages/server/src/routes/marketplaces/index.ts b/studio-frontend/packages/server/src/routes/marketplaces/index.ts deleted file mode 100644 index 3242789..0000000 --- a/studio-frontend/packages/server/src/routes/marketplaces/index.ts +++ /dev/null @@ -1,16 +0,0 @@ -import express from 'express' -import marketplacesController from '../../controllers/marketplaces' -const router: express.Router = express.Router() - -// READ -router.get('/templates', marketplacesController.getAllTemplates) - -router.post('/custom', marketplacesController.saveCustomTemplate) - -// READ -router.get('/custom', marketplacesController.getAllCustomTemplates) - -// DELETE -router.delete(['/', '/custom/:id'], marketplacesController.deleteCustomTemplate) - -export default router diff --git a/studio-frontend/packages/server/src/routes/openai-assistants-files/index.ts b/studio-frontend/packages/server/src/routes/openai-assistants-files/index.ts deleted file mode 100644 index 419fcef..0000000 --- a/studio-frontend/packages/server/src/routes/openai-assistants-files/index.ts +++ /dev/null @@ -1,12 +0,0 @@ -import express from 'express' -import multer from 'multer' -import path from 'path' -import openaiAssistantsController from '../../controllers/openai-assistants' - -const router: express.Router = express.Router() -const upload = multer({ dest: `${path.join(__dirname, '..', '..', '..', 'uploads')}/` }) - -router.post('/download/', openaiAssistantsController.getFileFromAssistant) -router.post('/upload/', upload.array('files'), openaiAssistantsController.uploadAssistantFiles) - -export default router diff --git a/studio-frontend/packages/server/src/routes/openai-assistants-vector-store/index.ts b/studio-frontend/packages/server/src/routes/openai-assistants-vector-store/index.ts deleted file mode 100644 index efd0f8e..0000000 --- a/studio-frontend/packages/server/src/routes/openai-assistants-vector-store/index.ts +++ /dev/null @@ -1,30 +0,0 @@ -import express from 'express' -import multer from 'multer' -import path from 'path' -import openaiAssistantsVectorStoreController from '../../controllers/openai-assistants-vector-store' - -const router: express.Router = express.Router() -const upload = multer({ dest: `${path.join(__dirname, '..', '..', '..', 'uploads')}/` }) - -// CREATE -router.post('/', openaiAssistantsVectorStoreController.createAssistantVectorStore) - -// READ -router.get('/:id', openaiAssistantsVectorStoreController.getAssistantVectorStore) - -// LIST -router.get('/', openaiAssistantsVectorStoreController.listAssistantVectorStore) - -// UPDATE -router.put(['/', '/:id'], openaiAssistantsVectorStoreController.updateAssistantVectorStore) - -// DELETE -router.delete(['/', '/:id'], openaiAssistantsVectorStoreController.deleteAssistantVectorStore) - -// POST -router.post('/:id', upload.array('files'), openaiAssistantsVectorStoreController.uploadFilesToAssistantVectorStore) - -// DELETE -router.patch(['/', '/:id'], openaiAssistantsVectorStoreController.deleteFilesFromAssistantVectorStore) - -export default router diff --git a/studio-frontend/packages/server/src/routes/openai-assistants/index.ts b/studio-frontend/packages/server/src/routes/openai-assistants/index.ts deleted file mode 100644 index cfac2ea..0000000 --- a/studio-frontend/packages/server/src/routes/openai-assistants/index.ts +++ /dev/null @@ -1,15 +0,0 @@ -import express from 'express' -import openaiAssistantsController from '../../controllers/openai-assistants' -const router: express.Router = express.Router() - -// CREATE - -// READ -router.get('/', openaiAssistantsController.getAllOpenaiAssistants) -router.get(['/', '/:id'], openaiAssistantsController.getSingleOpenaiAssistant) - -// UPDATE - -// DELETE - -export default router diff --git a/studio-frontend/packages/server/src/routes/predictions/index.ts b/studio-frontend/packages/server/src/routes/predictions/index.ts deleted file mode 100644 index a8cf640..0000000 --- a/studio-frontend/packages/server/src/routes/predictions/index.ts +++ /dev/null @@ -1,13 +0,0 @@ -import express from 'express' -import multer from 'multer' -import path from 'path' -import predictionsController from '../../controllers/predictions' - -const router: express.Router = express.Router() - -const upload = multer({ dest: `${path.join(__dirname, '..', '..', '..', 'uploads')}/` }) - -// CREATE -router.post(['/', '/:id'], upload.array('files'), predictionsController.getRateLimiterMiddleware, predictionsController.createPrediction) - -export default router diff --git a/studio-frontend/packages/server/src/routes/prompts-lists/index.ts b/studio-frontend/packages/server/src/routes/prompts-lists/index.ts deleted file mode 100644 index 51800a4..0000000 --- a/studio-frontend/packages/server/src/routes/prompts-lists/index.ts +++ /dev/null @@ -1,8 +0,0 @@ -import express from 'express' -import promptsListController from '../../controllers/prompts-lists' -const router: express.Router = express.Router() - -// CREATE -router.post('/', promptsListController.createPromptsList) - -export default router diff --git a/studio-frontend/packages/server/src/routes/public-chatbots/index.ts b/studio-frontend/packages/server/src/routes/public-chatbots/index.ts deleted file mode 100644 index 7750fe7..0000000 --- a/studio-frontend/packages/server/src/routes/public-chatbots/index.ts +++ /dev/null @@ -1,14 +0,0 @@ -import express from 'express' -import chatflowsController from '../../controllers/chatflows' -const router: express.Router = express.Router() - -// CREATE - -// READ -router.get(['/', '/:id'], chatflowsController.getSinglePublicChatbotConfig) - -// UPDATE - -// DELETE - -export default router diff --git a/studio-frontend/packages/server/src/routes/public-chatflows/index.ts b/studio-frontend/packages/server/src/routes/public-chatflows/index.ts deleted file mode 100644 index 31c930f..0000000 --- a/studio-frontend/packages/server/src/routes/public-chatflows/index.ts +++ /dev/null @@ -1,14 +0,0 @@ -import express from 'express' -import chatflowsController from '../../controllers/chatflows' -const router: express.Router = express.Router() - -// CREATE - -// READ -router.get(['/', '/:id'], chatflowsController.getSinglePublicChatflow) - -// UPDATE - -// DELETE - -export default router diff --git a/studio-frontend/packages/server/src/routes/stats/index.ts b/studio-frontend/packages/server/src/routes/stats/index.ts deleted file mode 100644 index 7708b3a..0000000 --- a/studio-frontend/packages/server/src/routes/stats/index.ts +++ /dev/null @@ -1,9 +0,0 @@ -import express from 'express' -import statsController from '../../controllers/stats' - -const router: express.Router = express.Router() - -// READ -router.get(['/', '/:id'], statsController.getChatflowStats) - -export default router diff --git a/studio-frontend/packages/server/src/routes/upsert-history/index.ts b/studio-frontend/packages/server/src/routes/upsert-history/index.ts deleted file mode 100644 index cdf5512..0000000 --- a/studio-frontend/packages/server/src/routes/upsert-history/index.ts +++ /dev/null @@ -1,15 +0,0 @@ -import express from 'express' -import upsertHistoryController from '../../controllers/upsert-history' -const router: express.Router = express.Router() - -// CREATE - -// READ -router.get(['/', '/:id'], upsertHistoryController.getAllUpsertHistory) - -// PATCH -router.patch('/', upsertHistoryController.patchDeleteUpsertHistory) - -// DELETE - -export default router diff --git a/studio-frontend/packages/server/src/routes/vectors/index.ts b/studio-frontend/packages/server/src/routes/vectors/index.ts deleted file mode 100644 index 843ea63..0000000 --- a/studio-frontend/packages/server/src/routes/vectors/index.ts +++ /dev/null @@ -1,19 +0,0 @@ -import express from 'express' -import multer from 'multer' -import path from 'path' -import vectorsController from '../../controllers/vectors' - -const router: express.Router = express.Router() - -const upload = multer({ dest: `${path.join(__dirname, '..', '..', '..', 'uploads')}/` }) - -// CREATE -router.post( - ['/upsert/', '/upsert/:id'], - upload.array('files'), - vectorsController.getRateLimiterMiddleware, - vectorsController.upsertVectorMiddleware -) -router.post(['/internal-upsert/', '/internal-upsert/:id'], upload.array('files'), vectorsController.createInternalUpsert) - -export default router diff --git a/studio-frontend/packages/server/src/services/assistants/index.ts b/studio-frontend/packages/server/src/services/assistants/index.ts deleted file mode 100644 index 10b176a..0000000 --- a/studio-frontend/packages/server/src/services/assistants/index.ts +++ /dev/null @@ -1,348 +0,0 @@ -import OpenAI from 'openai' -import { StatusCodes } from 'http-status-codes' -import { uniqWith, isEqual, cloneDeep } from 'lodash' -import { getRunningExpressApp } from '../../utils/getRunningExpressApp' -import { Assistant } from '../../database/entities/Assistant' -import { Credential } from '../../database/entities/Credential' -import { decryptCredentialData, getAppVersion } from '../../utils' -import { InternalFlowiseError } from '../../errors/internalFlowiseError' -import { getErrorMessage } from '../../errors/utils' -import { DeleteResult } from 'typeorm' - -const createAssistant = async (requestBody: any): Promise => { - try { - const appServer = getRunningExpressApp() - if (!requestBody.details) { - throw new InternalFlowiseError(StatusCodes.INTERNAL_SERVER_ERROR, `Invalid request body`) - } - const assistantDetails = JSON.parse(requestBody.details) - try { - const credential = await appServer.AppDataSource.getRepository(Credential).findOneBy({ - id: requestBody.credential - }) - - if (!credential) { - throw new InternalFlowiseError(StatusCodes.NOT_FOUND, `Credential ${requestBody.credential} not found`) - } - - // Decrpyt credentialData - const decryptedCredentialData = await decryptCredentialData(credential.encryptedData) - const openAIApiKey = decryptedCredentialData['openAIApiKey'] - if (!openAIApiKey) { - throw new InternalFlowiseError(StatusCodes.NOT_FOUND, `OpenAI ApiKey not found`) - } - const openai = new OpenAI({ apiKey: openAIApiKey }) - - // Prepare tools - let tools = [] - if (assistantDetails.tools) { - for (const tool of assistantDetails.tools ?? []) { - tools.push({ - type: tool - }) - } - } - - // Save tool_resources to be stored later into database - const savedToolResources = cloneDeep(assistantDetails.tool_resources) - - // Cleanup tool_resources for creating assistant - if (assistantDetails.tool_resources) { - for (const toolResource in assistantDetails.tool_resources) { - if (toolResource === 'file_search') { - assistantDetails.tool_resources['file_search'] = { - vector_store_ids: assistantDetails.tool_resources['file_search'].vector_store_ids - } - } else if (toolResource === 'code_interpreter') { - assistantDetails.tool_resources['code_interpreter'] = { - file_ids: assistantDetails.tool_resources['code_interpreter'].file_ids - } - } - } - } - - // If the assistant doesn't exist, create a new one - if (!assistantDetails.id) { - const newAssistant = await openai.beta.assistants.create({ - name: assistantDetails.name, - description: assistantDetails.description, - instructions: assistantDetails.instructions, - model: assistantDetails.model, - tools, - tool_resources: assistantDetails.tool_resources, - temperature: assistantDetails.temperature, - top_p: assistantDetails.top_p - }) - assistantDetails.id = newAssistant.id - } else { - const retrievedAssistant = await openai.beta.assistants.retrieve(assistantDetails.id) - let filteredTools = uniqWith([...retrievedAssistant.tools.filter((tool) => tool.type === 'function'), ...tools], isEqual) - // Remove empty functions - filteredTools = filteredTools.filter((tool) => !(tool.type === 'function' && !(tool as any).function)) - - await openai.beta.assistants.update(assistantDetails.id, { - name: assistantDetails.name, - description: assistantDetails.description ?? '', - instructions: assistantDetails.instructions ?? '', - model: assistantDetails.model, - tools: filteredTools, - tool_resources: assistantDetails.tool_resources, - temperature: assistantDetails.temperature, - top_p: assistantDetails.top_p - }) - } - - const newAssistantDetails = { - ...assistantDetails - } - if (savedToolResources) newAssistantDetails.tool_resources = savedToolResources - - requestBody.details = JSON.stringify(newAssistantDetails) - } catch (error) { - throw new InternalFlowiseError(StatusCodes.INTERNAL_SERVER_ERROR, `Error creating new assistant - ${getErrorMessage(error)}`) - } - const newAssistant = new Assistant() - Object.assign(newAssistant, requestBody) - - const assistant = appServer.AppDataSource.getRepository(Assistant).create(newAssistant) - const dbResponse = await appServer.AppDataSource.getRepository(Assistant).save(assistant) - - await appServer.telemetry.sendTelemetry('assistant_created', { - version: await getAppVersion(), - assistantId: dbResponse.id - }) - return dbResponse - } catch (error) { - throw new InternalFlowiseError( - StatusCodes.INTERNAL_SERVER_ERROR, - `Error: assistantsService.createAssistant - ${getErrorMessage(error)}` - ) - } -} - -const deleteAssistant = async (assistantId: string, isDeleteBoth: any): Promise => { - try { - const appServer = getRunningExpressApp() - const assistant = await appServer.AppDataSource.getRepository(Assistant).findOneBy({ - id: assistantId - }) - if (!assistant) { - throw new InternalFlowiseError(StatusCodes.NOT_FOUND, `Assistant ${assistantId} not found`) - } - try { - const assistantDetails = JSON.parse(assistant.details) - const credential = await appServer.AppDataSource.getRepository(Credential).findOneBy({ - id: assistant.credential - }) - - if (!credential) { - throw new InternalFlowiseError(StatusCodes.NOT_FOUND, `Credential ${assistant.credential} not found`) - } - - // Decrpyt credentialData - const decryptedCredentialData = await decryptCredentialData(credential.encryptedData) - const openAIApiKey = decryptedCredentialData['openAIApiKey'] - if (!openAIApiKey) { - throw new InternalFlowiseError(StatusCodes.NOT_FOUND, `OpenAI ApiKey not found`) - } - - const openai = new OpenAI({ apiKey: openAIApiKey }) - const dbResponse = await appServer.AppDataSource.getRepository(Assistant).delete({ id: assistantId }) - if (isDeleteBoth) await openai.beta.assistants.del(assistantDetails.id) - return dbResponse - } catch (error: any) { - throw new InternalFlowiseError(StatusCodes.INTERNAL_SERVER_ERROR, `Error deleting assistant - ${getErrorMessage(error)}`) - } - } catch (error) { - throw new InternalFlowiseError( - StatusCodes.INTERNAL_SERVER_ERROR, - `Error: assistantsService.deleteAssistant - ${getErrorMessage(error)}` - ) - } -} - -const getAllAssistants = async (): Promise => { - try { - const appServer = getRunningExpressApp() - const dbResponse = await appServer.AppDataSource.getRepository(Assistant).find() - return dbResponse - } catch (error) { - throw new InternalFlowiseError( - StatusCodes.INTERNAL_SERVER_ERROR, - `Error: assistantsService.getAllAssistants - ${getErrorMessage(error)}` - ) - } -} - -const getAssistantById = async (assistantId: string): Promise => { - try { - const appServer = getRunningExpressApp() - const dbResponse = await appServer.AppDataSource.getRepository(Assistant).findOneBy({ - id: assistantId - }) - if (!dbResponse) { - throw new InternalFlowiseError(StatusCodes.NOT_FOUND, `Assistant ${assistantId} not found`) - } - return dbResponse - } catch (error) { - throw new InternalFlowiseError( - StatusCodes.INTERNAL_SERVER_ERROR, - `Error: assistantsService.getAssistantById - ${getErrorMessage(error)}` - ) - } -} - -const updateAssistant = async (assistantId: string, requestBody: any): Promise => { - try { - const appServer = getRunningExpressApp() - const assistant = await appServer.AppDataSource.getRepository(Assistant).findOneBy({ - id: assistantId - }) - - if (!assistant) { - throw new InternalFlowiseError(StatusCodes.NOT_FOUND, `Assistant ${assistantId} not found`) - } - try { - const openAIAssistantId = JSON.parse(assistant.details)?.id - const body = requestBody - const assistantDetails = JSON.parse(body.details) - const credential = await appServer.AppDataSource.getRepository(Credential).findOneBy({ - id: body.credential - }) - - if (!credential) { - throw new InternalFlowiseError(StatusCodes.NOT_FOUND, `Credential ${body.credential} not found`) - } - - // Decrpyt credentialData - const decryptedCredentialData = await decryptCredentialData(credential.encryptedData) - const openAIApiKey = decryptedCredentialData['openAIApiKey'] - if (!openAIApiKey) { - throw new InternalFlowiseError(StatusCodes.NOT_FOUND, `OpenAI ApiKey not found`) - } - - const openai = new OpenAI({ apiKey: openAIApiKey }) - - let tools = [] - if (assistantDetails.tools) { - for (const tool of assistantDetails.tools ?? []) { - tools.push({ - type: tool - }) - } - } - - // Save tool_resources to be stored later into database - const savedToolResources = cloneDeep(assistantDetails.tool_resources) - - // Cleanup tool_resources before updating - if (assistantDetails.tool_resources) { - for (const toolResource in assistantDetails.tool_resources) { - if (toolResource === 'file_search') { - assistantDetails.tool_resources['file_search'] = { - vector_store_ids: assistantDetails.tool_resources['file_search'].vector_store_ids - } - } else if (toolResource === 'code_interpreter') { - assistantDetails.tool_resources['code_interpreter'] = { - file_ids: assistantDetails.tool_resources['code_interpreter'].file_ids - } - } - } - } - - const retrievedAssistant = await openai.beta.assistants.retrieve(openAIAssistantId) - let filteredTools = uniqWith([...retrievedAssistant.tools.filter((tool) => tool.type === 'function'), ...tools], isEqual) - filteredTools = filteredTools.filter((tool) => !(tool.type === 'function' && !(tool as any).function)) - - await openai.beta.assistants.update(openAIAssistantId, { - name: assistantDetails.name, - description: assistantDetails.description, - instructions: assistantDetails.instructions, - model: assistantDetails.model, - tools: filteredTools, - tool_resources: assistantDetails.tool_resources, - temperature: assistantDetails.temperature, - top_p: assistantDetails.top_p - }) - - const newAssistantDetails = { - ...assistantDetails, - id: openAIAssistantId - } - if (savedToolResources) newAssistantDetails.tool_resources = savedToolResources - - const updateAssistant = new Assistant() - body.details = JSON.stringify(newAssistantDetails) - Object.assign(updateAssistant, body) - - appServer.AppDataSource.getRepository(Assistant).merge(assistant, updateAssistant) - const dbResponse = await appServer.AppDataSource.getRepository(Assistant).save(assistant) - return dbResponse - } catch (error) { - throw new InternalFlowiseError(StatusCodes.INTERNAL_SERVER_ERROR, `Error updating assistant - ${getErrorMessage(error)}`) - } - } catch (error) { - throw new InternalFlowiseError( - StatusCodes.INTERNAL_SERVER_ERROR, - `Error: assistantsService.updateAssistant - ${getErrorMessage(error)}` - ) - } -} - -const importAssistants = async (newAssistants: Partial[]): Promise => { - try { - const appServer = getRunningExpressApp() - - // step 1 - check whether array is zero - if (newAssistants.length == 0) return - - // step 2 - check whether ids are duplicate in database - let ids = '(' - let count: number = 0 - const lastCount = newAssistants.length - 1 - newAssistants.forEach((newAssistant) => { - ids += `'${newAssistant.id}'` - if (lastCount != count) ids += ',' - if (lastCount == count) ids += ')' - count += 1 - }) - - const selectResponse = await appServer.AppDataSource.getRepository(Assistant) - .createQueryBuilder('assistant') - .select('assistant.id') - .where(`assistant.id IN ${ids}`) - .getMany() - const foundIds = selectResponse.map((response) => { - return response.id - }) - - // step 3 - remove ids that are only duplicate - const prepVariables: Partial[] = newAssistants.map((newAssistant) => { - let id: string = '' - if (newAssistant.id) id = newAssistant.id - if (foundIds.includes(id)) { - newAssistant.id = undefined - } - return newAssistant - }) - - // step 4 - transactional insert array of entities - const insertResponse = await appServer.AppDataSource.getRepository(Assistant).insert(prepVariables) - - return insertResponse - } catch (error) { - throw new InternalFlowiseError( - StatusCodes.INTERNAL_SERVER_ERROR, - `Error: variableService.importVariables - ${getErrorMessage(error)}` - ) - } -} - -export default { - createAssistant, - deleteAssistant, - getAllAssistants, - getAssistantById, - updateAssistant, - importAssistants -} diff --git a/studio-frontend/packages/server/src/services/chat-messages/index.ts b/studio-frontend/packages/server/src/services/chat-messages/index.ts index 9f2d53f..3f44418 100644 --- a/studio-frontend/packages/server/src/services/chat-messages/index.ts +++ b/studio-frontend/packages/server/src/services/chat-messages/index.ts @@ -5,7 +5,6 @@ import { utilGetChatMessage } from '../../utils/getChatMessage' import { utilAddChatMessage } from '../../utils/addChatMesage' import { getRunningExpressApp } from '../../utils/getRunningExpressApp' import { ChatMessageFeedback } from '../../database/entities/ChatMessageFeedback' -import { removeFilesFromStorage } from 'flowise-components' import logger from '../../utils/logger' import { ChatMessage } from '../../database/entities/ChatMessage' import { InternalFlowiseError } from '../../errors/internalFlowiseError' @@ -110,14 +109,6 @@ const removeAllChatMessages = async ( const feedbackDeleteOptions: FindOptionsWhere = { chatId } await appServer.AppDataSource.getRepository(ChatMessageFeedback).delete(feedbackDeleteOptions) - // Delete all uploads corresponding to this chatflow/chatId - if (chatId) { - try { - await removeFilesFromStorage(chatflowid, chatId) - } catch (e) { - logger.error(`[server]: Error deleting file storage for chatflow ${chatflowid}, chatId ${chatId}: ${e}`) - } - } const dbResponse = await appServer.AppDataSource.getRepository(ChatMessage).delete(deleteOptions) return dbResponse } catch (error) { diff --git a/studio-frontend/packages/server/src/services/chatflows/index.ts b/studio-frontend/packages/server/src/services/chatflows/index.ts index d5abbcd..a6d5acc 100644 --- a/studio-frontend/packages/server/src/services/chatflows/index.ts +++ b/studio-frontend/packages/server/src/services/chatflows/index.ts @@ -1,4 +1,3 @@ -import { removeFolderFromStorage } from 'flowise-components' import { StatusCodes } from 'http-status-codes' import { ChatflowType, IReactFlowObject } from '../../Interface' import { ChatFlow } from '../../database/entities/ChatFlow' @@ -7,11 +6,8 @@ import { ChatMessageFeedback } from '../../database/entities/ChatMessageFeedback import { UpsertHistory } from '../../database/entities/UpsertHistory' import { InternalFlowiseError } from '../../errors/internalFlowiseError' import { getErrorMessage } from '../../errors/utils' -import documentStoreService from '../../services/documentstore' -import { constructGraphs, getAppVersion, getEndingNodes, getTelemetryFlowObj, isFlowValidForStream } from '../../utils' -import { containsBase64File, updateFlowDataWithFilePaths } from '../../utils/fileRepository' +import { getAppVersion } from '../../utils' import { getRunningExpressApp } from '../../utils/getRunningExpressApp' -import { utilGetUploadsConfig } from '../../utils/getUploadsConfig' import logger from '../../utils/logger' import axios, { AxiosRequestConfig } from 'axios' import { HttpsProxyAgent } from 'https-proxy-agent' @@ -35,66 +31,6 @@ const getGithubAxiosConfig = (): AxiosRequestConfig => { const STUDIO_SERVER_URL = process.env.STUDIO_SERVER_URL || 'http://studio-backend.studio.svc.cluster.local:5000' -// Check if chatflow valid for streaming -const checkIfChatflowIsValidForStreaming = async (chatflowId: string): Promise => { - try { - const appServer = getRunningExpressApp() - //** - const chatflow = await appServer.AppDataSource.getRepository(ChatFlow).findOneBy({ - id: chatflowId - }) - if (!chatflow) { - throw new InternalFlowiseError(StatusCodes.NOT_FOUND, `Chatflow ${chatflowId} not found`) - } - - /*** Get Ending Node with Directed Graph ***/ - const flowData = chatflow.flowData - const parsedFlowData: IReactFlowObject = JSON.parse(flowData) - const nodes = parsedFlowData.nodes - const edges = parsedFlowData.edges - const { graph, nodeDependencies } = constructGraphs(nodes, edges) - - const endingNodes = getEndingNodes(nodeDependencies, graph, nodes) - - let isStreaming = false - for (const endingNode of endingNodes) { - const endingNodeData = endingNode.data - const isEndingNode = endingNodeData?.outputs?.output === 'EndingNode' - // Once custom function ending node exists, flow is always unavailable to stream - if (isEndingNode) { - return { isStreaming: false } - } - isStreaming = isFlowValidForStream(nodes, endingNodeData) - } - - // If it is a Multi/Sequential Agents, always enable streaming - if (endingNodes.filter((node) => node.data.category === 'Multi Agents' || node.data.category === 'Sequential Agents').length > 0) { - return { isStreaming: true } - } - - const dbResponse = { isStreaming: isStreaming } - return dbResponse - } catch (error) { - throw new InternalFlowiseError( - StatusCodes.INTERNAL_SERVER_ERROR, - `Error: chatflowsService.checkIfChatflowIsValidForStreaming - ${getErrorMessage(error)}` - ) - } -} - -// Check if chatflow valid for uploads -const checkIfChatflowIsValidForUploads = async (chatflowId: string): Promise => { - try { - const dbResponse = await utilGetUploadsConfig(chatflowId) - return dbResponse - } catch (error) { - throw new InternalFlowiseError( - StatusCodes.INTERNAL_SERVER_ERROR, - `Error: chatflowsService.checkIfChatflowIsValidForUploads - ${getErrorMessage(error)}` - ) - } -} - const deleteChatflow = async (chatflowId: string): Promise => { try { const appServer = getRunningExpressApp() @@ -105,10 +41,6 @@ const deleteChatflow = async (chatflowId: string): Promise => { } const dbResponse = await appServer.AppDataSource.getRepository(ChatFlow).delete({ id: chatflowId }) try { - // Delete all uploads corresponding to this chatflow - await removeFolderFromStorage(chatflowId) - await documentStoreService.updateDocumentStoreUsage(chatflowId, undefined) - // Delete all chat messages await appServer.AppDataSource.getRepository(ChatMessage).delete({ chatflowid: chatflowId }) @@ -260,30 +192,8 @@ const getChatflowById = async (chatflowId: string): Promise => { const saveChatflow = async (newChatFlow: ChatFlow): Promise => { try { const appServer = getRunningExpressApp() - let dbResponse: ChatFlow - if (containsBase64File(newChatFlow)) { - // we need a 2-step process, as we need to save the chatflow first and then update the file paths - // this is because we need the chatflow id to create the file paths - - // step 1 - save with empty flowData - const incomingFlowData = newChatFlow.flowData - newChatFlow.flowData = JSON.stringify({}) - const chatflow = appServer.AppDataSource.getRepository(ChatFlow).create(newChatFlow) - const step1Results = await appServer.AppDataSource.getRepository(ChatFlow).save(chatflow) - - // step 2 - convert base64 to file paths and update the chatflow - step1Results.flowData = await updateFlowDataWithFilePaths(step1Results.id, incomingFlowData) - await _checkAndUpdateDocumentStoreUsage(step1Results) - dbResponse = await appServer.AppDataSource.getRepository(ChatFlow).save(step1Results) - } else { - const chatflow = appServer.AppDataSource.getRepository(ChatFlow).create(newChatFlow) - dbResponse = await appServer.AppDataSource.getRepository(ChatFlow).save(chatflow) - } - await appServer.telemetry.sendTelemetry('chatflow_created', { - version: await getAppVersion(), - chatflowId: dbResponse.id, - flowGraph: getTelemetryFlowObj(JSON.parse(dbResponse.flowData)?.nodes, JSON.parse(dbResponse.flowData)?.edges) - }) + const chatflow = appServer.AppDataSource.getRepository(ChatFlow).create(newChatFlow) + const dbResponse = await appServer.AppDataSource.getRepository(ChatFlow).save(chatflow) return dbResponse } catch (error) { throw new InternalFlowiseError( @@ -349,17 +259,9 @@ const importChatflows = async (newChatflows: Partial[]): Promise const updateChatflow = async (chatflow: ChatFlow, updateChatFlow: ChatFlow): Promise => { try { const appServer = getRunningExpressApp() - if (updateChatFlow.flowData && containsBase64File(updateChatFlow)) { - updateChatFlow.flowData = await updateFlowDataWithFilePaths(chatflow.id, updateChatFlow.flowData) - } const newDbChatflow = appServer.AppDataSource.getRepository(ChatFlow).merge(chatflow, updateChatFlow) - await _checkAndUpdateDocumentStoreUsage(newDbChatflow) const dbResponse = await appServer.AppDataSource.getRepository(ChatFlow).save(newDbChatflow) - - // chatFlowPool is initialized only when a flow is opened - // if the user attempts to rename/update category without opening any flow, chatFlowPool will be undefined if (appServer.chatflowPool) { - // Update chatflowpool inSync to false, to build flow from scratch again because data has been changed appServer.chatflowPool.updateInSync(chatflow.id, false) } return dbResponse @@ -407,13 +309,9 @@ const getSinglePublicChatbotConfig = async (chatflowId: string): Promise => if (!dbResponse) { throw new InternalFlowiseError(StatusCodes.NOT_FOUND, `Chatflow ${chatflowId} not found`) } - const uploadsConfig = await utilGetUploadsConfig(chatflowId) - // even if chatbotConfig is not set but uploads are enabled - // send uploadsConfig to the chatbot - if (dbResponse.chatbotConfig || uploadsConfig) { + if (dbResponse.chatbotConfig) { try { - const parsedConfig = dbResponse.chatbotConfig ? JSON.parse(dbResponse.chatbotConfig) : {} - return { ...parsedConfig, uploads: uploadsConfig } + return JSON.parse(dbResponse.chatbotConfig) } catch (e) { throw new InternalFlowiseError(StatusCodes.INTERNAL_SERVER_ERROR, `Error parsing Chatbot Config for Chatflow ${chatflowId}`) } @@ -608,21 +506,7 @@ const updateDeploymentStatus = async (chatflowId: string, status: string, messag } } -const _checkAndUpdateDocumentStoreUsage = async (chatflow: ChatFlow) => { - const parsedFlowData: IReactFlowObject = JSON.parse(chatflow.flowData) - const nodes = parsedFlowData.nodes - // from the nodes array find if there is a node with name == documentStore) - const node = nodes.length > 0 && nodes.find((node) => node.data.name === 'documentStore') - if (!node || !node.data || !node.data.inputs || node.data.inputs['selectedStore'] === undefined) { - await documentStoreService.updateDocumentStoreUsage(chatflow.id, undefined) - } else { - await documentStoreService.updateDocumentStoreUsage(chatflow.id, node.data.inputs['selectedStore']) - } -} - export default { - checkIfChatflowIsValidForStreaming, - checkIfChatflowIsValidForUploads, deleteChatflow, getAllChatflows, getAllChatflowsbyUserId, @@ -636,7 +520,6 @@ export default { deployChatflowSandboxService, stopChatflowSandboxService, buildDeploymentPackageService, - getSinglePublicChatbotConfig, oneClickDeploymentService, updateDeploymentStatus } \ No newline at end of file diff --git a/studio-frontend/packages/server/src/services/documentstore/index.ts b/studio-frontend/packages/server/src/services/documentstore/index.ts deleted file mode 100644 index 53b3cef..0000000 --- a/studio-frontend/packages/server/src/services/documentstore/index.ts +++ /dev/null @@ -1,1275 +0,0 @@ -import { getRunningExpressApp } from '../../utils/getRunningExpressApp' -import { DocumentStore } from '../../database/entities/DocumentStore' -import { - addSingleFileToStorage, - getFileFromStorage, - ICommonObject, - IDocument, - removeFilesFromStorage, - removeSpecificFileFromStorage -} from 'flowise-components' -import { - chatType, - DocumentStoreStatus, - IDocumentStoreFileChunkPagedResponse, - IDocumentStoreLoader, - IDocumentStoreLoaderFile, - IDocumentStoreLoaderForPreview, - IDocumentStoreWhereUsed, - INodeData -} from '../../Interface' -import { DocumentStoreFileChunk } from '../../database/entities/DocumentStoreFileChunk' -import { v4 as uuidv4 } from 'uuid' -import { databaseEntities, getAppVersion, saveUpsertFlowData } from '../../utils' -import logger from '../../utils/logger' -import nodesService from '../nodes' -import { InternalFlowiseError } from '../../errors/internalFlowiseError' -import { StatusCodes } from 'http-status-codes' -import { getErrorMessage } from '../../errors/utils' -import { ChatFlow } from '../../database/entities/ChatFlow' -import { Document } from '@langchain/core/documents' -import { App } from '../../index' -import { UpsertHistory } from '../../database/entities/UpsertHistory' -import { cloneDeep, omit } from 'lodash' -import telemetryService from '../telemetry' - -const DOCUMENT_STORE_BASE_FOLDER = 'docustore' - -const createDocumentStore = async (newDocumentStore: DocumentStore) => { - try { - const appServer = getRunningExpressApp() - const documentStore = appServer.AppDataSource.getRepository(DocumentStore).create(newDocumentStore) - const dbResponse = await appServer.AppDataSource.getRepository(DocumentStore).save(documentStore) - return dbResponse - } catch (error) { - throw new InternalFlowiseError( - StatusCodes.INTERNAL_SERVER_ERROR, - `Error: documentStoreServices.createDocumentStore - ${getErrorMessage(error)}` - ) - } -} - -const getAllDocumentStores = async () => { - try { - const appServer = getRunningExpressApp() - const entities = await appServer.AppDataSource.getRepository(DocumentStore).find() - return entities - } catch (error) { - throw new InternalFlowiseError( - StatusCodes.INTERNAL_SERVER_ERROR, - `Error: documentStoreServices.getAllDocumentStores - ${getErrorMessage(error)}` - ) - } -} - -const getAllDocumentFileChunks = async () => { - try { - const appServer = getRunningExpressApp() - const entities = await appServer.AppDataSource.getRepository(DocumentStoreFileChunk).find() - return entities - } catch (error) { - throw new InternalFlowiseError( - StatusCodes.INTERNAL_SERVER_ERROR, - `Error: documentStoreServices.getAllDocumentFileChunks - ${getErrorMessage(error)}` - ) - } -} - -const deleteLoaderFromDocumentStore = async (storeId: string, loaderId: string) => { - try { - const appServer = getRunningExpressApp() - const entity = await appServer.AppDataSource.getRepository(DocumentStore).findOneBy({ - id: storeId - }) - if (!entity) { - throw new InternalFlowiseError( - StatusCodes.NOT_FOUND, - `Error: documentStoreServices.deleteLoaderFromDocumentStore - Document store ${storeId} not found` - ) - } - const existingLoaders = JSON.parse(entity.loaders) - const found = existingLoaders.find((uFile: IDocumentStoreLoader) => uFile.id === loaderId) - if (found) { - if (found.files?.length) { - for (const file of found.files) { - if (file.name) { - await removeSpecificFileFromStorage(DOCUMENT_STORE_BASE_FOLDER, storeId, file.name) - } - } - } - const index = existingLoaders.indexOf(found) - if (index > -1) { - existingLoaders.splice(index, 1) - } - // remove the chunks - await appServer.AppDataSource.getRepository(DocumentStoreFileChunk).delete({ docId: found.id }) - - entity.loaders = JSON.stringify(existingLoaders) - const results = await appServer.AppDataSource.getRepository(DocumentStore).save(entity) - return results - } else { - throw new InternalFlowiseError(StatusCodes.INTERNAL_SERVER_ERROR, `Unable to locate loader in Document Store ${entity.name}`) - } - } catch (error) { - throw new InternalFlowiseError( - StatusCodes.INTERNAL_SERVER_ERROR, - `Error: documentStoreServices.deleteLoaderFromDocumentStore - ${getErrorMessage(error)}` - ) - } -} - -const getDocumentStoreById = async (storeId: string) => { - try { - const appServer = getRunningExpressApp() - const entity = await appServer.AppDataSource.getRepository(DocumentStore).findOneBy({ - id: storeId - }) - if (!entity) { - throw new InternalFlowiseError( - StatusCodes.NOT_FOUND, - `Error: documentStoreServices.getDocumentStoreById - Document store ${storeId} not found` - ) - } - return entity - } catch (error) { - throw new InternalFlowiseError( - StatusCodes.INTERNAL_SERVER_ERROR, - `Error: documentStoreServices.getDocumentStoreById - ${getErrorMessage(error)}` - ) - } -} - -const getUsedChatflowNames = async (entity: DocumentStore) => { - try { - const appServer = getRunningExpressApp() - if (entity.whereUsed) { - const whereUsed = JSON.parse(entity.whereUsed) - const updatedWhereUsed: IDocumentStoreWhereUsed[] = [] - for (let i = 0; i < whereUsed.length; i++) { - const associatedChatflow = await appServer.AppDataSource.getRepository(ChatFlow).findOne({ - where: { id: whereUsed[i] }, - select: ['id', 'name'] - }) - if (associatedChatflow) { - updatedWhereUsed.push({ - id: whereUsed[i], - name: associatedChatflow.name - }) - } - } - return updatedWhereUsed - } - return [] - } catch (error) { - throw new InternalFlowiseError( - StatusCodes.INTERNAL_SERVER_ERROR, - `Error: documentStoreServices.getUsedChatflowNames - ${getErrorMessage(error)}` - ) - } -} - -// Get chunks for a specific loader or store -const getDocumentStoreFileChunks = async (storeId: string, fileId: string, pageNo: number = 1) => { - try { - const appServer = getRunningExpressApp() - const entity = await appServer.AppDataSource.getRepository(DocumentStore).findOneBy({ - id: storeId - }) - if (!entity) { - throw new InternalFlowiseError( - StatusCodes.NOT_FOUND, - `Error: documentStoreServices.getDocumentStoreById - Document store ${storeId} not found` - ) - } - const loaders = JSON.parse(entity.loaders) - - let found: IDocumentStoreLoader | undefined - if (fileId !== 'all') { - found = loaders.find((loader: IDocumentStoreLoader) => loader.id === fileId) - if (!found) { - throw new InternalFlowiseError( - StatusCodes.NOT_FOUND, - `Error: documentStoreServices.getDocumentStoreById - Document file ${fileId} not found` - ) - } - } - let totalChars = 0 - loaders.forEach((loader: IDocumentStoreLoader) => { - totalChars += loader.totalChars - }) - if (found) { - found.totalChars = totalChars - found.id = fileId - found.status = entity.status - } - const PAGE_SIZE = 50 - const skip = (pageNo - 1) * PAGE_SIZE - const take = PAGE_SIZE - let whereCondition: any = { docId: fileId } - if (fileId === 'all') { - whereCondition = { storeId: storeId } - } - const count = await appServer.AppDataSource.getRepository(DocumentStoreFileChunk).count({ - where: whereCondition - }) - const chunksWithCount = await appServer.AppDataSource.getRepository(DocumentStoreFileChunk).find({ - skip, - take, - where: whereCondition, - order: { - chunkNo: 'ASC' - } - }) - - if (!chunksWithCount) { - throw new InternalFlowiseError(StatusCodes.NOT_FOUND, `File ${fileId} not found`) - } - - const response: IDocumentStoreFileChunkPagedResponse = { - chunks: chunksWithCount, - count: count, - file: found, - currentPage: pageNo, - storeName: entity.name, - description: entity.description - } - return response - } catch (error) { - throw new InternalFlowiseError( - StatusCodes.INTERNAL_SERVER_ERROR, - `Error: documentStoreServices.getDocumentStoreFileChunks - ${getErrorMessage(error)}` - ) - } -} - -const deleteDocumentStore = async (storeId: string) => { - try { - const appServer = getRunningExpressApp() - // delete all the chunks associated with the store - await appServer.AppDataSource.getRepository(DocumentStoreFileChunk).delete({ - storeId: storeId - }) - // now delete the files associated with the store - const entity = await appServer.AppDataSource.getRepository(DocumentStore).findOneBy({ - id: storeId - }) - if (!entity) { - throw new InternalFlowiseError(StatusCodes.NOT_FOUND, `Document store ${storeId} not found`) - } - await removeFilesFromStorage(DOCUMENT_STORE_BASE_FOLDER, entity.id) - - // delete upsert history - await appServer.AppDataSource.getRepository(UpsertHistory).delete({ - chatflowid: storeId - }) - - // now delete the store - const tbd = await appServer.AppDataSource.getRepository(DocumentStore).delete({ - id: storeId - }) - - return { deleted: tbd.affected } - } catch (error) { - throw new InternalFlowiseError( - StatusCodes.INTERNAL_SERVER_ERROR, - `Error: documentStoreServices.deleteDocumentStore - ${getErrorMessage(error)}` - ) - } -} - -const deleteDocumentStoreFileChunk = async (storeId: string, docId: string, chunkId: string) => { - try { - const appServer = getRunningExpressApp() - const entity = await appServer.AppDataSource.getRepository(DocumentStore).findOneBy({ - id: storeId - }) - if (!entity) { - throw new InternalFlowiseError(StatusCodes.NOT_FOUND, `Document store ${storeId} not found`) - } - const loaders = JSON.parse(entity.loaders) - const found = loaders.find((ldr: IDocumentStoreLoader) => ldr.id === docId) - if (!found) { - throw new InternalFlowiseError(StatusCodes.NOT_FOUND, `Document store loader ${docId} not found`) - } - - const tbdChunk = await appServer.AppDataSource.getRepository(DocumentStoreFileChunk).findOneBy({ - id: chunkId - }) - if (!tbdChunk) { - throw new InternalFlowiseError(StatusCodes.NOT_FOUND, `Document Chunk ${chunkId} not found`) - } - await appServer.AppDataSource.getRepository(DocumentStoreFileChunk).delete(chunkId) - found.totalChunks-- - found.totalChars -= tbdChunk.pageContent.length - entity.loaders = JSON.stringify(loaders) - await appServer.AppDataSource.getRepository(DocumentStore).save(entity) - return getDocumentStoreFileChunks(storeId, docId) - } catch (error) { - throw new InternalFlowiseError( - StatusCodes.INTERNAL_SERVER_ERROR, - `Error: documentStoreServices.deleteDocumentStoreFileChunk - ${getErrorMessage(error)}` - ) - } -} - -const deleteVectorStoreFromStore = async (storeId: string) => { - try { - const appServer = getRunningExpressApp() - const entity = await appServer.AppDataSource.getRepository(DocumentStore).findOneBy({ - id: storeId - }) - if (!entity) { - throw new InternalFlowiseError(StatusCodes.NOT_FOUND, `Document store ${storeId} not found`) - } - - if (!entity.embeddingConfig) { - throw new InternalFlowiseError(StatusCodes.NOT_FOUND, `Embedding for Document store ${storeId} not found`) - } - - if (!entity.vectorStoreConfig) { - throw new InternalFlowiseError(StatusCodes.NOT_FOUND, `Vector Store for Document store ${storeId} not found`) - } - - if (!entity.recordManagerConfig) { - throw new InternalFlowiseError( - StatusCodes.NOT_FOUND, - `Record Manager for Document Store ${storeId} is needed to delete data from Vector Store` - ) - } - - const options: ICommonObject = { - chatflowid: storeId, - appDataSource: appServer.AppDataSource, - databaseEntities, - logger - } - - // Get Record Manager Instance - const recordManagerConfig = JSON.parse(entity.recordManagerConfig) - const recordManagerObj = await _createRecordManagerObject( - appServer, - { recordManagerName: recordManagerConfig.name, recordManagerConfig: recordManagerConfig.config }, - options - ) - - // Get Embeddings Instance - const embeddingConfig = JSON.parse(entity.embeddingConfig) - const embeddingObj = await _createEmbeddingsObject( - appServer, - { embeddingName: embeddingConfig.name, embeddingConfig: embeddingConfig.config }, - options - ) - - // Get Vector Store Node Data - const vectorStoreConfig = JSON.parse(entity.vectorStoreConfig) - const vStoreNodeData = _createVectorStoreNodeData( - appServer, - { vectorStoreName: vectorStoreConfig.name, vectorStoreConfig: vectorStoreConfig.config }, - embeddingObj, - recordManagerObj - ) - - // Get Vector Store Instance - const vectorStoreObj = await _createVectorStoreObject( - appServer, - { vectorStoreName: vectorStoreConfig.name, vectorStoreConfig: vectorStoreConfig.config }, - vStoreNodeData - ) - const idsToDelete: string[] = [] // empty ids because we get it dynamically from the record manager - - // Call the delete method of the vector store - if (vectorStoreObj.vectorStoreMethods.delete) { - await vectorStoreObj.vectorStoreMethods.delete(vStoreNodeData, idsToDelete, options) - } - } catch (error) { - throw new InternalFlowiseError( - StatusCodes.INTERNAL_SERVER_ERROR, - `Error: documentStoreServices.deleteVectorStoreFromStore - ${getErrorMessage(error)}` - ) - } -} - -const editDocumentStoreFileChunk = async (storeId: string, docId: string, chunkId: string, content: string, metadata: ICommonObject) => { - try { - const appServer = getRunningExpressApp() - const entity = await appServer.AppDataSource.getRepository(DocumentStore).findOneBy({ - id: storeId - }) - if (!entity) { - throw new InternalFlowiseError(StatusCodes.NOT_FOUND, `Document store ${storeId} not found`) - } - const loaders = JSON.parse(entity.loaders) - const found = loaders.find((ldr: IDocumentStoreLoader) => ldr.id === docId) - if (!found) { - throw new InternalFlowiseError(StatusCodes.NOT_FOUND, `Document store loader ${docId} not found`) - } - - const editChunk = await appServer.AppDataSource.getRepository(DocumentStoreFileChunk).findOneBy({ - id: chunkId - }) - if (!editChunk) { - throw new InternalFlowiseError(StatusCodes.NOT_FOUND, `Document Chunk ${chunkId} not found`) - } - found.totalChars -= editChunk.pageContent.length - editChunk.pageContent = content - editChunk.metadata = JSON.stringify(metadata) - found.totalChars += content.length - await appServer.AppDataSource.getRepository(DocumentStoreFileChunk).save(editChunk) - entity.loaders = JSON.stringify(loaders) - await appServer.AppDataSource.getRepository(DocumentStore).save(entity) - return getDocumentStoreFileChunks(storeId, docId) - } catch (error) { - throw new InternalFlowiseError( - StatusCodes.INTERNAL_SERVER_ERROR, - `Error: documentStoreServices.editDocumentStoreFileChunk - ${getErrorMessage(error)}` - ) - } -} - -// Update documentStore -const updateDocumentStore = async (documentStore: DocumentStore, updatedDocumentStore: DocumentStore) => { - try { - const appServer = getRunningExpressApp() - const tmpUpdatedDocumentStore = appServer.AppDataSource.getRepository(DocumentStore).merge(documentStore, updatedDocumentStore) - const dbResponse = await appServer.AppDataSource.getRepository(DocumentStore).save(tmpUpdatedDocumentStore) - return dbResponse - } catch (error) { - throw new InternalFlowiseError( - StatusCodes.INTERNAL_SERVER_ERROR, - `Error: documentStoreServices.updateDocumentStore - ${getErrorMessage(error)}` - ) - } -} - -const _saveFileToStorage = async (fileBase64: string, entity: DocumentStore) => { - const splitDataURI = fileBase64.split(',') - const filename = splitDataURI.pop()?.split(':')[1] ?? '' - const bf = Buffer.from(splitDataURI.pop() || '', 'base64') - const mimePrefix = splitDataURI.pop() - let mime = '' - if (mimePrefix) { - mime = mimePrefix.split(';')[0].split(':')[1] - } - await addSingleFileToStorage(mime, bf, filename, DOCUMENT_STORE_BASE_FOLDER, entity.id) - return { - id: uuidv4(), - name: filename, - mimePrefix: mime, - size: bf.length, - status: DocumentStoreStatus.NEW, - uploaded: new Date() - } -} - -const _splitIntoChunks = async (data: IDocumentStoreLoaderForPreview) => { - try { - const appServer = getRunningExpressApp() - let splitterInstance = null - if (data.splitterConfig && Object.keys(data.splitterConfig).length > 0) { - const nodeInstanceFilePath = appServer.nodesPool.componentNodes[data.splitterId].filePath as string - const nodeModule = await import(nodeInstanceFilePath) - const newNodeInstance = new nodeModule.nodeClass() - let nodeData = { - inputs: { ...data.splitterConfig }, - id: 'splitter_0' - } - splitterInstance = await newNodeInstance.init(nodeData) - } - const nodeInstanceFilePath = appServer.nodesPool.componentNodes[data.loaderId].filePath as string - const nodeModule = await import(nodeInstanceFilePath) - // doc loader configs - const nodeData = { - credential: data.credential || undefined, - inputs: { ...data.loaderConfig, textSplitter: splitterInstance }, - outputs: { output: 'document' } - } - const options: ICommonObject = { - chatflowid: uuidv4(), - appDataSource: appServer.AppDataSource, - databaseEntities, - logger - } - const docNodeInstance = new nodeModule.nodeClass() - let docs: IDocument[] = await docNodeInstance.init(nodeData, '', options) - return docs - } catch (error) { - throw new InternalFlowiseError( - StatusCodes.INTERNAL_SERVER_ERROR, - `Error: documentStoreServices.splitIntoChunks - ${getErrorMessage(error)}` - ) - } -} - -const _normalizeFilePaths = async (data: IDocumentStoreLoaderForPreview, entity: DocumentStore | null) => { - const keys = Object.getOwnPropertyNames(data.loaderConfig) - let rehydrated = false - for (let i = 0; i < keys.length; i++) { - const input = data.loaderConfig[keys[i]] - if (!input) { - continue - } - if (typeof input !== 'string') { - continue - } - let documentStoreEntity: DocumentStore | null = entity - if (input.startsWith('FILE-STORAGE::')) { - if (!documentStoreEntity) { - const appServer = getRunningExpressApp() - documentStoreEntity = await appServer.AppDataSource.getRepository(DocumentStore).findOneBy({ - id: data.storeId - }) - if (!documentStoreEntity) { - throw new InternalFlowiseError(StatusCodes.NOT_FOUND, `Document store ${data.storeId} not found`) - } - } - const fileName = input.replace('FILE-STORAGE::', '') - let files: string[] = [] - if (fileName.startsWith('[') && fileName.endsWith(']')) { - files = JSON.parse(fileName) - } else { - files = [fileName] - } - const loaders = JSON.parse(documentStoreEntity.loaders) - const currentLoader = loaders.find((ldr: IDocumentStoreLoader) => ldr.id === data.id) - if (currentLoader) { - const base64Files: string[] = [] - for (const file of files) { - const bf = await getFileFromStorage(file, DOCUMENT_STORE_BASE_FOLDER, documentStoreEntity.id) - // find the file entry that has the same name as the file - const uploadedFile = currentLoader.files.find((uFile: IDocumentStoreLoaderFile) => uFile.name === file) - const mimePrefix = 'data:' + uploadedFile.mimePrefix + ';base64' - const base64String = mimePrefix + ',' + bf.toString('base64') + `,filename:${file}` - base64Files.push(base64String) - } - data.loaderConfig[keys[i]] = JSON.stringify(base64Files) - rehydrated = true - } - } - } - data.rehydrated = rehydrated -} - -const previewChunks = async (data: IDocumentStoreLoaderForPreview) => { - try { - if (data.preview) { - if ( - data.loaderId === 'cheerioWebScraper' || - data.loaderId === 'puppeteerWebScraper' || - data.loaderId === 'playwrightWebScraper' - ) { - data.loaderConfig['limit'] = 3 - } - } - if (!data.rehydrated) { - await _normalizeFilePaths(data, null) - } - let docs = await _splitIntoChunks(data) - const totalChunks = docs.length - // if -1, return all chunks - if (data.previewChunkCount === -1) data.previewChunkCount = totalChunks - // return all docs if the user ask for more than we have - if (totalChunks <= data.previewChunkCount) data.previewChunkCount = totalChunks - // return only the first n chunks - if (totalChunks > data.previewChunkCount) docs = docs.slice(0, data.previewChunkCount) - - return { chunks: docs, totalChunks: totalChunks, previewChunkCount: data.previewChunkCount } - } catch (error) { - throw new InternalFlowiseError( - StatusCodes.INTERNAL_SERVER_ERROR, - `Error: documentStoreServices.previewChunks - ${getErrorMessage(error)}` - ) - } -} - -const processAndSaveChunks = async (data: IDocumentStoreLoaderForPreview) => { - try { - const appServer = getRunningExpressApp() - const entity = await appServer.AppDataSource.getRepository(DocumentStore).findOneBy({ - id: data.storeId - }) - if (!entity) { - throw new InternalFlowiseError( - StatusCodes.NOT_FOUND, - `Error: documentStoreServices.processAndSaveChunks - Document store ${data.storeId} not found` - ) - } - const existingLoaders = JSON.parse(entity.loaders) - const newLoaderId = data.id ?? uuidv4() - const found = existingLoaders.find((ldr: IDocumentStoreLoader) => ldr.id === newLoaderId) - if (found) { - const foundIndex = existingLoaders.findIndex((ldr: IDocumentStoreLoader) => ldr.id === newLoaderId) - - if (!data.loaderId) data.loaderId = found.loaderId - if (!data.loaderName) data.loaderName = found.loaderName - if (!data.loaderConfig) data.loaderConfig = found.loaderConfig - if (!data.splitterId) data.splitterId = found.splitterId - if (!data.splitterName) data.splitterName = found.splitterName - if (!data.splitterConfig) data.splitterConfig = found.splitterConfig - if (found.credential) { - data.credential = found.credential - } - - let loader: IDocumentStoreLoader = { - ...found, - loaderId: data.loaderId, - loaderName: data.loaderName, - loaderConfig: data.loaderConfig, - splitterId: data.splitterId, - splitterName: data.splitterName, - splitterConfig: data.splitterConfig, - totalChunks: 0, - totalChars: 0, - status: DocumentStoreStatus.SYNCING - } - if (data.credential) { - loader.credential = data.credential - } - - existingLoaders[foundIndex] = loader - entity.loaders = JSON.stringify(existingLoaders) - } else { - let loader: IDocumentStoreLoader = { - id: newLoaderId, - loaderId: data.loaderId, - loaderName: data.loaderName, - loaderConfig: data.loaderConfig, - splitterId: data.splitterId, - splitterName: data.splitterName, - splitterConfig: data.splitterConfig, - totalChunks: 0, - totalChars: 0, - status: DocumentStoreStatus.SYNCING - } - if (data.credential) { - loader.credential = data.credential - } - existingLoaders.push(loader) - entity.loaders = JSON.stringify(existingLoaders) - } - await appServer.AppDataSource.getRepository(DocumentStore).save(entity) - // this method will run async, will have to be moved to a worker thread - _saveChunksToStorage(data, entity, newLoaderId).then(() => {}) - return getDocumentStoreFileChunks(data.storeId as string, newLoaderId) - } catch (error) { - throw new InternalFlowiseError( - StatusCodes.INTERNAL_SERVER_ERROR, - `Error: documentStoreServices.processAndSaveChunks - ${getErrorMessage(error)}` - ) - } -} - -const _saveChunksToStorage = async (data: IDocumentStoreLoaderForPreview, entity: DocumentStore, newLoaderId: string) => { - const re = new RegExp('^data.*;base64', 'i') - - try { - const appServer = getRunningExpressApp() - //step 1: restore the full paths, if any - await _normalizeFilePaths(data, entity) - //step 2: split the file into chunks - previewChunks(data).then(async (response) => { - //step 3: remove all files associated with the loader - const existingLoaders = JSON.parse(entity.loaders) - const loader = existingLoaders.find((ldr: IDocumentStoreLoader) => ldr.id === newLoaderId) - if (data.id) { - const index = existingLoaders.indexOf(loader) - if (index > -1) { - existingLoaders.splice(index, 1) - if (!data.rehydrated) { - if (loader.files) { - loader.files.map(async (file: IDocumentStoreLoaderFile) => { - await removeSpecificFileFromStorage(DOCUMENT_STORE_BASE_FOLDER, entity.id, file.name) - }) - } - } - } - } - //step 4: save new file to storage - let filesWithMetadata = [] - const keys = Object.getOwnPropertyNames(data.loaderConfig) - for (let i = 0; i < keys.length; i++) { - const input = data.loaderConfig[keys[i]] - if (!input) { - continue - } - if (typeof input !== 'string') { - continue - } - if (input.startsWith('[') && input.endsWith(']')) { - const files = JSON.parse(input) - const fileNames: string[] = [] - for (let j = 0; j < files.length; j++) { - const file = files[j] - if (re.test(file)) { - const fileMetadata = await _saveFileToStorage(file, entity) - fileNames.push(fileMetadata.name) - filesWithMetadata.push(fileMetadata) - } - } - data.loaderConfig[keys[i]] = 'FILE-STORAGE::' + JSON.stringify(fileNames) - } else if (re.test(input)) { - const fileNames: string[] = [] - const fileMetadata = await _saveFileToStorage(input, entity) - fileNames.push(fileMetadata.name) - filesWithMetadata.push(fileMetadata) - data.loaderConfig[keys[i]] = 'FILE-STORAGE::' + JSON.stringify(fileNames) - break - } - } - //step 5: update with the new files and loaderConfig - if (filesWithMetadata.length > 0) { - loader.loaderConfig = data.loaderConfig - loader.files = filesWithMetadata - } - //step 6: update the loaders with the new loaderConfig - if (data.id) { - existingLoaders.push(loader) - } - //step 7: remove all previous chunks - await appServer.AppDataSource.getRepository(DocumentStoreFileChunk).delete({ docId: newLoaderId }) - if (response.chunks) { - //step 8: now save the new chunks - const totalChars = response.chunks.reduce((acc, chunk) => { - if (chunk.pageContent) { - return acc + chunk.pageContent.length - } - return acc - }, 0) - response.chunks.map(async (chunk: IDocument, index: number) => { - const docChunk: DocumentStoreFileChunk = { - docId: newLoaderId, - storeId: data.storeId || '', - id: uuidv4(), - chunkNo: index + 1, - pageContent: chunk.pageContent, - metadata: JSON.stringify(chunk.metadata) - } - const dChunk = appServer.AppDataSource.getRepository(DocumentStoreFileChunk).create(docChunk) - await appServer.AppDataSource.getRepository(DocumentStoreFileChunk).save(dChunk) - }) - // update the loader with the new metrics - loader.totalChunks = response.totalChunks - loader.totalChars = totalChars - } - loader.status = 'SYNC' - // have a flag and iterate over the loaders and update the entity status to SYNC - const allSynced = existingLoaders.every((ldr: IDocumentStoreLoader) => ldr.status === 'SYNC') - entity.status = allSynced ? DocumentStoreStatus.SYNC : DocumentStoreStatus.STALE - entity.loaders = JSON.stringify(existingLoaders) - //step 9: update the entity in the database - await appServer.AppDataSource.getRepository(DocumentStore).save(entity) - return - }) - } catch (error) { - throw new InternalFlowiseError( - StatusCodes.INTERNAL_SERVER_ERROR, - `Error: documentStoreServices._saveChunksToStorage - ${getErrorMessage(error)}` - ) - } -} - -// Get all component nodes -const getDocumentLoaders = async () => { - const removeDocumentLoadersWithName = ['documentStore', 'vectorStoreToDocument', 'unstructuredFolderLoader', 'folderFiles'] - - try { - const dbResponse = await nodesService.getAllNodesForCategory('Document Loaders') - return dbResponse.filter((node) => !removeDocumentLoadersWithName.includes(node.name)) - } catch (error) { - throw new InternalFlowiseError( - StatusCodes.INTERNAL_SERVER_ERROR, - `Error: documentStoreServices.getDocumentLoaders - ${getErrorMessage(error)}` - ) - } -} - -const updateDocumentStoreUsage = async (chatId: string, storeId: string | undefined) => { - try { - // find the document store - const appServer = getRunningExpressApp() - // find all entities that have the chatId in their whereUsed - const entities = await appServer.AppDataSource.getRepository(DocumentStore).find() - entities.map(async (entity: DocumentStore) => { - const whereUsed = JSON.parse(entity.whereUsed) - const found = whereUsed.find((w: string) => w === chatId) - if (found) { - if (!storeId) { - // remove the chatId from the whereUsed, as the store is being deleted - const index = whereUsed.indexOf(chatId) - if (index > -1) { - whereUsed.splice(index, 1) - entity.whereUsed = JSON.stringify(whereUsed) - await appServer.AppDataSource.getRepository(DocumentStore).save(entity) - } - } else if (entity.id === storeId) { - // do nothing, already found and updated - } else if (entity.id !== storeId) { - // remove the chatId from the whereUsed, as a new store is being used - const index = whereUsed.indexOf(chatId) - if (index > -1) { - whereUsed.splice(index, 1) - entity.whereUsed = JSON.stringify(whereUsed) - await appServer.AppDataSource.getRepository(DocumentStore).save(entity) - } - } - } else { - if (entity.id === storeId) { - // add the chatId to the whereUsed - whereUsed.push(chatId) - entity.whereUsed = JSON.stringify(whereUsed) - await appServer.AppDataSource.getRepository(DocumentStore).save(entity) - } - } - }) - } catch (error) { - throw new InternalFlowiseError( - StatusCodes.INTERNAL_SERVER_ERROR, - `Error: documentStoreServices.updateDocumentStoreUsage - ${getErrorMessage(error)}` - ) - } -} - -const updateVectorStoreConfigOnly = async (data: ICommonObject) => { - try { - const appServer = getRunningExpressApp() - const entity = await appServer.AppDataSource.getRepository(DocumentStore).findOneBy({ - id: data.storeId - }) - if (!entity) { - throw new InternalFlowiseError(StatusCodes.NOT_FOUND, `Document store ${data.storeId} not found`) - } - - if (data.vectorStoreName) { - entity.vectorStoreConfig = JSON.stringify({ - config: data.vectorStoreConfig, - name: data.vectorStoreName - }) - - const updatedEntity = await appServer.AppDataSource.getRepository(DocumentStore).save(entity) - return updatedEntity - } - return {} - } catch (error) { - throw new InternalFlowiseError( - StatusCodes.INTERNAL_SERVER_ERROR, - `Error: documentStoreServices.updateVectorStoreConfig - ${getErrorMessage(error)}` - ) - } -} -const saveVectorStoreConfig = async (data: ICommonObject) => { - try { - const appServer = getRunningExpressApp() - const entity = await appServer.AppDataSource.getRepository(DocumentStore).findOneBy({ - id: data.storeId - }) - if (!entity) { - throw new InternalFlowiseError(StatusCodes.NOT_FOUND, `Document store ${data.storeId} not found`) - } - - if (data.embeddingName) { - entity.embeddingConfig = JSON.stringify({ - config: data.embeddingConfig, - name: data.embeddingName - }) - } else if (entity.embeddingConfig && !data.embeddingName && !data.embeddingConfig) { - data.embeddingConfig = JSON.parse(entity.embeddingConfig)?.config - data.embeddingName = JSON.parse(entity.embeddingConfig)?.name - } else if (!data.embeddingName && !data.embeddingConfig) { - entity.embeddingConfig = null - } - - if (data.vectorStoreName) { - entity.vectorStoreConfig = JSON.stringify({ - config: data.vectorStoreConfig, - name: data.vectorStoreName - }) - } else if (entity.vectorStoreConfig && !data.vectorStoreName && !data.vectorStoreConfig) { - data.vectorStoreConfig = JSON.parse(entity.vectorStoreConfig)?.config - data.vectorStoreName = JSON.parse(entity.vectorStoreConfig)?.name - } else if (!data.vectorStoreName && !data.vectorStoreConfig) { - entity.vectorStoreConfig = null - } - - if (data.recordManagerName) { - entity.recordManagerConfig = JSON.stringify({ - config: data.recordManagerConfig, - name: data.recordManagerName - }) - } else if (entity.recordManagerConfig && !data.recordManagerName && !data.recordManagerConfig) { - data.recordManagerConfig = JSON.parse(entity.recordManagerConfig)?.config - data.recordManagerName = JSON.parse(entity.recordManagerConfig)?.name - } else if (!data.recordManagerName && !data.recordManagerConfig) { - entity.recordManagerConfig = null - } - - if (entity.status !== DocumentStoreStatus.UPSERTED && (data.vectorStoreName || data.recordManagerName || data.embeddingName)) { - // if the store is not already in sync, mark it as sync - // this also means that the store is not yet sync'ed to vector store - entity.status = DocumentStoreStatus.SYNC - } - await appServer.AppDataSource.getRepository(DocumentStore).save(entity) - return entity - } catch (error) { - throw new InternalFlowiseError( - StatusCodes.INTERNAL_SERVER_ERROR, - `Error: documentStoreServices.saveVectorStoreConfig - ${getErrorMessage(error)}` - ) - } -} - -const insertIntoVectorStore = async (data: ICommonObject) => { - try { - const appServer = getRunningExpressApp() - const entity = await saveVectorStoreConfig(data) - entity.status = DocumentStoreStatus.UPSERTING - await appServer.AppDataSource.getRepository(DocumentStore).save(entity) - - // TODO: to be moved into a worker thread... - const indexResult = await _insertIntoVectorStoreWorkerThread(data) - return indexResult - } catch (error) { - throw new InternalFlowiseError( - StatusCodes.INTERNAL_SERVER_ERROR, - `Error: documentStoreServices.insertIntoVectorStore - ${getErrorMessage(error)}` - ) - } -} - -const _insertIntoVectorStoreWorkerThread = async (data: ICommonObject) => { - try { - const appServer = getRunningExpressApp() - const entity = await saveVectorStoreConfig(data) - let upsertHistory: Record = {} - const chatflowid = data.storeId // fake chatflowid because this is not tied to any chatflow - - const options: ICommonObject = { - chatflowid, - appDataSource: appServer.AppDataSource, - databaseEntities, - logger - } - - let recordManagerObj = undefined - - // Get Record Manager Instance - if (data.recordManagerName && data.recordManagerConfig) { - recordManagerObj = await _createRecordManagerObject(appServer, data, options, upsertHistory) - } - - // Get Embeddings Instance - const embeddingObj = await _createEmbeddingsObject(appServer, data, options, upsertHistory) - - // Get Vector Store Node Data - const vStoreNodeData = _createVectorStoreNodeData(appServer, data, embeddingObj, recordManagerObj) - // Prepare docs for upserting - const chunks = await appServer.AppDataSource.getRepository(DocumentStoreFileChunk).find({ - where: { - storeId: data.storeId - } - }) - const docs: Document[] = chunks.map((chunk: DocumentStoreFileChunk) => { - return new Document({ - pageContent: chunk.pageContent, - metadata: JSON.parse(chunk.metadata) - }) - }) - vStoreNodeData.inputs.document = docs - - // Get Vector Store Instance - const vectorStoreObj = await _createVectorStoreObject(appServer, data, vStoreNodeData, upsertHistory) - const indexResult = await vectorStoreObj.vectorStoreMethods.upsert(vStoreNodeData, options) - - // Save to DB - if (indexResult) { - const result = cloneDeep(upsertHistory) - result['flowData'] = JSON.stringify(result['flowData']) - result['result'] = JSON.stringify(omit(indexResult, ['totalKeys', 'addedDocs'])) - result.chatflowid = chatflowid - const newUpsertHistory = new UpsertHistory() - Object.assign(newUpsertHistory, result) - const upsertHistoryItem = appServer.AppDataSource.getRepository(UpsertHistory).create(newUpsertHistory) - await appServer.AppDataSource.getRepository(UpsertHistory).save(upsertHistoryItem) - } - - await telemetryService.createEvent({ - name: `vector_upserted`, - data: { - version: await getAppVersion(), - chatlowId: chatflowid, - type: chatType.INTERNAL, - flowGraph: omit(indexResult['result'], ['totalKeys', 'addedDocs']) - } - }) - - entity.status = DocumentStoreStatus.UPSERTED - await appServer.AppDataSource.getRepository(DocumentStore).save(entity) - - return indexResult ?? { result: 'Successfully Upserted' } - } catch (error) { - throw new InternalFlowiseError( - StatusCodes.INTERNAL_SERVER_ERROR, - `Error: documentStoreServices._insertIntoVectorStoreWorkerThread - ${getErrorMessage(error)}` - ) - } -} - -// Get all component nodes - Embeddings -const getEmbeddingProviders = async () => { - try { - const dbResponse = await nodesService.getAllNodesForCategory('Embeddings') - return dbResponse.filter((node) => !node.tags?.includes('LlamaIndex')) - } catch (error) { - throw new InternalFlowiseError( - StatusCodes.INTERNAL_SERVER_ERROR, - `Error: documentStoreServices.getEmbeddingProviders - ${getErrorMessage(error)}` - ) - } -} - -// Get all component nodes - Vector Stores -const getVectorStoreProviders = async () => { - try { - const dbResponse = await nodesService.getAllNodesForCategory('Vector Stores') - return dbResponse.filter((node) => !node.tags?.includes('LlamaIndex') && node.name !== 'documentStoreVS') - } catch (error) { - throw new InternalFlowiseError( - StatusCodes.INTERNAL_SERVER_ERROR, - `Error: documentStoreServices.getVectorStoreProviders - ${getErrorMessage(error)}` - ) - } -} -// Get all component nodes - Vector Stores -const getRecordManagerProviders = async () => { - try { - const dbResponse = await nodesService.getAllNodesForCategory('Record Manager') - return dbResponse.filter((node) => !node.tags?.includes('LlamaIndex')) - } catch (error) { - throw new InternalFlowiseError( - StatusCodes.INTERNAL_SERVER_ERROR, - `Error: documentStoreServices.getRecordManagerProviders - ${getErrorMessage(error)}` - ) - } -} - -const queryVectorStore = async (data: ICommonObject) => { - try { - const appServer = getRunningExpressApp() - const entity = await appServer.AppDataSource.getRepository(DocumentStore).findOneBy({ - id: data.storeId - }) - if (!entity) { - throw new InternalFlowiseError(StatusCodes.INTERNAL_SERVER_ERROR, `Document store ${data.storeId} not found`) - } - const options: ICommonObject = { - chatflowid: uuidv4(), - appDataSource: appServer.AppDataSource, - databaseEntities, - logger - } - - if (!entity.embeddingConfig) { - throw new InternalFlowiseError(StatusCodes.INTERNAL_SERVER_ERROR, `Embedding for ${data.storeId} is not configured`) - } - - if (!entity.vectorStoreConfig) { - throw new InternalFlowiseError(StatusCodes.INTERNAL_SERVER_ERROR, `Vector Store for ${data.storeId} is not configured`) - } - - const embeddingConfig = JSON.parse(entity.embeddingConfig) - data.embeddingName = embeddingConfig.name - data.embeddingConfig = embeddingConfig.config - let embeddingObj = await _createEmbeddingsObject(appServer, data, options) - - const vsConfig = JSON.parse(entity.vectorStoreConfig) - data.vectorStoreName = vsConfig.name - data.vectorStoreConfig = vsConfig.config - if (data.inputs) { - data.vectorStoreConfig = { ...vsConfig.config, ...data.inputs } - } - - const vStoreNodeData = _createVectorStoreNodeData(appServer, data, embeddingObj, undefined) - - // Get Vector Store Instance - const vectorStoreObj = await _createVectorStoreObject(appServer, data, vStoreNodeData) - const retriever = await vectorStoreObj.init(vStoreNodeData, '', options) - if (!retriever) { - throw new InternalFlowiseError(StatusCodes.INTERNAL_SERVER_ERROR, `Failed to create retriever`) - } - const startMillis = Date.now() - const results = await retriever.invoke(data.query, undefined) - if (!results) { - throw new InternalFlowiseError(StatusCodes.INTERNAL_SERVER_ERROR, `Failed to retrieve results`) - } - const endMillis = Date.now() - const timeTaken = endMillis - startMillis - const docs: any = results.map((result: IDocument) => { - return { - pageContent: result.pageContent, - metadata: result.metadata, - id: uuidv4() - } - }) - // query our document store chunk with the storeId and pageContent - for (const doc of docs) { - const documentStoreChunk = await appServer.AppDataSource.getRepository(DocumentStoreFileChunk).findOneBy({ - storeId: data.storeId, - pageContent: doc.pageContent - }) - if (documentStoreChunk) { - doc.id = documentStoreChunk.id - doc.chunkNo = documentStoreChunk.chunkNo - } else { - // this should not happen, only possible if the vector store has more content - // than our document store - doc.id = uuidv4() - doc.chunkNo = -1 - } - } - - return { - timeTaken: timeTaken, - docs: docs - } - } catch (error) { - throw new InternalFlowiseError( - StatusCodes.INTERNAL_SERVER_ERROR, - `Error: documentStoreServices.queryVectorStore - ${getErrorMessage(error)}` - ) - } -} - -const _createEmbeddingsObject = async ( - appServer: App, - data: ICommonObject, - options: ICommonObject, - upsertHistory?: Record -): Promise => { - // prepare embedding node data - const embeddingComponent = appServer.nodesPool.componentNodes[data.embeddingName] - const embeddingNodeData: any = { - inputs: { ...data.embeddingConfig }, - outputs: { output: 'document' }, - id: `${embeddingComponent.name}_0`, - label: embeddingComponent.label, - name: embeddingComponent.name, - category: embeddingComponent.category, - inputParams: embeddingComponent.inputs || [] - } - if (data.embeddingConfig.credential) { - embeddingNodeData.credential = data.embeddingConfig.credential - } - - // save to upsert history - if (upsertHistory) upsertHistory['flowData'] = saveUpsertFlowData(embeddingNodeData, upsertHistory) - - // init embedding object - const embeddingNodeInstanceFilePath = embeddingComponent.filePath as string - const embeddingNodeModule = await import(embeddingNodeInstanceFilePath) - const embeddingNodeInstance = new embeddingNodeModule.nodeClass() - const embeddingObj = await embeddingNodeInstance.init(embeddingNodeData, '', options) - if (!embeddingObj) { - throw new InternalFlowiseError(StatusCodes.INTERNAL_SERVER_ERROR, `Failed to create EmbeddingObj`) - } - return embeddingObj -} - -const _createRecordManagerObject = async ( - appServer: App, - data: ICommonObject, - options: ICommonObject, - upsertHistory?: Record -) => { - // prepare record manager node data - const recordManagerComponent = appServer.nodesPool.componentNodes[data.recordManagerName] - const rmNodeData: any = { - inputs: { ...data.recordManagerConfig }, - id: `${recordManagerComponent.name}_0`, - inputParams: recordManagerComponent.inputs, - label: recordManagerComponent.label, - name: recordManagerComponent.name, - category: recordManagerComponent.category - } - if (data.recordManagerConfig.credential) { - rmNodeData.credential = data.recordManagerConfig.credential - } - - // save to upsert history - if (upsertHistory) upsertHistory['flowData'] = saveUpsertFlowData(rmNodeData, upsertHistory) - - // init record manager object - const rmNodeInstanceFilePath = recordManagerComponent.filePath as string - const rmNodeModule = await import(rmNodeInstanceFilePath) - const rmNodeInstance = new rmNodeModule.nodeClass() - const recordManagerObj = await rmNodeInstance.init(rmNodeData, '', options) - if (!recordManagerObj) { - throw new InternalFlowiseError(StatusCodes.INTERNAL_SERVER_ERROR, `Failed to create RecordManager obj`) - } - return recordManagerObj -} - -const _createVectorStoreNodeData = (appServer: App, data: ICommonObject, embeddingObj: any, recordManagerObj?: any) => { - const vectorStoreComponent = appServer.nodesPool.componentNodes[data.vectorStoreName] - const vStoreNodeData: any = { - id: `${vectorStoreComponent.name}_0`, - inputs: { ...data.vectorStoreConfig }, - outputs: { output: 'retriever' }, - label: vectorStoreComponent.label, - name: vectorStoreComponent.name, - category: vectorStoreComponent.category - } - if (data.vectorStoreConfig.credential) { - vStoreNodeData.credential = data.vectorStoreConfig.credential - } - - if (embeddingObj) { - vStoreNodeData.inputs.embeddings = embeddingObj - } - - if (recordManagerObj) { - vStoreNodeData.inputs.recordManager = recordManagerObj - } - - // Get all input params except the ones that are anchor points to avoid JSON stringify circular error - const filterInputParams = ['document', 'embeddings', 'recordManager'] - const inputParams = vectorStoreComponent.inputs?.filter((input) => !filterInputParams.includes(input.name)) - vStoreNodeData.inputParams = inputParams - return vStoreNodeData -} - -const _createVectorStoreObject = async ( - appServer: App, - data: ICommonObject, - vStoreNodeData: INodeData, - upsertHistory?: Record -) => { - const vStoreNodeInstanceFilePath = appServer.nodesPool.componentNodes[data.vectorStoreName].filePath as string - const vStoreNodeModule = await import(vStoreNodeInstanceFilePath) - const vStoreNodeInstance = new vStoreNodeModule.nodeClass() - if (upsertHistory) upsertHistory['flowData'] = saveUpsertFlowData(vStoreNodeData, upsertHistory) - return vStoreNodeInstance -} - -export default { - updateDocumentStoreUsage, - deleteDocumentStore, - createDocumentStore, - deleteLoaderFromDocumentStore, - getAllDocumentStores, - getAllDocumentFileChunks, - getDocumentStoreById, - getUsedChatflowNames, - getDocumentStoreFileChunks, - updateDocumentStore, - previewChunks, - processAndSaveChunks, - deleteDocumentStoreFileChunk, - editDocumentStoreFileChunk, - getDocumentLoaders, - insertIntoVectorStore, - getEmbeddingProviders, - getVectorStoreProviders, - getRecordManagerProviders, - saveVectorStoreConfig, - queryVectorStore, - deleteVectorStoreFromStore, - updateVectorStoreConfigOnly -} diff --git a/studio-frontend/packages/server/src/services/export-import/index.ts b/studio-frontend/packages/server/src/services/export-import/index.ts index 9899d24..568dde0 100644 --- a/studio-frontend/packages/server/src/services/export-import/index.ts +++ b/studio-frontend/packages/server/src/services/export-import/index.ts @@ -2,21 +2,18 @@ import { StatusCodes } from 'http-status-codes' import { ChatFlow } from '../../database/entities/ChatFlow' import { Tool } from '../../database/entities/Tool' import { Variable } from '../../database/entities/Variable' -import { Assistant } from '../../database/entities/Assistant' import { InternalFlowiseError } from '../../errors/internalFlowiseError' import { getErrorMessage } from '../../errors/utils' import { getRunningExpressApp } from '../../utils/getRunningExpressApp' import chatflowService from '../chatflows' import toolsService from '../tools' import variableService from '../variables' -import assistantService from '../assistants' type ExportInput = { tool: boolean chatflow: boolean agentflow: boolean variable: boolean - assistant: boolean } type ExportData = { @@ -24,7 +21,6 @@ type ExportData = { ChatFlow: ChatFlow[] AgentFlow: ChatFlow[] Variable: Variable[] - Assistant: Assistant[] } const convertExportInput = (body: any): ExportInput => { @@ -62,16 +58,12 @@ const exportData = async (exportInput: ExportInput): Promise<{ FileDefaultName: let allVars: Variable[] = [] if (exportInput.variable === true) allVars = await variableService.getAllVariables() - let allAssistants: Assistant[] = [] - if (exportInput.assistant === true) allAssistants = await assistantService.getAllAssistants() - return { FileDefaultName, Tool: allTool, ChatFlow: allChatflow, AgentFlow: allMultiAgent, - Variable: allVars, - Assistant: allAssistants + Variable: allVars } } catch (error) { throw new InternalFlowiseError( @@ -96,7 +88,6 @@ const importData = async (importData: ExportData) => { // step 3 - importAgentlows if (importData.AgentFlow.length > 0) await chatflowService.importChatflows(importData.AgentFlow) if (importData.Variable.length > 0) await variableService.importVariables(importData.Variable) - if (importData.Assistant.length > 0) await assistantService.importAssistants(importData.Assistant) queryRunner.commitTransaction() } catch (error) { queryRunner.rollbackTransaction() diff --git a/studio-frontend/packages/server/src/services/fetch-links/index.ts b/studio-frontend/packages/server/src/services/fetch-links/index.ts deleted file mode 100644 index 53c6e94..0000000 --- a/studio-frontend/packages/server/src/services/fetch-links/index.ts +++ /dev/null @@ -1,34 +0,0 @@ -import { webCrawl, xmlScrape } from 'flowise-components' -import { StatusCodes } from 'http-status-codes' -import { InternalFlowiseError } from '../../errors/internalFlowiseError' -import { getErrorMessage } from '../../errors/utils' - -const getAllLinks = async (requestUrl: string, relativeLinksMethod: string, queryLimit: string): Promise => { - try { - const url = decodeURIComponent(requestUrl) - if (!relativeLinksMethod) { - throw new InternalFlowiseError( - StatusCodes.INTERNAL_SERVER_ERROR, - `Please choose a Relative Links Method in Additional Parameters!` - ) - } - const limit = parseInt(queryLimit) - if (process.env.DEBUG === 'true') console.info(`Start ${relativeLinksMethod}`) - const links: string[] = relativeLinksMethod === 'webCrawl' ? await webCrawl(url, limit) : await xmlScrape(url, limit) - if (process.env.DEBUG === 'true') console.info(`Finish ${relativeLinksMethod}`) - const dbResponse = { - status: 'OK', - links - } - return dbResponse - } catch (error) { - throw new InternalFlowiseError( - StatusCodes.INTERNAL_SERVER_ERROR, - `Error: fetchLinksService.getAllLinks - ${getErrorMessage(error)}` - ) - } -} - -export default { - getAllLinks -} diff --git a/studio-frontend/packages/server/src/services/leads/index.ts b/studio-frontend/packages/server/src/services/leads/index.ts deleted file mode 100644 index 85e38d3..0000000 --- a/studio-frontend/packages/server/src/services/leads/index.ts +++ /dev/null @@ -1,43 +0,0 @@ -import { v4 as uuidv4 } from 'uuid' -import { StatusCodes } from 'http-status-codes' -import { getRunningExpressApp } from '../../utils/getRunningExpressApp' -import { Lead } from '../../database/entities/Lead' -import { ILead } from '../../Interface' -import { InternalFlowiseError } from '../../errors/internalFlowiseError' -import { getErrorMessage } from '../../errors/utils' - -const getAllLeads = async (chatflowid: string) => { - try { - const appServer = getRunningExpressApp() - const dbResponse = await appServer.AppDataSource.getRepository(Lead).find({ - where: { - chatflowid - } - }) - return dbResponse - } catch (error) { - throw new InternalFlowiseError(StatusCodes.INTERNAL_SERVER_ERROR, `Error: leadsService.getAllLeads - ${getErrorMessage(error)}`) - } -} - -const createLead = async (body: Partial) => { - try { - const chatId = body.chatId ?? uuidv4() - - const newLead = new Lead() - Object.assign(newLead, body) - Object.assign(newLead, { chatId }) - - const appServer = getRunningExpressApp() - const lead = appServer.AppDataSource.getRepository(Lead).create(newLead) - const dbResponse = await appServer.AppDataSource.getRepository(Lead).save(lead) - return dbResponse - } catch (error) { - throw new InternalFlowiseError(StatusCodes.INTERNAL_SERVER_ERROR, `Error: leadsService.createLead - ${getErrorMessage(error)}`) - } -} - -export default { - createLead, - getAllLeads -} diff --git a/studio-frontend/packages/server/src/services/load-prompts/index.ts b/studio-frontend/packages/server/src/services/load-prompts/index.ts deleted file mode 100644 index 5f041a8..0000000 --- a/studio-frontend/packages/server/src/services/load-prompts/index.ts +++ /dev/null @@ -1,28 +0,0 @@ -import { Client } from 'langchainhub' -import { StatusCodes } from 'http-status-codes' -import { parsePrompt } from '../../utils/hub' -import { InternalFlowiseError } from '../../errors/internalFlowiseError' -import { getErrorMessage } from '../../errors/utils' - -const createPrompt = async (promptName: string): Promise => { - try { - let hub = new Client() - const prompt = await hub.pull(promptName) - const templates = parsePrompt(prompt) - const dbResponse = { - status: 'OK', - prompt: promptName, - templates: templates - } - return dbResponse - } catch (error) { - throw new InternalFlowiseError( - StatusCodes.INTERNAL_SERVER_ERROR, - `Error: loadPromptsService.createPrompt - ${getErrorMessage(error)}` - ) - } -} - -export default { - createPrompt -} diff --git a/studio-frontend/packages/server/src/services/marketplaces/index.ts b/studio-frontend/packages/server/src/services/marketplaces/index.ts deleted file mode 100644 index ee5d131..0000000 --- a/studio-frontend/packages/server/src/services/marketplaces/index.ts +++ /dev/null @@ -1,248 +0,0 @@ -import path from 'path' -import * as fs from 'fs' -import { StatusCodes } from 'http-status-codes' -import { InternalFlowiseError } from '../../errors/internalFlowiseError' -import { getErrorMessage } from '../../errors/utils' -import { IReactFlowEdge, IReactFlowNode } from '../../Interface' -import { getRunningExpressApp } from '../../utils/getRunningExpressApp' -import { DeleteResult } from 'typeorm' -import { CustomTemplate } from '../../database/entities/CustomTemplate' - -import chatflowsService from '../chatflows' - -type ITemplate = { - badge: string - description: string - framework: string[] - usecases: string[] - nodes: IReactFlowNode[] - edges: IReactFlowEdge[] -} - -const getCategories = (fileDataObj: ITemplate) => { - return Array.from(new Set(fileDataObj?.nodes?.map((node) => node.data.category).filter((category) => category))) -} - -// Get all templates for marketplaces -const getAllTemplates = async () => { - try { - let marketplaceDir = path.join(__dirname, '..', '..', '..', 'marketplaces', 'chatflows') - let jsonsInDir = fs.readdirSync(marketplaceDir).filter((file) => path.extname(file) === '.json') - let templates: any[] = [] - jsonsInDir.forEach((file, index) => { - const filePath = path.join(__dirname, '..', '..', '..', 'marketplaces', 'chatflows', file) - const fileData = fs.readFileSync(filePath) - const fileDataObj = JSON.parse(fileData.toString()) as ITemplate - - const template = { - id: index, - templateName: file.split('.json')[0], - flowData: fileData.toString(), - badge: fileDataObj?.badge, - framework: fileDataObj?.framework, - usecases: fileDataObj?.usecases, - categories: getCategories(fileDataObj), - type: 'Chatflow', - description: fileDataObj?.description || '' - } - templates.push(template) - }) - - marketplaceDir = path.join(__dirname, '..', '..', '..', 'marketplaces', 'tools') - jsonsInDir = fs.readdirSync(marketplaceDir).filter((file) => path.extname(file) === '.json') - jsonsInDir.forEach((file, index) => { - const filePath = path.join(__dirname, '..', '..', '..', 'marketplaces', 'tools', file) - const fileData = fs.readFileSync(filePath) - const fileDataObj = JSON.parse(fileData.toString()) - const template = { - ...fileDataObj, - id: index, - type: 'Tool', - framework: fileDataObj?.framework, - badge: fileDataObj?.badge, - usecases: fileDataObj?.usecases, - categories: [], - templateName: file.split('.json')[0] - } - templates.push(template) - }) - - marketplaceDir = path.join(__dirname, '..', '..', '..', 'marketplaces', 'agentflows') - jsonsInDir = fs.readdirSync(marketplaceDir).filter((file) => path.extname(file) === '.json') - jsonsInDir.forEach((file, index) => { - const filePath = path.join(__dirname, '..', '..', '..', 'marketplaces', 'agentflows', file) - const fileData = fs.readFileSync(filePath) - const fileDataObj = JSON.parse(fileData.toString()) - const template = { - id: index, - templateName: file.split('.json')[0], - flowData: fileData.toString(), - badge: fileDataObj?.badge, - framework: fileDataObj?.framework, - usecases: fileDataObj?.usecases, - categories: getCategories(fileDataObj), - type: 'Agentflow', - description: fileDataObj?.description || '' - } - templates.push(template) - }) - const sortedTemplates = templates.sort((a, b) => a.templateName.localeCompare(b.templateName)) - const FlowiseDocsQnAIndex = sortedTemplates.findIndex((tmp) => tmp.templateName === 'Flowise Docs QnA') - if (FlowiseDocsQnAIndex > 0) { - sortedTemplates.unshift(sortedTemplates.splice(FlowiseDocsQnAIndex, 1)[0]) - } - const dbResponse = sortedTemplates - return dbResponse - } catch (error) { - throw new InternalFlowiseError( - StatusCodes.INTERNAL_SERVER_ERROR, - `Error: marketplacesService.getAllTemplates - ${getErrorMessage(error)}` - ) - } -} - -const deleteCustomTemplate = async (templateId: string): Promise => { - try { - const appServer = getRunningExpressApp() - return await appServer.AppDataSource.getRepository(CustomTemplate).delete({ id: templateId }) - } catch (error) { - throw new InternalFlowiseError( - StatusCodes.INTERNAL_SERVER_ERROR, - `Error: marketplacesService.deleteCustomTemplate - ${getErrorMessage(error)}` - ) - } -} - -const getAllCustomTemplates = async (): Promise => { - try { - const appServer = getRunningExpressApp() - const templates: any[] = await appServer.AppDataSource.getRepository(CustomTemplate).find() - templates.map((template) => { - template.usecases = template.usecases ? JSON.parse(template.usecases) : '' - if (template.type === 'Tool') { - template.flowData = JSON.parse(template.flowData) - template.iconSrc = template.flowData.iconSrc - template.schema = template.flowData.schema - template.func = template.flowData.func - template.categories = [] - template.flowData = undefined - } else { - template.categories = getCategories(JSON.parse(template.flowData)) - } - if (!template.badge) { - template.badge = '' - } - if (!template.framework) { - template.framework = '' - } - }) - return templates - } catch (error) { - throw new InternalFlowiseError( - StatusCodes.INTERNAL_SERVER_ERROR, - `Error: marketplacesService.getAllCustomTemplates - ${getErrorMessage(error)}` - ) - } -} - -const saveCustomTemplate = async (body: any): Promise => { - try { - const appServer = getRunningExpressApp() - let flowDataStr = '' - let derivedFramework = '' - const customTemplate = new CustomTemplate() - Object.assign(customTemplate, body) - - if (body.chatflowId) { - const chatflow = await chatflowsService.getChatflowById(body.chatflowId) - const flowData = JSON.parse(chatflow.flowData) - const { framework, exportJson } = _generateExportFlowData(flowData) - flowDataStr = JSON.stringify(exportJson) - customTemplate.framework = framework - } else if (body.tool) { - const flowData = { - iconSrc: body.tool.iconSrc, - schema: body.tool.schema, - func: body.tool.func - } - customTemplate.framework = '' - customTemplate.type = 'Tool' - flowDataStr = JSON.stringify(flowData) - } - customTemplate.framework = derivedFramework - if (customTemplate.usecases) { - customTemplate.usecases = JSON.stringify(customTemplate.usecases) - } - const entity = appServer.AppDataSource.getRepository(CustomTemplate).create(customTemplate) - entity.flowData = flowDataStr - const flowTemplate = await appServer.AppDataSource.getRepository(CustomTemplate).save(entity) - return flowTemplate - } catch (error) { - throw new InternalFlowiseError( - StatusCodes.INTERNAL_SERVER_ERROR, - `Error: marketplacesService.saveCustomTemplate - ${getErrorMessage(error)}` - ) - } -} - -const _generateExportFlowData = (flowData: any) => { - const nodes = flowData.nodes - const edges = flowData.edges - - let framework = 'Langchain' - for (let i = 0; i < nodes.length; i += 1) { - nodes[i].selected = false - const node = nodes[i] - - const newNodeData = { - id: node.data.id, - label: node.data.label, - version: node.data.version, - name: node.data.name, - type: node.data.type, - baseClasses: node.data.baseClasses, - tags: node.data.tags, - category: node.data.category, - description: node.data.description, - inputParams: node.data.inputParams, - inputAnchors: node.data.inputAnchors, - inputs: {}, - outputAnchors: node.data.outputAnchors, - outputs: node.data.outputs, - selected: false - } - - if (node.data.tags && node.data.tags.length) { - if (node.data.tags.includes('LlamaIndex')) { - framework = 'LlamaIndex' - } - } - - // Remove password, file & folder - if (node.data.inputs && Object.keys(node.data.inputs).length) { - const nodeDataInputs: any = {} - for (const input in node.data.inputs) { - const inputParam = node.data.inputParams.find((inp: any) => inp.name === input) - if (inputParam && inputParam.type === 'password') continue - if (inputParam && inputParam.type === 'file') continue - if (inputParam && inputParam.type === 'folder') continue - nodeDataInputs[input] = node.data.inputs[input] - } - newNodeData.inputs = nodeDataInputs - } - - nodes[i].data = newNodeData - } - const exportJson = { - nodes, - edges - } - return { exportJson, framework } -} - -export default { - getAllTemplates, - getAllCustomTemplates, - saveCustomTemplate, - deleteCustomTemplate -} diff --git a/studio-frontend/packages/server/src/services/nodes/index.ts b/studio-frontend/packages/server/src/services/nodes/index.ts index 7dd8b2f..7ab1488 100644 --- a/studio-frontend/packages/server/src/services/nodes/index.ts +++ b/studio-frontend/packages/server/src/services/nodes/index.ts @@ -1,13 +1,18 @@ import { cloneDeep } from 'lodash' import { StatusCodes } from 'http-status-codes' import { getRunningExpressApp } from '../../utils/getRunningExpressApp' -import { INodeData } from '../../Interface' -import { INodeOptionsValue, ICommonObject, handleEscapeCharacters } from 'flowise-components' -import { databaseEntities } from '../../utils' +import { INodeData, ICommonObject } from '../../Interface' +import { databaseEntities, handleEscapeCharacters } from '../../utils' import logger from '../../utils/logger' import { InternalFlowiseError } from '../../errors/internalFlowiseError' import { getErrorMessage } from '../../errors/utils' +export interface INodeOptionsValue { + label: string + name: string + description?: string +} + // Get all component nodes const getAllNodes = async () => { try { diff --git a/studio-frontend/packages/server/src/services/openai-assistants-vector-store/index.ts b/studio-frontend/packages/server/src/services/openai-assistants-vector-store/index.ts deleted file mode 100644 index 7b598e1..0000000 --- a/studio-frontend/packages/server/src/services/openai-assistants-vector-store/index.ts +++ /dev/null @@ -1,258 +0,0 @@ -import OpenAI from 'openai' -import { StatusCodes } from 'http-status-codes' -import fs from 'fs' -import { Credential } from '../../database/entities/Credential' -import { InternalFlowiseError } from '../../errors/internalFlowiseError' -import { getErrorMessage } from '../../errors/utils' -import { getRunningExpressApp } from '../../utils/getRunningExpressApp' -import { decryptCredentialData } from '../../utils' - -const getAssistantVectorStore = async (credentialId: string, vectorStoreId: string) => { - try { - const appServer = getRunningExpressApp() - const credential = await appServer.AppDataSource.getRepository(Credential).findOneBy({ - id: credentialId - }) - if (!credential) { - throw new InternalFlowiseError(StatusCodes.NOT_FOUND, `Credential ${credentialId} not found in the database!`) - } - // Decrpyt credentialData - const decryptedCredentialData = await decryptCredentialData(credential.encryptedData) - const openAIApiKey = decryptedCredentialData['openAIApiKey'] - if (!openAIApiKey) { - throw new InternalFlowiseError(StatusCodes.NOT_FOUND, `OpenAI ApiKey not found`) - } - - const openai = new OpenAI({ apiKey: openAIApiKey }) - const dbResponse = await openai.vectorStores.retrieve(vectorStoreId) - return dbResponse - } catch (error) { - throw new InternalFlowiseError( - StatusCodes.INTERNAL_SERVER_ERROR, - `Error: openaiAssistantsVectorStoreService.getAssistantVectorStore - ${getErrorMessage(error)}` - ) - } -} - -const listAssistantVectorStore = async (credentialId: string) => { - try { - const appServer = getRunningExpressApp() - const credential = await appServer.AppDataSource.getRepository(Credential).findOneBy({ - id: credentialId - }) - if (!credential) { - throw new InternalFlowiseError(StatusCodes.NOT_FOUND, `Credential ${credentialId} not found in the database!`) - } - // Decrpyt credentialData - const decryptedCredentialData = await decryptCredentialData(credential.encryptedData) - const openAIApiKey = decryptedCredentialData['openAIApiKey'] - if (!openAIApiKey) { - throw new InternalFlowiseError(StatusCodes.NOT_FOUND, `OpenAI ApiKey not found`) - } - - const openai = new OpenAI({ apiKey: openAIApiKey }) - const dbResponse = await openai.vectorStores.list() - return dbResponse.data - } catch (error) { - throw new InternalFlowiseError( - StatusCodes.INTERNAL_SERVER_ERROR, - `Error: openaiAssistantsVectorStoreService.listAssistantVectorStore - ${getErrorMessage(error)}` - ) - } -} - -const createAssistantVectorStore = async (credentialId: string, obj: OpenAI.VectorStores.VectorStoreCreateParams) => { - try { - const appServer = getRunningExpressApp() - const credential = await appServer.AppDataSource.getRepository(Credential).findOneBy({ - id: credentialId - }) - if (!credential) { - throw new InternalFlowiseError(StatusCodes.NOT_FOUND, `Credential ${credentialId} not found in the database!`) - } - // Decrpyt credentialData - const decryptedCredentialData = await decryptCredentialData(credential.encryptedData) - const openAIApiKey = decryptedCredentialData['openAIApiKey'] - if (!openAIApiKey) { - throw new InternalFlowiseError(StatusCodes.NOT_FOUND, `OpenAI ApiKey not found`) - } - - const openai = new OpenAI({ apiKey: openAIApiKey }) - const dbResponse = await openai.vectorStores.create(obj) - return dbResponse - } catch (error) { - throw new InternalFlowiseError( - StatusCodes.INTERNAL_SERVER_ERROR, - `Error: openaiAssistantsVectorStoreService.createAssistantVectorStore - ${getErrorMessage(error)}` - ) - } -} - -const updateAssistantVectorStore = async ( - credentialId: string, - vectorStoreId: string, - obj: OpenAI.VectorStores.VectorStoreUpdateParams -) => { - try { - const appServer = getRunningExpressApp() - const credential = await appServer.AppDataSource.getRepository(Credential).findOneBy({ - id: credentialId - }) - if (!credential) { - throw new InternalFlowiseError(StatusCodes.NOT_FOUND, `Credential ${credentialId} not found in the database!`) - } - // Decrpyt credentialData - const decryptedCredentialData = await decryptCredentialData(credential.encryptedData) - const openAIApiKey = decryptedCredentialData['openAIApiKey'] - if (!openAIApiKey) { - throw new InternalFlowiseError(StatusCodes.NOT_FOUND, `OpenAI ApiKey not found`) - } - - const openai = new OpenAI({ apiKey: openAIApiKey }) - const dbResponse = await openai.vectorStores.update(vectorStoreId, obj) - const vectorStoreFiles = await openai.vectorStores.files.list(vectorStoreId) - if (vectorStoreFiles.data?.length) { - const files = [] - for (const file of vectorStoreFiles.data) { - const fileData = await openai.files.retrieve(file.id) - files.push(fileData) - } - ;(dbResponse as any).files = files - } - return dbResponse - } catch (error) { - throw new InternalFlowiseError( - StatusCodes.INTERNAL_SERVER_ERROR, - `Error: openaiAssistantsVectorStoreService.updateAssistantVectorStore - ${getErrorMessage(error)}` - ) - } -} - -const deleteAssistantVectorStore = async (credentialId: string, vectorStoreId: string) => { - try { - const appServer = getRunningExpressApp() - const credential = await appServer.AppDataSource.getRepository(Credential).findOneBy({ - id: credentialId - }) - if (!credential) { - throw new InternalFlowiseError(StatusCodes.NOT_FOUND, `Credential ${credentialId} not found in the database!`) - } - // Decrpyt credentialData - const decryptedCredentialData = await decryptCredentialData(credential.encryptedData) - const openAIApiKey = decryptedCredentialData['openAIApiKey'] - if (!openAIApiKey) { - throw new InternalFlowiseError(StatusCodes.NOT_FOUND, `OpenAI ApiKey not found`) - } - - const openai = new OpenAI({ apiKey: openAIApiKey }) - const dbResponse = await openai.vectorStores.del(vectorStoreId) - return dbResponse - } catch (error) { - throw new InternalFlowiseError( - StatusCodes.INTERNAL_SERVER_ERROR, - `Error: openaiAssistantsVectorStoreService.deleteAssistantVectorStore - ${getErrorMessage(error)}` - ) - } -} - -const uploadFilesToAssistantVectorStore = async ( - credentialId: string, - vectorStoreId: string, - files: { filePath: string; fileName: string }[] -): Promise => { - try { - const appServer = getRunningExpressApp() - const credential = await appServer.AppDataSource.getRepository(Credential).findOneBy({ - id: credentialId - }) - if (!credential) { - throw new InternalFlowiseError(StatusCodes.NOT_FOUND, `Credential ${credentialId} not found in the database!`) - } - // Decrpyt credentialData - const decryptedCredentialData = await decryptCredentialData(credential.encryptedData) - const openAIApiKey = decryptedCredentialData['openAIApiKey'] - if (!openAIApiKey) { - throw new InternalFlowiseError(StatusCodes.NOT_FOUND, `OpenAI ApiKey not found`) - } - - const openai = new OpenAI({ apiKey: openAIApiKey }) - const uploadedFiles = [] - for (const file of files) { - const toFile = await OpenAI.toFile(fs.readFileSync(file.filePath), file.fileName) - const createdFile = await openai.files.create({ - file: toFile, - purpose: 'assistants' - }) - uploadedFiles.push(createdFile) - fs.unlinkSync(file.filePath) - } - - const file_ids = [...uploadedFiles.map((file) => file.id)] - - const res = await openai.vectorStores.fileBatches.createAndPoll(vectorStoreId, { - file_ids - }) - if (res.status === 'completed' && res.file_counts.completed === uploadedFiles.length) return uploadedFiles - else if (res.status === 'failed') - throw new InternalFlowiseError( - StatusCodes.INTERNAL_SERVER_ERROR, - 'Error: openaiAssistantsVectorStoreService.uploadFilesToAssistantVectorStore - Upload failed!' - ) - else - throw new InternalFlowiseError( - StatusCodes.INTERNAL_SERVER_ERROR, - 'Error: openaiAssistantsVectorStoreService.uploadFilesToAssistantVectorStore - Upload cancelled!' - ) - } catch (error) { - throw new InternalFlowiseError( - StatusCodes.INTERNAL_SERVER_ERROR, - `Error: openaiAssistantsVectorStoreService.uploadFilesToAssistantVectorStore - ${getErrorMessage(error)}` - ) - } -} - -const deleteFilesFromAssistantVectorStore = async (credentialId: string, vectorStoreId: string, file_ids: string[]) => { - try { - const appServer = getRunningExpressApp() - const credential = await appServer.AppDataSource.getRepository(Credential).findOneBy({ - id: credentialId - }) - if (!credential) { - throw new InternalFlowiseError(StatusCodes.NOT_FOUND, `Credential ${credentialId} not found in the database!`) - } - // Decrpyt credentialData - const decryptedCredentialData = await decryptCredentialData(credential.encryptedData) - const openAIApiKey = decryptedCredentialData['openAIApiKey'] - if (!openAIApiKey) { - throw new InternalFlowiseError(StatusCodes.NOT_FOUND, `OpenAI ApiKey not found`) - } - - const openai = new OpenAI({ apiKey: openAIApiKey }) - const deletedFileIds = [] - let count = 0 - for (const file of file_ids) { - const res = await openai.vectorStores.files.del(vectorStoreId, file) - if (res.deleted) { - deletedFileIds.push(file) - count += 1 - } - } - - return { deletedFileIds, count } - } catch (error) { - throw new InternalFlowiseError( - StatusCodes.INTERNAL_SERVER_ERROR, - `Error: openaiAssistantsVectorStoreService.uploadFilesToAssistantVectorStore - ${getErrorMessage(error)}` - ) - } -} - -export default { - getAssistantVectorStore, - listAssistantVectorStore, - createAssistantVectorStore, - updateAssistantVectorStore, - deleteAssistantVectorStore, - uploadFilesToAssistantVectorStore, - deleteFilesFromAssistantVectorStore -} diff --git a/studio-frontend/packages/server/src/services/openai-assistants/index.ts b/studio-frontend/packages/server/src/services/openai-assistants/index.ts deleted file mode 100644 index 7c3df3e..0000000 --- a/studio-frontend/packages/server/src/services/openai-assistants/index.ts +++ /dev/null @@ -1,120 +0,0 @@ -import OpenAI from 'openai' -import fs from 'fs' -import { StatusCodes } from 'http-status-codes' -import { decryptCredentialData } from '../../utils' -import { getRunningExpressApp } from '../../utils/getRunningExpressApp' -import { Credential } from '../../database/entities/Credential' -import { InternalFlowiseError } from '../../errors/internalFlowiseError' -import { getErrorMessage } from '../../errors/utils' - -// ---------------------------------------- -// Assistants -// ---------------------------------------- - -// List available assistants -const getAllOpenaiAssistants = async (credentialId: string): Promise => { - try { - const appServer = getRunningExpressApp() - const credential = await appServer.AppDataSource.getRepository(Credential).findOneBy({ - id: credentialId - }) - if (!credential) { - throw new InternalFlowiseError(StatusCodes.NOT_FOUND, `Credential ${credentialId} not found in the database!`) - } - // Decrpyt credentialData - const decryptedCredentialData = await decryptCredentialData(credential.encryptedData) - const openAIApiKey = decryptedCredentialData['openAIApiKey'] - if (!openAIApiKey) { - throw new InternalFlowiseError(StatusCodes.NOT_FOUND, `OpenAI ApiKey not found`) - } - const openai = new OpenAI({ apiKey: openAIApiKey }) - const retrievedAssistants = await openai.beta.assistants.list() - const dbResponse = retrievedAssistants.data - return dbResponse - } catch (error) { - throw new InternalFlowiseError( - StatusCodes.INTERNAL_SERVER_ERROR, - `Error: openaiAssistantsService.getAllOpenaiAssistants - ${getErrorMessage(error)}` - ) - } -} - -// Get assistant object -const getSingleOpenaiAssistant = async (credentialId: string, assistantId: string): Promise => { - try { - const appServer = getRunningExpressApp() - const credential = await appServer.AppDataSource.getRepository(Credential).findOneBy({ - id: credentialId - }) - if (!credential) { - throw new InternalFlowiseError(StatusCodes.NOT_FOUND, `Credential ${credentialId} not found in the database!`) - } - // Decrpyt credentialData - const decryptedCredentialData = await decryptCredentialData(credential.encryptedData) - const openAIApiKey = decryptedCredentialData['openAIApiKey'] - if (!openAIApiKey) { - throw new InternalFlowiseError(StatusCodes.NOT_FOUND, `OpenAI ApiKey not found`) - } - - const openai = new OpenAI({ apiKey: openAIApiKey }) - const dbResponse = await openai.beta.assistants.retrieve(assistantId) - const resp = await openai.files.list() - const existingFiles = resp.data ?? [] - if (dbResponse.tool_resources?.code_interpreter?.file_ids?.length) { - ;(dbResponse.tool_resources.code_interpreter as any).files = [ - ...existingFiles.filter((file) => dbResponse.tool_resources?.code_interpreter?.file_ids?.includes(file.id)) - ] - } - if (dbResponse.tool_resources?.file_search?.vector_store_ids?.length) { - // Since there can only be 1 vector store per assistant - const vectorStoreId = dbResponse.tool_resources.file_search.vector_store_ids[0] - const vectorStoreFiles = await openai.vectorStores.files.list(vectorStoreId) - const fileIds = vectorStoreFiles.data?.map((file) => file.id) ?? [] - ;(dbResponse.tool_resources.file_search as any).files = [...existingFiles.filter((file) => fileIds.includes(file.id))] - ;(dbResponse.tool_resources.file_search as any).vector_store_object = await openai.vectorStores.retrieve(vectorStoreId) - } - return dbResponse - } catch (error) { - throw new InternalFlowiseError( - StatusCodes.INTERNAL_SERVER_ERROR, - `Error: openaiAssistantsService.getSingleOpenaiAssistant - ${getErrorMessage(error)}` - ) - } -} - -const uploadFilesToAssistant = async (credentialId: string, files: { filePath: string; fileName: string }[]) => { - const appServer = getRunningExpressApp() - const credential = await appServer.AppDataSource.getRepository(Credential).findOneBy({ - id: credentialId - }) - if (!credential) { - throw new InternalFlowiseError(StatusCodes.NOT_FOUND, `Credential ${credentialId} not found in the database!`) - } - // Decrpyt credentialData - const decryptedCredentialData = await decryptCredentialData(credential.encryptedData) - const openAIApiKey = decryptedCredentialData['openAIApiKey'] - if (!openAIApiKey) { - throw new InternalFlowiseError(StatusCodes.NOT_FOUND, `OpenAI ApiKey not found`) - } - - const openai = new OpenAI({ apiKey: openAIApiKey }) - const uploadedFiles = [] - - for (const file of files) { - const toFile = await OpenAI.toFile(fs.readFileSync(file.filePath), file.fileName) - const createdFile = await openai.files.create({ - file: toFile, - purpose: 'assistants' - }) - uploadedFiles.push(createdFile) - fs.unlinkSync(file.filePath) - } - - return uploadedFiles -} - -export default { - getAllOpenaiAssistants, - getSingleOpenaiAssistant, - uploadFilesToAssistant -} diff --git a/studio-frontend/packages/server/src/services/predictions/index.ts b/studio-frontend/packages/server/src/services/predictions/index.ts deleted file mode 100644 index 6f2dbe1..0000000 --- a/studio-frontend/packages/server/src/services/predictions/index.ts +++ /dev/null @@ -1,21 +0,0 @@ -import { Request } from 'express' -import { StatusCodes } from 'http-status-codes' -import { utilBuildChatflow } from '../../utils/buildChatflow' -import { InternalFlowiseError } from '../../errors/internalFlowiseError' -import { getErrorMessage } from '../../errors/utils' - -const buildChatflow = async (fullRequest: Request) => { - try { - const dbResponse = await utilBuildChatflow(fullRequest) - return dbResponse - } catch (error) { - throw new InternalFlowiseError( - StatusCodes.INTERNAL_SERVER_ERROR, - `Error: predictionsServices.buildChatflow - ${getErrorMessage(error)}` - ) - } -} - -export default { - buildChatflow -} diff --git a/studio-frontend/packages/server/src/services/prompts-lists/index.ts b/studio-frontend/packages/server/src/services/prompts-lists/index.ts deleted file mode 100644 index 62a97c8..0000000 --- a/studio-frontend/packages/server/src/services/prompts-lists/index.ts +++ /dev/null @@ -1,22 +0,0 @@ -import axios from 'axios' - -const createPromptsList = async (requestBody: any) => { - try { - const tags = requestBody.tags ? `tags=${requestBody.tags}` : '' - // Default to 100, TODO: add pagination and use offset & limit - const url = `https://api.hub.langchain.com/repos/?limit=100&${tags}has_commits=true&sort_field=num_likes&sort_direction=desc&is_archived=false` - const resp = await axios.get(url) - if (resp.data.repos) { - return { - status: 'OK', - repos: resp.data.repos - } - } - } catch (error) { - return { status: 'ERROR', repos: [] } - } -} - -export default { - createPromptsList -} diff --git a/studio-frontend/packages/server/src/services/stats/index.ts b/studio-frontend/packages/server/src/services/stats/index.ts deleted file mode 100644 index 8d9b99d..0000000 --- a/studio-frontend/packages/server/src/services/stats/index.ts +++ /dev/null @@ -1,53 +0,0 @@ -import { StatusCodes } from 'http-status-codes' -import { ChatMessageRatingType, chatType } from '../../Interface' -import { ChatMessage } from '../../database/entities/ChatMessage' -import { utilGetChatMessage } from '../../utils/getChatMessage' -import { ChatMessageFeedback } from '../../database/entities/ChatMessageFeedback' -import { InternalFlowiseError } from '../../errors/internalFlowiseError' -import { getErrorMessage } from '../../errors/utils' - -// get stats for showing in chatflow -const getChatflowStats = async ( - chatflowid: string, - chatTypeFilter: chatType | undefined, - startDate?: string, - endDate?: string, - messageId?: string, - feedback?: boolean, - feedbackTypes?: ChatMessageRatingType[] -): Promise => { - try { - const chatmessages = (await utilGetChatMessage( - chatflowid, - chatTypeFilter, - undefined, - undefined, - undefined, - undefined, - startDate, - endDate, - messageId, - feedback, - feedbackTypes - )) as Array - const totalMessages = chatmessages.length - const totalFeedback = chatmessages.filter((message) => message?.feedback).length - const positiveFeedback = chatmessages.filter((message) => message?.feedback?.rating === 'THUMBS_UP').length - const dbResponse = { - totalMessages, - totalFeedback, - positiveFeedback - } - - return dbResponse - } catch (error) { - throw new InternalFlowiseError( - StatusCodes.INTERNAL_SERVER_ERROR, - `Error: statsService.getChatflowStats - ${getErrorMessage(error)}` - ) - } -} - -export default { - getChatflowStats -} diff --git a/studio-frontend/packages/server/src/services/telemetry/index.ts b/studio-frontend/packages/server/src/services/telemetry/index.ts deleted file mode 100644 index 4c77efd..0000000 --- a/studio-frontend/packages/server/src/services/telemetry/index.ts +++ /dev/null @@ -1,10 +0,0 @@ -import { getRunningExpressApp } from '../../utils/getRunningExpressApp' - -const createEvent = async (eventInfo: any) => { - const appServer = getRunningExpressApp() - await appServer.telemetry.sendTelemetry(eventInfo.name, eventInfo.data) -} - -export default { - createEvent -} diff --git a/studio-frontend/packages/server/src/services/tools/index.ts b/studio-frontend/packages/server/src/services/tools/index.ts index dcfdc94..e0393cf 100644 --- a/studio-frontend/packages/server/src/services/tools/index.ts +++ b/studio-frontend/packages/server/src/services/tools/index.ts @@ -2,7 +2,6 @@ import { StatusCodes } from 'http-status-codes' import { Tool } from '../../database/entities/Tool' import { InternalFlowiseError } from '../../errors/internalFlowiseError' import { getErrorMessage } from '../../errors/utils' -import { getAppVersion } from '../../utils' import { getRunningExpressApp } from '../../utils/getRunningExpressApp' const createTool = async (requestBody: any): Promise => { @@ -12,11 +11,6 @@ const createTool = async (requestBody: any): Promise => { Object.assign(newTool, requestBody) const tool = await appServer.AppDataSource.getRepository(Tool).create(newTool) const dbResponse = await appServer.AppDataSource.getRepository(Tool).save(tool) - await appServer.telemetry.sendTelemetry('tool_created', { - version: await getAppVersion(), - toolId: dbResponse.id, - toolName: dbResponse.name - }) return dbResponse } catch (error) { throw new InternalFlowiseError(StatusCodes.INTERNAL_SERVER_ERROR, `Error: toolsService.createTool - ${getErrorMessage(error)}`) diff --git a/studio-frontend/packages/server/src/services/upsert-history/index.ts b/studio-frontend/packages/server/src/services/upsert-history/index.ts deleted file mode 100644 index 72e484b..0000000 --- a/studio-frontend/packages/server/src/services/upsert-history/index.ts +++ /dev/null @@ -1,75 +0,0 @@ -import { MoreThanOrEqual, LessThanOrEqual } from 'typeorm' -import { StatusCodes } from 'http-status-codes' -import { getRunningExpressApp } from '../../utils/getRunningExpressApp' -import { UpsertHistory } from '../../database/entities/UpsertHistory' -import { InternalFlowiseError } from '../../errors/internalFlowiseError' -import { getErrorMessage } from '../../errors/utils' - -const getAllUpsertHistory = async ( - sortOrder: string | undefined, - chatflowid: string | undefined, - startDate: string | undefined, - endDate: string | undefined -) => { - try { - const appServer = getRunningExpressApp() - - const setDateToStartOrEndOfDay = (dateTimeStr: string, setHours: 'start' | 'end') => { - const date = new Date(dateTimeStr) - if (isNaN(date.getTime())) { - return undefined - } - setHours === 'start' ? date.setHours(0, 0, 0, 0) : date.setHours(23, 59, 59, 999) - return date - } - - let fromDate - if (startDate) fromDate = setDateToStartOrEndOfDay(startDate, 'start') - - let toDate - if (endDate) toDate = setDateToStartOrEndOfDay(endDate, 'end') - - let upsertHistory = await appServer.AppDataSource.getRepository(UpsertHistory).find({ - where: { - chatflowid, - ...(fromDate && { date: MoreThanOrEqual(fromDate) }), - ...(toDate && { date: LessThanOrEqual(toDate) }) - }, - order: { - date: sortOrder === 'DESC' ? 'DESC' : 'ASC' - } - }) - upsertHistory = upsertHistory.map((hist) => { - return { - ...hist, - result: hist.result ? JSON.parse(hist.result) : {}, - flowData: hist.flowData ? JSON.parse(hist.flowData) : {} - } - }) - - return upsertHistory - } catch (error) { - throw new InternalFlowiseError( - StatusCodes.INTERNAL_SERVER_ERROR, - `Error: upsertHistoryServices.getAllUpsertHistory - ${getErrorMessage(error)}` - ) - } -} - -const patchDeleteUpsertHistory = async (ids: string[] = []): Promise => { - try { - const appServer = getRunningExpressApp() - const dbResponse = await appServer.AppDataSource.getRepository(UpsertHistory).delete(ids) - return dbResponse - } catch (error) { - throw new InternalFlowiseError( - StatusCodes.INTERNAL_SERVER_ERROR, - `Error: upsertHistoryServices.patchDeleteUpsertHistory - ${getErrorMessage(error)}` - ) - } -} - -export default { - getAllUpsertHistory, - patchDeleteUpsertHistory -} diff --git a/studio-frontend/packages/server/src/services/vectors/index.ts b/studio-frontend/packages/server/src/services/vectors/index.ts deleted file mode 100644 index 15892f5..0000000 --- a/studio-frontend/packages/server/src/services/vectors/index.ts +++ /dev/null @@ -1,17 +0,0 @@ -import { Request } from 'express' -import { StatusCodes } from 'http-status-codes' -import { upsertVector } from '../../utils/upsertVector' -import { InternalFlowiseError } from '../../errors/internalFlowiseError' -import { getErrorMessage } from '../../errors/utils' - -const upsertVectorMiddleware = async (req: Request, isInternal: boolean = false) => { - try { - return await upsertVector(req, isInternal) - } catch (error) { - throw new InternalFlowiseError(StatusCodes.INTERNAL_SERVER_ERROR, `Error: vectorsService.upsertVector - ${getErrorMessage(error)}`) - } -} - -export default { - upsertVectorMiddleware -} diff --git a/studio-frontend/packages/server/src/utils/SSEStreamer.ts b/studio-frontend/packages/server/src/utils/SSEStreamer.ts deleted file mode 100644 index ef7139d..0000000 --- a/studio-frontend/packages/server/src/utils/SSEStreamer.ts +++ /dev/null @@ -1,325 +0,0 @@ -import express from 'express' -import { Response } from 'express' -import { IServerSideEventStreamer } from 'flowise-components' - -// define a new type that has a client type (INTERNAL or EXTERNAL) and Response type -type Client = { - // future use - clientType: 'INTERNAL' | 'EXTERNAL' - response: Response - // optional property with default value - started?: boolean -} - -export class SSEStreamer implements IServerSideEventStreamer { - clients: { [id: string]: Client } = {} - app: express.Application - - constructor(app: express.Application) { - this.app = app - } - - addExternalClient(chatId: string, res: Response) { - this.clients[chatId] = { clientType: 'EXTERNAL', response: res, started: false } - } - - addClient(chatId: string, res: Response) { - this.clients[chatId] = { clientType: 'INTERNAL', response: res, started: false } - } - - removeClient(chatId: string) { - const client = this.clients[chatId] - if (client) { - const clientResponse = { - event: 'end', - data: '[DONE]' - } - client.response.write('message\ndata:' + JSON.stringify(clientResponse) + '\n\n') - client.response.end() - delete this.clients[chatId] - } - } - - // Send SSE message to a specific client - streamEvent(chatId: string, data: string) { - const client = this.clients[chatId] - if (client) { - const clientResponse = { - event: 'start', - data: data - } - client.response.write('message:\ndata:' + JSON.stringify(clientResponse) + '\n\n') - } - } - - streamCustomEvent(chatId: string, eventType: string, data: any) { - const client = this.clients[chatId] - if (client) { - const clientResponse = { - event: eventType, - data: data - } - client.response.write('message:\ndata:' + JSON.stringify(clientResponse) + '\n\n') - } - } - - streamStartEvent(chatId: string, data: string) { - const client = this.clients[chatId] - // prevent multiple start events being streamed to the client - if (client && !client.started) { - const clientResponse = { - event: 'start', - data: data - } - client.response.write('message:\ndata:' + JSON.stringify(clientResponse) + '\n\n') - client.started = true - } - } - - streamTokenEvent(chatId: string, data: string) { - const client = this.clients[chatId] - if (client) { - const clientResponse = { - event: 'token', - data: data - } - client.response.write('message:\ndata:' + JSON.stringify(clientResponse) + '\n\n') - } - } - - streamSourceDocumentsEvent(chatId: string, data: any) { - const client = this.clients[chatId] - if (client) { - const clientResponse = { - event: 'sourceDocuments', - data: data - } - client.response.write('message:\ndata:' + JSON.stringify(clientResponse) + '\n\n') - } - } - streamArtifactsEvent(chatId: string, data: any) { - const client = this.clients[chatId] - if (client) { - const clientResponse = { - event: 'artifacts', - data: data - } - client.response.write('message:\ndata:' + JSON.stringify(clientResponse) + '\n\n') - } - } - streamUsedToolsEvent(chatId: string, data: any): void { - const client = this.clients[chatId] - if (client) { - const clientResponse = { - event: 'usedTools', - data: data - } - client.response.write('message:\ndata:' + JSON.stringify(clientResponse) + '\n\n') - } - } - streamFileAnnotationsEvent(chatId: string, data: any): void { - const client = this.clients[chatId] - if (client) { - const clientResponse = { - event: 'fileAnnotations', - data: data - } - client.response.write('message:\ndata:' + JSON.stringify(clientResponse) + '\n\n') - } - } - streamToolEvent(chatId: string, data: any): void { - const client = this.clients[chatId] - if (client) { - const clientResponse = { - event: 'tool', - data: data - } - client.response.write('message:\ndata:' + JSON.stringify(clientResponse) + '\n\n') - } - } - streamAgentReasoningEvent(chatId: string, data: any): void { - const client = this.clients[chatId] - if (client) { - const clientResponse = { - event: 'agentReasoning', - data: data - } - client.response.write('message:\ndata:' + JSON.stringify(clientResponse) + '\n\n') - } - } - streamNextAgentEvent(chatId: string, data: any): void { - const client = this.clients[chatId] - if (client) { - const clientResponse = { - event: 'nextAgent', - data: data - } - client.response.write('message:\ndata:' + JSON.stringify(clientResponse) + '\n\n') - } - } - streamActionEvent(chatId: string, data: any): void { - const client = this.clients[chatId] - if (client) { - const clientResponse = { - event: 'action', - data: data - } - client.response.write('message:\ndata:' + JSON.stringify(clientResponse) + '\n\n') - } - } - - streamAbortEvent(chatId: string): void { - const client = this.clients[chatId] - if (client) { - const clientResponse = { - event: 'abort', - data: '[DONE]' - } - client.response.write('message\ndata:' + JSON.stringify(clientResponse) + '\n\n') - } - } - - streamEndEvent(_: string) { - // placeholder for future use - } - - streamErrorEvent(chatId: string, msg: string) { - const client = this.clients[chatId] - if (client) { - const clientResponse = { - event: 'error', - data: msg - } - client.response.write('message\ndata:' + JSON.stringify(clientResponse) + '\n\n') - } - } - - streamMetadataEvent(chatId: string, apiResponse: any) { - const metadataJson: any = {} - if (apiResponse.chatId) { - metadataJson['chatId'] = apiResponse.chatId - } - if (apiResponse.chatMessageId) { - metadataJson['chatMessageId'] = apiResponse.chatMessageId - } - if (apiResponse.question) { - metadataJson['question'] = apiResponse.question - } - if (apiResponse.sessionId) { - metadataJson['sessionId'] = apiResponse.sessionId - } - if (apiResponse.memoryType) { - metadataJson['memoryType'] = apiResponse.memoryType - } - if (apiResponse.followUpPrompts) { - metadataJson['followUpPrompts'] = - typeof apiResponse.followUpPrompts === 'string' ? JSON.parse(apiResponse.followUpPrompts) : apiResponse.followUpPrompts - } - if (apiResponse.flowVariables) { - metadataJson['flowVariables'] = - typeof apiResponse.flowVariables === 'string' ? JSON.parse(apiResponse.flowVariables) : apiResponse.flowVariables - } - if (Object.keys(metadataJson).length > 0) { - this.streamCustomEvent(chatId, 'metadata', metadataJson) - } - } - - streamCalledToolsEvent(chatId: string, data: any): void { - const client = this.clients[chatId] - if (client) { - const clientResponse = { - event: 'calledTools', - data: data - } - client.response.write('message:\ndata:' + JSON.stringify(clientResponse) + '\n\n') - } - } - - streamAgentFlowExecutedDataEvent(chatId: string, data: any): void { - const client = this.clients[chatId] - if (client) { - const clientResponse = { - event: 'agentFlowExecutedData', - data: data - } - client.response.write('message:\ndata:' + JSON.stringify(clientResponse) + '\n\n') - } - } - - streamAgentFlowEvent(chatId: string, data: any): void { - const client = this.clients[chatId] - if (client) { - const clientResponse = { - event: 'agentFlowEvent', - data: data - } - client.response.write('message:\ndata:' + JSON.stringify(clientResponse) + '\n\n') - } - } - - streamNextAgentFlowEvent(chatId: string, data: any): void { - const client = this.clients[chatId] - if (client) { - const clientResponse = { - event: 'nextAgentFlow', - data: data - } - client.response.write('message:\ndata:' + JSON.stringify(clientResponse) + '\n\n') - } - } - - streamUsageMetadataEvent(chatId: string, data: any): void { - const client = this.clients[chatId] - if (client) { - const clientResponse = { - event: 'usageMetadata', - data: data - } - client.response.write('message:\ndata:' + JSON.stringify(clientResponse) + '\n\n') - } - } - - streamTTSStartEvent(chatId: string, chatMessageId: string, format: string): void { - const client = this.clients[chatId] - if (client) { - const clientResponse = { - event: 'tts_start', - data: { chatMessageId, format } - } - client.response.write('message:\ndata:' + JSON.stringify(clientResponse) + '\n\n') - } - } - - streamTTSDataEvent(chatId: string, chatMessageId: string, audioChunk: string): void { - const client = this.clients[chatId] - if (client) { - const clientResponse = { - event: 'tts_data', - data: { chatMessageId, audioChunk } - } - client.response.write('message:\ndata:' + JSON.stringify(clientResponse) + '\n\n') - } - } - - streamTTSEndEvent(chatId: string, chatMessageId: string): void { - const client = this.clients[chatId] - if (client) { - const clientResponse = { - event: 'tts_end', - data: { chatMessageId } - } - client.response.write('message:\ndata:' + JSON.stringify(clientResponse) + '\n\n') - } - } - - streamTTSAbortEvent(chatId: string, chatMessageId: string): void { - const client = this.clients[chatId] - if (client) { - const clientResponse = { - event: 'tts_abort', - data: { chatMessageId } - } - client.response.write('message:\ndata:' + JSON.stringify(clientResponse) + '\n\n') - } - } -} diff --git a/studio-frontend/packages/server/src/utils/apiKey.ts b/studio-frontend/packages/server/src/utils/apiKey.ts index a50b2b5..43e2d95 100644 --- a/studio-frontend/packages/server/src/utils/apiKey.ts +++ b/studio-frontend/packages/server/src/utils/apiKey.ts @@ -1,5 +1,5 @@ import { randomBytes, scryptSync, timingSafeEqual } from 'crypto' -import { ICommonObject } from 'flowise-components' +import { ICommonObject } from '../Interface' import moment from 'moment' import fs from 'fs' import path from 'path' diff --git a/studio-frontend/packages/server/src/utils/buildAgentGraph.ts b/studio-frontend/packages/server/src/utils/buildAgentGraph.ts deleted file mode 100644 index fd9a198..0000000 --- a/studio-frontend/packages/server/src/utils/buildAgentGraph.ts +++ /dev/null @@ -1,1046 +0,0 @@ -import { - ICommonObject, - IMultiAgentNode, - IAgentReasoning, - IAction, - ITeamState, - ConsoleCallbackHandler, - additionalCallbacks, - ISeqAgentsState, - ISeqAgentNode, - IUsedTool, - IDocument, - IServerSideEventStreamer -} from 'flowise-components' -import { omit, cloneDeep, flatten, uniq } from 'lodash' -import { StateGraph, END, START } from '@langchain/langgraph' -import { Document } from '@langchain/core/documents' -import { StatusCodes } from 'http-status-codes' -import { v4 as uuidv4 } from 'uuid' -import { StructuredTool } from '@langchain/core/tools' -import { BaseMessage, HumanMessage, AIMessage, AIMessageChunk, ToolMessage } from '@langchain/core/messages' -import { - IChatFlow, - IComponentNodes, - IDepthQueue, - IReactFlowNode, - IReactFlowObject, - IReactFlowEdge, - IMessage, - IncomingInput -} from '../Interface' -import { - buildFlow, - getStartingNodes, - getEndingNodes, - constructGraphs, - databaseEntities, - getSessionChatHistory, - getMemorySessionId, - clearSessionMemory -} from '../utils' -import { getRunningExpressApp } from './getRunningExpressApp' -import { replaceInputsWithConfig, resolveVariables } from '.' -import { InternalFlowiseError } from '../errors/internalFlowiseError' -import { getErrorMessage } from '../errors/utils' -import logger from './logger' - -/** - * Build Agent Graph - * @param {IChatFlow} chatflow - * @param {string} chatId - * @param {string} sessionId - * @param {ICommonObject} incomingInput - * @param {boolean} isInternal - * @param {string} baseURL - */ -export const buildAgentGraph = async ( - chatflow: IChatFlow, - chatId: string, - sessionId: string, - incomingInput: IncomingInput, - isInternal: boolean, - baseURL?: string, - sseStreamer?: IServerSideEventStreamer, - shouldStreamResponse?: boolean -): Promise => { - try { - const appServer = getRunningExpressApp() - const chatflowid = chatflow.id - - /*** Get chatflows and prepare data ***/ - const flowData = chatflow.flowData - const parsedFlowData: IReactFlowObject = JSON.parse(flowData) - const nodes = parsedFlowData.nodes - const edges = parsedFlowData.edges - - /*** Get Ending Node with Directed Graph ***/ - const { graph, nodeDependencies } = constructGraphs(nodes, edges) - const directedGraph = graph - - const endingNodes = getEndingNodes(nodeDependencies, directedGraph, nodes) - - /*** Get Starting Nodes with Reversed Graph ***/ - const constructedObj = constructGraphs(nodes, edges, { isReversed: true }) - const nonDirectedGraph = constructedObj.graph - let startingNodeIds: string[] = [] - let depthQueue: IDepthQueue = {} - const endingNodeIds = endingNodes.map((n) => n.id) - for (const endingNodeId of endingNodeIds) { - const resx = getStartingNodes(nonDirectedGraph, endingNodeId) - startingNodeIds.push(...resx.startingNodeIds) - depthQueue = Object.assign(depthQueue, resx.depthQueue) - } - startingNodeIds = [...new Set(startingNodeIds)] - - /*** Get Memory Node for Chat History ***/ - let chatHistory: IMessage[] = [] - const memoryNode = nodes.find((node) => node.data.name === 'agentMemory') - if (memoryNode) { - chatHistory = await getSessionChatHistory( - chatflowid, - getMemorySessionId(memoryNode, incomingInput, chatId, isInternal), - memoryNode, - appServer.nodesPool.componentNodes, - appServer.AppDataSource, - databaseEntities, - logger, - incomingInput.history - ) - } - - // Initialize nodes like ChatModels, Tools, etc. - const reactFlowNodes: IReactFlowNode[] = await buildFlow({ - startingNodeIds, - reactFlowNodes: nodes, - reactFlowEdges: edges, - graph, - depthQueue, - componentNodes: appServer.nodesPool.componentNodes, - question: incomingInput.question, - chatHistory, - chatId, - sessionId, - chatflowid, - appDataSource: appServer.AppDataSource, - overrideConfig: incomingInput?.overrideConfig, - cachePool: appServer.cachePool, - isUpsert: false, - uploads: incomingInput.uploads, - baseURL - }) - - const options = { - chatId, - sessionId, - chatflowid, - logger, - analytic: chatflow.analytic, - appDataSource: appServer.AppDataSource, - databaseEntities: databaseEntities, - cachePool: appServer.cachePool, - uploads: incomingInput.uploads, - baseURL, - signal: new AbortController() - } - - let streamResults - let finalResult = '' - let finalSummarization = '' - let lastWorkerResult = '' - let agentReasoning: IAgentReasoning[] = [] - let isSequential = false - let lastMessageRaw = {} as AIMessageChunk - let finalAction: IAction = {} - let totalSourceDocuments: IDocument[] = [] - let totalUsedTools: IUsedTool[] = [] - let totalArtifacts: ICommonObject[] = [] - - const workerNodes = reactFlowNodes.filter((node) => node.data.name === 'worker') - const supervisorNodes = reactFlowNodes.filter((node) => node.data.name === 'supervisor') - const seqAgentNodes = reactFlowNodes.filter((node) => node.data.category === 'Sequential Agents') - - const mapNameToLabel: Record = {} - - for (const node of [...workerNodes, ...supervisorNodes, ...seqAgentNodes]) { - if (!Object.prototype.hasOwnProperty.call(mapNameToLabel, node.data.instance.name)) { - mapNameToLabel[node.data.instance.name] = { - label: node.data.instance.label, - nodeName: node.data.name - } - } - } - - try { - if (!seqAgentNodes.length) { - streamResults = await compileMultiAgentsGraph( - chatflow, - mapNameToLabel, - reactFlowNodes, - endingNodeIds, - appServer.nodesPool.componentNodes, - options, - startingNodeIds, - incomingInput.question, - incomingInput.history, - chatHistory, - incomingInput?.overrideConfig, - sessionId || chatId, - seqAgentNodes.some((node) => node.data.inputs?.summarization) - ) - } else { - isSequential = true - streamResults = await compileSeqAgentsGraph( - depthQueue, - chatflow, - reactFlowNodes, - edges, - appServer.nodesPool.componentNodes, - options, - incomingInput.question, - incomingInput.history, - chatHistory, - incomingInput?.overrideConfig, - sessionId || chatId, - incomingInput.action - ) - } - - if (streamResults) { - let isStreamingStarted = false - for await (const output of await streamResults) { - if (!output?.__end__) { - for (const agentName of Object.keys(output)) { - if (!mapNameToLabel[agentName]) continue - - const nodeId = output[agentName]?.messages - ? output[agentName].messages[output[agentName].messages.length - 1]?.additional_kwargs?.nodeId - : '' - const usedTools = output[agentName]?.messages - ? output[agentName].messages.map((msg: BaseMessage) => msg.additional_kwargs?.usedTools) - : [] - const sourceDocuments = output[agentName]?.messages - ? output[agentName].messages.map((msg: BaseMessage) => msg.additional_kwargs?.sourceDocuments) - : [] - const artifacts = output[agentName]?.messages - ? output[agentName].messages.map((msg: BaseMessage) => msg.additional_kwargs?.artifacts) - : [] - const messages = output[agentName]?.messages - ? output[agentName].messages.map((msg: BaseMessage) => (typeof msg === 'string' ? msg : msg.content)) - : [] - lastMessageRaw = output[agentName]?.messages - ? output[agentName].messages[output[agentName].messages.length - 1] - : {} - - const state = omit(output[agentName], ['messages']) - - if (usedTools && usedTools.length) { - const cleanedTools = usedTools.filter((tool: IUsedTool) => tool) - if (cleanedTools.length) totalUsedTools.push(...cleanedTools) - } - - if (sourceDocuments && sourceDocuments.length) { - const cleanedDocs = sourceDocuments.filter((documents: IDocument) => documents) - if (cleanedDocs.length) totalSourceDocuments.push(...cleanedDocs) - } - - if (artifacts && artifacts.length) { - const cleanedArtifacts = artifacts.filter((artifact: ICommonObject) => artifact) - if (cleanedArtifacts.length) totalArtifacts.push(...cleanedArtifacts) - } - - /* - * Check if the next node is a condition node, if yes, then add the agent reasoning of the condition node - */ - if (isSequential) { - const inputEdges = edges.filter( - (edg) => edg.target === nodeId && edg.targetHandle.includes(`${nodeId}-input-sequentialNode`) - ) - - inputEdges.forEach((edge) => { - const parentNode = reactFlowNodes.find((nd) => nd.id === edge.source) - if (parentNode) { - if (parentNode.data.name.includes('seqCondition')) { - const newMessages = messages.slice(0, -1) - newMessages.push(mapNameToLabel[agentName].label) - const reasoning = { - agentName: parentNode.data.instance?.label || parentNode.data.type, - messages: newMessages, - nodeName: parentNode.data.name, - nodeId: parentNode.data.id - } - agentReasoning.push(reasoning) - } - } - }) - } - - const reasoning = { - agentName: mapNameToLabel[agentName].label, - messages, - next: output[agentName]?.next, - instructions: output[agentName]?.instructions, - usedTools: flatten(usedTools) as IUsedTool[], - sourceDocuments: flatten(sourceDocuments) as Document[], - artifacts: flatten(artifacts) as ICommonObject[], - state, - nodeName: isSequential ? mapNameToLabel[agentName].nodeName : undefined, - nodeId - } - agentReasoning.push(reasoning) - - finalSummarization = output[agentName]?.summarization ?? '' - - lastWorkerResult = - output[agentName]?.messages?.length && - output[agentName].messages[output[agentName].messages.length - 1]?.additional_kwargs?.type === 'worker' - ? output[agentName].messages[output[agentName].messages.length - 1].content - : lastWorkerResult - - if (shouldStreamResponse) { - if (!isStreamingStarted) { - isStreamingStarted = true - if (sseStreamer) { - sseStreamer.streamStartEvent(chatId, agentReasoning) - } - } - - if (sseStreamer) { - sseStreamer.streamAgentReasoningEvent(chatId, agentReasoning) - } - - // Send loading next agent indicator - if (reasoning.next && reasoning.next !== 'FINISH' && reasoning.next !== 'END') { - if (sseStreamer) { - sseStreamer.streamNextAgentEvent(chatId, mapNameToLabel[reasoning.next].label || reasoning.next) - } - } - } - } - } else { - finalResult = output.__end__.messages.length ? output.__end__.messages.pop()?.content : '' - if (Array.isArray(finalResult)) finalResult = output.__end__.instructions - if (shouldStreamResponse && sseStreamer) { - sseStreamer.streamTokenEvent(chatId, finalResult) - } - } - } - - /* - * For multi agents mode, sometimes finalResult is empty - * 1.) Provide lastWorkerResult as final result if available - * 2.) Provide summary as final result if available - */ - if (!isSequential && !finalResult) { - if (lastWorkerResult) finalResult = lastWorkerResult - else if (finalSummarization) finalResult = finalSummarization - if (shouldStreamResponse && sseStreamer) { - sseStreamer.streamTokenEvent(chatId, finalResult) - } - } - - /* - * For sequential mode, sometimes finalResult is empty - * Use last agent message as final result - */ - if (isSequential && !finalResult && agentReasoning.length) { - const lastMessages = agentReasoning[agentReasoning.length - 1].messages - const lastAgentReasoningMessage = lastMessages[lastMessages.length - 1] - - // If last message is an AI Message with tool calls, that means the last node was interrupted - if (lastMessageRaw.tool_calls && lastMessageRaw.tool_calls.length > 0) { - // The last node that got interrupted - const node = reactFlowNodes.find((node) => node.id === lastMessageRaw.additional_kwargs.nodeId) - - // Find the next tool node that is connected to the interrupted node, to get the approve/reject button text - const tooNodeId = edges.find( - (edge) => - edge.target.includes('seqToolNode') && - edge.source === (lastMessageRaw.additional_kwargs && lastMessageRaw.additional_kwargs.nodeId) - )?.target - const connectedToolNode = reactFlowNodes.find((node) => node.id === tooNodeId) - - // Map raw tool calls to used tools, to be shown on interrupted message - const mappedToolCalls = lastMessageRaw.tool_calls.map((toolCall: any) => { - return { tool: toolCall.name, toolInput: toolCall.args, toolOutput: '' } - }) - - // Emit the interrupt message to the client - let approveButtonText = 'Yes' - let rejectButtonText = 'No' - - if (connectedToolNode || node) { - if (connectedToolNode) { - const result = await connectedToolNode.data.instance.node.seekPermissionMessage(mappedToolCalls) - finalResult = result || 'Do you want to proceed?' - approveButtonText = connectedToolNode.data.inputs?.approveButtonText || 'Yes' - rejectButtonText = connectedToolNode.data.inputs?.rejectButtonText || 'No' - } else if (node) { - const result = await node.data.instance.agentInterruptToolNode.seekPermissionMessage(mappedToolCalls) - finalResult = result || 'Do you want to proceed?' - approveButtonText = node.data.inputs?.approveButtonText || 'Yes' - rejectButtonText = node.data.inputs?.rejectButtonText || 'No' - } - finalAction = { - id: uuidv4(), - mapping: { approve: approveButtonText, reject: rejectButtonText, toolCalls: lastMessageRaw.tool_calls }, - elements: [ - { type: 'approve-button', label: approveButtonText }, - { type: 'reject-button', label: rejectButtonText } - ] - } - if (shouldStreamResponse && sseStreamer) { - sseStreamer.streamTokenEvent(chatId, finalResult) - sseStreamer.streamActionEvent(chatId, finalAction) - } - } - totalUsedTools.push(...mappedToolCalls) - } else if (lastAgentReasoningMessage) { - finalResult = lastAgentReasoningMessage - if (shouldStreamResponse && sseStreamer) { - sseStreamer.streamTokenEvent(chatId, finalResult) - } - } - } - - totalSourceDocuments = uniq(flatten(totalSourceDocuments)) - totalUsedTools = uniq(flatten(totalUsedTools)) - totalArtifacts = uniq(flatten(totalArtifacts)) - - if (shouldStreamResponse && sseStreamer) { - sseStreamer.streamUsedToolsEvent(chatId, totalUsedTools) - sseStreamer.streamSourceDocumentsEvent(chatId, totalSourceDocuments) - sseStreamer.streamArtifactsEvent(chatId, totalArtifacts) - sseStreamer.streamEndEvent(chatId) - } - - return { - finalResult, - finalAction, - sourceDocuments: totalSourceDocuments, - artifacts: totalArtifacts, - usedTools: totalUsedTools, - agentReasoning - } - } - } catch (e) { - // clear agent memory because checkpoints were saved during runtime - await clearSessionMemory(nodes, appServer.nodesPool.componentNodes, chatId, appServer.AppDataSource, sessionId) - if (getErrorMessage(e).includes('Aborted')) { - if (shouldStreamResponse && sseStreamer) { - sseStreamer.streamAbortEvent(chatId) - } - return { finalResult, agentReasoning } - } - throw new Error(getErrorMessage(e)) - } - return streamResults - } catch (e) { - logger.error('[server]: Error:', e) - throw new InternalFlowiseError(StatusCodes.INTERNAL_SERVER_ERROR, `Error buildAgentGraph - ${getErrorMessage(e)}`) - } -} - -/** - * Compile Multi Agents Graph - * @param {IChatFlow} chatflow - * @param {Record} mapNameToLabel - * @param {IReactFlowNode[]} reactflowNodes - * @param {string[]} workerNodeIds - * @param {IComponentNodes} componentNodes - * @param {ICommonObject} options - * @param {string[]} startingNodeIds - * @param {string} question - * @param {ICommonObject} overrideConfig - * @param {string} threadId - * @param {boolean} summarization - */ -const compileMultiAgentsGraph = async ( - chatflow: IChatFlow, - mapNameToLabel: Record, - reactflowNodes: IReactFlowNode[] = [], - workerNodeIds: string[], - componentNodes: IComponentNodes, - options: ICommonObject, - startingNodeIds: string[], - question: string, - prependHistoryMessages: IMessage[] = [], - chatHistory: IMessage[] = [], - overrideConfig?: ICommonObject, - threadId?: string, - summarization?: boolean -) => { - const appServer = getRunningExpressApp() - const channels: ITeamState = { - messages: { - value: (x: BaseMessage[], y: BaseMessage[]) => x.concat(y), - default: () => [] - }, - next: 'initialState', - instructions: "Solve the user's request.", - team_members: [] - } - - if (summarization) channels.summarization = 'summarize' - - const workflowGraph = new StateGraph({ - //@ts-ignore - channels - }) - - const workerNodes = reactflowNodes.filter((node) => workerNodeIds.includes(node.data.id)) - - let supervisorWorkers: { [key: string]: IMultiAgentNode[] } = {} - - // Init worker nodes - for (const workerNode of workerNodes) { - const nodeInstanceFilePath = componentNodes[workerNode.data.name].filePath as string - const nodeModule = await import(nodeInstanceFilePath) - const newNodeInstance = new nodeModule.nodeClass() - - let flowNodeData = cloneDeep(workerNode.data) - if (overrideConfig) flowNodeData = replaceInputsWithConfig(flowNodeData, overrideConfig) - flowNodeData = await resolveVariables(appServer.AppDataSource, flowNodeData, reactflowNodes, question, chatHistory, overrideConfig) - - try { - const workerResult: IMultiAgentNode = await newNodeInstance.init(flowNodeData, question, options) - const parentSupervisor = workerResult.parentSupervisorName - if (!parentSupervisor || workerResult.type !== 'worker') continue - if (Object.prototype.hasOwnProperty.call(supervisorWorkers, parentSupervisor)) { - supervisorWorkers[parentSupervisor].push(workerResult) - } else { - supervisorWorkers[parentSupervisor] = [workerResult] - } - - workflowGraph.addNode(workerResult.name, workerResult.node) - } catch (e) { - throw new InternalFlowiseError(StatusCodes.INTERNAL_SERVER_ERROR, `Error initialize worker nodes - ${getErrorMessage(e)}`) - } - } - - // Init supervisor nodes - for (const supervisor in supervisorWorkers) { - const supervisorInputLabel = mapNameToLabel[supervisor].label - const supervisorNode = reactflowNodes.find((node) => supervisorInputLabel === node.data.inputs?.supervisorName) - if (!supervisorNode) continue - - const nodeInstanceFilePath = componentNodes[supervisorNode.data.name].filePath as string - const nodeModule = await import(nodeInstanceFilePath) - const newNodeInstance = new nodeModule.nodeClass() - - let flowNodeData = cloneDeep(supervisorNode.data) - - if (overrideConfig) flowNodeData = replaceInputsWithConfig(flowNodeData, overrideConfig) - flowNodeData = await resolveVariables(appServer.AppDataSource, flowNodeData, reactflowNodes, question, chatHistory, overrideConfig) - - if (flowNodeData.inputs) flowNodeData.inputs.workerNodes = supervisorWorkers[supervisor] - - try { - const supervisorResult: IMultiAgentNode = await newNodeInstance.init(flowNodeData, question, options) - if (!supervisorResult.workers?.length) continue - - if (supervisorResult.moderations && supervisorResult.moderations.length > 0) { - try { - for (const moderation of supervisorResult.moderations) { - question = await moderation.checkForViolations(question) - } - } catch (e) { - throw new InternalFlowiseError(StatusCodes.INTERNAL_SERVER_ERROR, getErrorMessage(e)) - } - } - - workflowGraph.addNode(supervisorResult.name, supervisorResult.node) - - for (const worker of supervisorResult.workers) { - //@ts-ignore - workflowGraph.addEdge(worker, supervisorResult.name) - } - - let conditionalEdges: { [key: string]: string } = {} - for (let i = 0; i < supervisorResult.workers.length; i++) { - conditionalEdges[supervisorResult.workers[i]] = supervisorResult.workers[i] - } - - //@ts-ignore - workflowGraph.addConditionalEdges(supervisorResult.name, (x: ITeamState) => x.next, { - ...conditionalEdges, - FINISH: END - }) - - //@ts-ignore - workflowGraph.addEdge(START, supervisorResult.name) - - // Add agentflow to pool - ;(workflowGraph as any).signal = options.signal - appServer.chatflowPool.add( - `${chatflow.id}_${options.chatId}`, - workflowGraph as any, - reactflowNodes.filter((node) => startingNodeIds.includes(node.id)), - overrideConfig - ) - - // Get memory - let memory = supervisorResult?.checkpointMemory - - const graph = workflowGraph.compile({ checkpointer: memory }) - - const loggerHandler = new ConsoleCallbackHandler(logger) - const callbacks = await additionalCallbacks(flowNodeData, options) - const config = { configurable: { thread_id: threadId } } - - let prependMessages = [] - // Only append in the first message - if (prependHistoryMessages.length === chatHistory.length) { - for (const message of prependHistoryMessages) { - if (message.role === 'apiMessage' || message.type === 'apiMessage') { - prependMessages.push(new AIMessage({ content: message.message || message.content || '' })) - } else if (message.role === 'userMessage' || message.type === 'userMessage') { - prependMessages.push(new HumanMessage({ content: message.message || message.content || '' })) - } - } - } - - // Return stream result as we should only have 1 supervisor - return await graph.stream( - { - messages: [...prependMessages, new HumanMessage({ content: question })] - }, - { recursionLimit: supervisorResult?.recursionLimit ?? 100, callbacks: [loggerHandler, ...callbacks], configurable: config } - ) - } catch (e) { - throw new InternalFlowiseError(StatusCodes.INTERNAL_SERVER_ERROR, `Error initialize supervisor nodes - ${getErrorMessage(e)}`) - } - } -} - -/** - * Compile Seq Agents Graph - * @param {IDepthQueue} depthQueue - * @param {IChatFlow} chatflow - * @param {IReactFlowNode[]} reactflowNodes - * @param {IReactFlowEdge[]} reactflowEdges - * @param {IComponentNodes} componentNodes - * @param {ICommonObject} options - * @param {string} question - * @param {IMessage[]} chatHistory - * @param {ICommonObject} overrideConfig - * @param {string} threadId - * @param {IAction} action - */ -const compileSeqAgentsGraph = async ( - depthQueue: IDepthQueue, - chatflow: IChatFlow, - reactflowNodes: IReactFlowNode[] = [], - reactflowEdges: IReactFlowEdge[] = [], - componentNodes: IComponentNodes, - options: ICommonObject, - question: string, - prependHistoryMessages: IMessage[] = [], - chatHistory: IMessage[] = [], - overrideConfig?: ICommonObject, - threadId?: string, - action?: IAction -) => { - const appServer = getRunningExpressApp() - - let channels: ISeqAgentsState = { - messages: { - value: (x: BaseMessage[], y: BaseMessage[]) => x.concat(y), - default: () => [] - } - } - - // Get state - const seqStateNode = reactflowNodes.find((node: IReactFlowNode) => node.data.name === 'seqState') - if (seqStateNode) { - channels = { - ...seqStateNode.data.instance.node, - ...channels - } - } - - let seqGraph = new StateGraph({ - //@ts-ignore - channels - }) - - /*** Validate Graph ***/ - const startAgentNodes: IReactFlowNode[] = reactflowNodes.filter((node: IReactFlowNode) => node.data.name === 'seqStart') - if (!startAgentNodes.length) throw new InternalFlowiseError(StatusCodes.INTERNAL_SERVER_ERROR, 'Start node not found') - if (startAgentNodes.length > 1) - throw new InternalFlowiseError(StatusCodes.INTERNAL_SERVER_ERROR, 'Graph should have only one start node') - - const endAgentNodes: IReactFlowNode[] = reactflowNodes.filter((node: IReactFlowNode) => node.data.name === 'seqEnd') - const loopNodes: IReactFlowNode[] = reactflowNodes.filter((node: IReactFlowNode) => node.data.name === 'seqLoop') - if (!endAgentNodes.length && !loopNodes.length) { - throw new InternalFlowiseError(StatusCodes.INTERNAL_SERVER_ERROR, 'Graph should have at least one End/Loop node') - } - /*** End of Validation ***/ - - let flowNodeData - let conditionalEdges: Record; func: any }> = {} - let interruptedRouteMapping: Record> = {} - let conditionalToolNodes: Record = {} - let bindModel: Record = {} - let interruptToolNodeNames = [] - - const initiateNode = async (node: IReactFlowNode) => { - const nodeInstanceFilePath = componentNodes[node.data.name].filePath as string - const nodeModule = await import(nodeInstanceFilePath) - const newNodeInstance = new nodeModule.nodeClass() - - flowNodeData = cloneDeep(node.data) - if (overrideConfig) flowNodeData = replaceInputsWithConfig(flowNodeData, overrideConfig) - flowNodeData = await resolveVariables(appServer.AppDataSource, flowNodeData, reactflowNodes, question, chatHistory, overrideConfig) - - const seqAgentNode: ISeqAgentNode = await newNodeInstance.init(flowNodeData, question, options) - return seqAgentNode - } - - /* - * Two objectives we want to achieve here: - * 1.) Prepare the mapping of conditional outputs to next nodes. This mapping will ONLY be used to add conditional edges to the Interrupted Agent connected next to Condition/ConditionAgent Node. - * For example, if the condition node has 2 outputs 'Yes' and 'No', and 'Yes' leads to 'agentName1' and 'No' leads to 'agentName2', then the mapping should be like: - * { - * : { 'Yes': 'agentName1', 'No': 'agentName2' } - * } - * 2.) With the interruptedRouteMapping object, avoid adding conditional edges to the Interrupted Agent for the nodes that are already interrupted by tools. It will be separately added from the function - agentInterruptToolFunc - */ - const processInterruptedRouteMapping = (conditionNodeId: string) => { - const conditionEdges = reactflowEdges.filter((edge) => edge.source === conditionNodeId) ?? [] - - for (const conditionEdge of conditionEdges) { - const nextNodeId = conditionEdge.target - const conditionNodeOutputAnchorId = conditionEdge.sourceHandle - - const nextNode = reactflowNodes.find((node) => node.id === nextNodeId) - if (!nextNode) continue - - const conditionNode = reactflowNodes.find((node) => node.id === conditionNodeId) - if (!conditionNode) continue - - const outputAnchors = conditionNode?.data.outputAnchors - if (!outputAnchors || !outputAnchors.length || !outputAnchors[0].options) continue - - const conditionOutputAnchorLabel = - outputAnchors[0].options.find((option: any) => option.id === conditionNodeOutputAnchorId)?.label ?? '' - if (!conditionOutputAnchorLabel) continue - - if (Object.prototype.hasOwnProperty.call(interruptedRouteMapping, conditionNodeId)) { - interruptedRouteMapping[conditionNodeId] = { - ...interruptedRouteMapping[conditionNodeId], - [conditionOutputAnchorLabel]: nextNode.data.instance.name - } - } else { - interruptedRouteMapping[conditionNodeId] = { - [conditionOutputAnchorLabel]: nextNode.data.instance.name - } - } - } - } - - /* - * Prepare Conditional Edges - * Example: { - * 'seqCondition_1': { nodes: { 'Yes': 'agentName1', 'No': 'agentName2' }, func: , disabled: true }, - * 'seqCondition_2': { nodes: { 'Yes': 'agentName3', 'No': 'agentName4' }, func: } - * } - */ - const prepareConditionalEdges = (nodeId: string, nodeInstance: ISeqAgentNode) => { - const conditionEdges = reactflowEdges.filter((edge) => edge.target === nodeId && edge.source.includes('seqCondition')) ?? [] - - for (const conditionEdge of conditionEdges) { - const conditionNodeId = conditionEdge.source - const conditionNodeOutputAnchorId = conditionEdge.sourceHandle - - const conditionNode = reactflowNodes.find((node) => node.id === conditionNodeId) - const outputAnchors = conditionNode?.data.outputAnchors - - if (!outputAnchors || !outputAnchors.length || !outputAnchors[0].options) continue - - const conditionOutputAnchorLabel = - outputAnchors[0].options.find((option: any) => option.id === conditionNodeOutputAnchorId)?.label ?? '' - - if (!conditionOutputAnchorLabel) continue - - if (Object.prototype.hasOwnProperty.call(conditionalEdges, conditionNodeId)) { - conditionalEdges[conditionNodeId] = { - ...conditionalEdges[conditionNodeId], - nodes: { ...conditionalEdges[conditionNodeId].nodes, [conditionOutputAnchorLabel]: nodeInstance.name } - } - } else { - conditionalEdges[conditionNodeId] = { - nodes: { [conditionOutputAnchorLabel]: nodeInstance.name }, - func: conditionNode.data.instance.node - } - } - } - } - - /* - * Prepare Conditional Tool Edges. This is just for LLMNode -> ToolNode - * Example: { - * 'agent_1': { source: agent, toolNodes: [node] } - * } - */ - const prepareLLMToToolEdges = (predecessorAgent: ISeqAgentNode, toolNodeInstance: ISeqAgentNode) => { - if (Object.prototype.hasOwnProperty.call(conditionalToolNodes, predecessorAgent.id)) { - const toolNodes = conditionalToolNodes[predecessorAgent.id].toolNodes - toolNodes.push(toolNodeInstance) - conditionalToolNodes[predecessorAgent.id] = { source: predecessorAgent, toolNodes } - } else { - conditionalToolNodes[predecessorAgent.id] = { - source: predecessorAgent, - toolNodes: [toolNodeInstance] - } - } - } - - /*** This is to bind the tools to the model of LLMNode, when the LLMNode is predecessor/successor of ToolNode ***/ - const createBindModel = (agent: ISeqAgentNode, toolNodeInstance: ISeqAgentNode) => { - const tools = flatten(toolNodeInstance.node?.tools) - bindModel[agent.id] = agent.llm.bindTools(tools) - } - - /*** Start processing every Agent nodes ***/ - for (const agentNodeId of getSortedDepthNodes(depthQueue)) { - const agentNode = reactflowNodes.find((node) => node.id === agentNodeId) - if (!agentNode) continue - - const eligibleSeqNodes = ['seqAgent', 'seqEnd', 'seqLoop', 'seqToolNode', 'seqLLMNode'] - const nodesToAdd = ['seqAgent', 'seqToolNode', 'seqLLMNode'] - - if (eligibleSeqNodes.includes(agentNode.data.name)) { - try { - const agentInstance: ISeqAgentNode = await initiateNode(agentNode) - - if (nodesToAdd.includes(agentNode.data.name)) { - // Add node to graph - seqGraph.addNode(agentInstance.name, agentInstance.node) - - /* - * If it is an Interrupted Agent, we want to: - * 1.) Add conditional edges to the Interrupted Agent via agentInterruptToolFunc - * 2.) Add agent to the interruptToolNodeNames list - */ - if (agentInstance.type === 'agent' && agentNode.data.inputs?.interrupt) { - interruptToolNodeNames.push(agentInstance.agentInterruptToolNode.name) - - const nextNodeId = reactflowEdges.find((edge) => edge.source === agentNode.id)?.target - const nextNode = reactflowNodes.find((node) => node.id === nextNodeId) - - let nextNodeSeqAgentName = '' - if (nextNodeId && nextNode) { - nextNodeSeqAgentName = nextNode.data.instance.name - - // If next node is Condition Node, process the interrupted route mapping, see more details from comments of processInterruptedRouteMapping - if (nextNode.data.name.includes('seqCondition')) { - const conditionNode = nextNodeId - processInterruptedRouteMapping(conditionNode) - seqGraph = await agentInstance.agentInterruptToolFunc( - seqGraph, - undefined, - nextNode.data.instance.node, - interruptedRouteMapping[conditionNode] - ) - } else { - seqGraph = await agentInstance.agentInterruptToolFunc(seqGraph, nextNodeSeqAgentName) - } - } else { - seqGraph = await agentInstance.agentInterruptToolFunc(seqGraph, nextNodeSeqAgentName) - } - } - } - - if (agentInstance.predecessorAgents) { - const predecessorAgents: ISeqAgentNode[] = agentInstance.predecessorAgents - - const edges = [] - for (const predecessorAgent of predecessorAgents) { - // Add start edge and set entry point - if (predecessorAgent.name === START) { - if (agentInstance.moderations && agentInstance.moderations.length > 0) { - try { - for (const moderation of agentInstance.moderations) { - question = await moderation.checkForViolations(question) - } - } catch (e) { - throw new InternalFlowiseError(StatusCodes.INTERNAL_SERVER_ERROR, getErrorMessage(e)) - } - } - //@ts-ignore - seqGraph.addEdge(START, agentInstance.name) - } else if (predecessorAgent.type === 'condition') { - /* - * If current node is Condition Node, AND predecessor is an Interrupted Agent - * Don't add conditional edges to the Interrupted Agent, as it will be added separately from the function - agentInterruptToolFunc - */ - if (!Object.prototype.hasOwnProperty.call(interruptedRouteMapping, predecessorAgent.id)) { - prepareConditionalEdges(agentNode.data.id, agentInstance) - } - } else if (agentNode.data.name === 'seqToolNode') { - // Prepare the conditional edges for LLMNode -> ToolNode AND bind the tools to LLMNode - prepareLLMToToolEdges(predecessorAgent, agentInstance) - createBindModel(predecessorAgent, agentInstance) - - // If current ToolNode has interrupt turned on, add the ToolNode name to interruptToolNodeNames - if (agentInstance.node.interrupt) { - interruptToolNodeNames.push(agentInstance.name) - } - } else if (predecessorAgent.name) { - // In the scenario when ToolNode -> LLMNode, bind the tools to LLMNode - if (agentInstance.type === 'llm' && predecessorAgent.type === 'tool') { - createBindModel(agentInstance, predecessorAgent) - } - - // Add edge to graph ONLY when predecessor is not an Interrupted Agent - if (!predecessorAgent.agentInterruptToolNode) { - edges.push(predecessorAgent.name) - } - } - } - - // Edges can be multiple, in the case of parallel node executions - if (edges.length > 1) { - //@ts-ignore - seqGraph.addEdge(edges, agentInstance.name) - } else if (edges.length === 1) { - //@ts-ignore - seqGraph.addEdge(...edges, agentInstance.name) - } - } - } catch (e) { - throw new InternalFlowiseError(StatusCodes.INTERNAL_SERVER_ERROR, `Error initialize agent nodes - ${getErrorMessage(e)}`) - } - } - } - - /*** Add conditional edges to graph for condition nodes ***/ - for (const conditionNodeId in conditionalEdges) { - const startConditionEdges = reactflowEdges.filter((edge) => edge.target === conditionNodeId) - if (!startConditionEdges.length) continue - - for (const startConditionEdge of startConditionEdges) { - const startConditionNode = reactflowNodes.find((node) => node.id === startConditionEdge.source) - if (!startConditionNode) continue - seqGraph.addConditionalEdges( - startConditionNode.data.instance.name, - conditionalEdges[conditionNodeId].func, - //@ts-ignore - conditionalEdges[conditionNodeId].nodes - ) - } - } - - /*** Add conditional edges to graph for LLMNode -> ToolNode ***/ - for (const llmSourceNodeId in conditionalToolNodes) { - const connectedToolNodes = conditionalToolNodes[llmSourceNodeId].toolNodes - const sourceNode = conditionalToolNodes[llmSourceNodeId].source - - const routeMessage = (state: ISeqAgentsState) => { - const messages = state.messages as unknown as BaseMessage[] - const lastMessage = messages[messages.length - 1] as AIMessage - - if (!lastMessage.tool_calls?.length) { - return END - } - - for (const toolCall of lastMessage.tool_calls) { - for (const toolNode of connectedToolNodes) { - const tools = (toolNode.node?.tools as StructuredTool[]) || ((toolNode as any).tools as StructuredTool[]) - if (tools.some((tool) => tool.name === toolCall.name)) { - return toolNode.name - } - } - } - return END - } - - seqGraph.addConditionalEdges( - //@ts-ignore - sourceNode.name, - routeMessage - ) - } - - /*** Add agentflow to pool ***/ - ;(seqGraph as any).signal = options.signal - appServer.chatflowPool.add( - `${chatflow.id}_${options.chatId}`, - seqGraph as any, - reactflowNodes.filter((node) => startAgentNodes.map((nd) => nd.id).includes(node.id)), - overrideConfig - ) - - /*** Get memory ***/ - const startNode = reactflowNodes.find((node: IReactFlowNode) => node.data.name === 'seqStart') - let memory = startNode?.data.instance?.checkpointMemory - - try { - const graph = seqGraph.compile({ checkpointer: memory, interruptBefore: interruptToolNodeNames as any }) - - const loggerHandler = new ConsoleCallbackHandler(logger) - const callbacks = await additionalCallbacks(flowNodeData as any, options) - const config = { configurable: { thread_id: threadId }, bindModel } - - let prependMessages = [] - // Only append in the first message - if (prependHistoryMessages.length === chatHistory.length) { - for (const message of prependHistoryMessages) { - if (message.role === 'apiMessage' || message.type === 'apiMessage') { - prependMessages.push(new AIMessage({ content: message.message || message.content || '' })) - } else if (message.role === 'userMessage' || message.type === 'userMessage') { - prependMessages.push(new HumanMessage({ content: message.message || message.content || '' })) - } - } - } - - let humanMsg: { messages: HumanMessage[] | ToolMessage[] } | null = { - messages: [...prependMessages, new HumanMessage({ content: question })] - } - - if (action && action.mapping && question === action.mapping.approve) { - humanMsg = null - } else if (action && action.mapping && question === action.mapping.reject) { - humanMsg = { - messages: action.mapping.toolCalls.map((toolCall) => { - return new ToolMessage({ - name: toolCall.name, - content: `Tool ${toolCall.name} call denied by user. Acknowledge that, and DONT perform further actions. Only ask if user have other questions`, - tool_call_id: toolCall.id!, - additional_kwargs: { toolCallsDenied: true } - }) - }) - } - } - return await graph.stream(humanMsg, { callbacks: [loggerHandler, ...callbacks], configurable: config }) - } catch (e) { - logger.error('Error compile graph', e) - throw new InternalFlowiseError(StatusCodes.INTERNAL_SERVER_ERROR, `Error compile graph - ${getErrorMessage(e)}`) - } -} - -const getSortedDepthNodes = (depthQueue: IDepthQueue) => { - // Step 1: Convert the object into an array of [key, value] pairs and sort them by the value - const sortedEntries = Object.entries(depthQueue).sort((a, b) => a[1] - b[1]) - - // Step 2: Group keys by their depth values - const groupedByDepth: Record = {} - sortedEntries.forEach(([key, value]) => { - if (!groupedByDepth[value]) { - groupedByDepth[value] = [] - } - groupedByDepth[value].push(key) - }) - - // Step 3: Create the final sorted array with grouped keys - const sortedArray: (string | string[])[] = [] - Object.keys(groupedByDepth) - .sort((a, b) => parseInt(a) - parseInt(b)) - .forEach((depth) => { - const items = groupedByDepth[parseInt(depth)] - sortedArray.push(...items) - }) - - return sortedArray.flat() -} diff --git a/studio-frontend/packages/server/src/utils/buildChatflow.ts b/studio-frontend/packages/server/src/utils/buildChatflow.ts deleted file mode 100644 index 429492f..0000000 --- a/studio-frontend/packages/server/src/utils/buildChatflow.ts +++ /dev/null @@ -1,571 +0,0 @@ -import { Request } from 'express' -import { - IFileUpload, - convertSpeechToText, - ICommonObject, - addSingleFileToStorage, - addArrayFilesToStorage, - mapMimeTypeToInputField, - IServerSideEventStreamer -} from 'flowise-components' -import { StatusCodes } from 'http-status-codes' -import { - IncomingInput, - IMessage, - INodeData, - IReactFlowObject, - IReactFlowNode, - IDepthQueue, - chatType, - IChatMessage, - IChatFlow, - IReactFlowEdge -} from '../Interface' -import { InternalFlowiseError } from '../errors/internalFlowiseError' -import { ChatFlow } from '../database/entities/ChatFlow' -import { getRunningExpressApp } from '../utils/getRunningExpressApp' -import { - isFlowValidForStream, - buildFlow, - getTelemetryFlowObj, - getAppVersion, - resolveVariables, - getSessionChatHistory, - findMemoryNode, - replaceInputsWithConfig, - getStartingNodes, - isStartNodeDependOnInput, - getMemorySessionId, - isSameOverrideConfig, - getEndingNodes, - constructGraphs, - isSameChatId -} from '../utils' -import { validateChatflowAPIKey } from './validateKey' -import { databaseEntities } from '.' -import { v4 as uuidv4 } from 'uuid' -import { omit } from 'lodash' -import * as fs from 'fs' -import logger from './logger' -import { utilAddChatMessage } from './addChatMesage' -import { buildAgentGraph } from './buildAgentGraph' -import { getErrorMessage } from '../errors/utils' -import { ChatMessage } from '../database/entities/ChatMessage' -import { IAction } from 'flowise-components' - -/** - * Build Chatflow - * @param {Request} req - * @param {boolean} isInternal - */ -export const utilBuildChatflow = async (req: Request, isInternal: boolean = false): Promise => { - try { - const appServer = getRunningExpressApp() - const chatflowid = req.params.id - - const httpProtocol = req.get('x-forwarded-proto') || req.protocol - const baseURL = `${httpProtocol}://${req.get('host')}` - - let incomingInput: IncomingInput = req.body - let nodeToExecuteData: INodeData - const chatflow = await appServer.AppDataSource.getRepository(ChatFlow).findOneBy({ - id: chatflowid - }) - if (!chatflow) { - throw new InternalFlowiseError(StatusCodes.NOT_FOUND, `Chatflow ${chatflowid} not found`) - } - - const chatId = incomingInput.chatId ?? incomingInput.overrideConfig?.sessionId ?? uuidv4() - const userMessageDateTime = new Date() - if (!isInternal) { - const isKeyValidated = await validateChatflowAPIKey(req, chatflow) - if (!isKeyValidated) { - throw new InternalFlowiseError(StatusCodes.UNAUTHORIZED, `Unauthorized`) - } - } - - let fileUploads: IFileUpload[] = [] - if (incomingInput.uploads) { - fileUploads = incomingInput.uploads - for (let i = 0; i < fileUploads.length; i += 1) { - const upload = fileUploads[i] - - if ((upload.type === 'file' || upload.type === 'audio') && upload.data) { - const filename = upload.name - const splitDataURI = upload.data.split(',') - const bf = Buffer.from(splitDataURI.pop() || '', 'base64') - const mime = splitDataURI[0].split(':')[1].split(';')[0] - await addSingleFileToStorage(mime, bf, filename, chatflowid, chatId) - upload.type = 'stored-file' - // Omit upload.data since we don't store the content in database - fileUploads[i] = omit(upload, ['data']) - } - - if (upload.type === 'url' && upload.data) { - const filename = upload.name - const urlData = upload.data - fileUploads[i] = { data: urlData, name: filename, type: 'url', mime: upload.mime ?? 'image/png' } - } - - // Run Speech to Text conversion - if (upload.mime === 'audio/webm' || upload.mime === 'audio/mp4' || upload.mime === 'audio/ogg') { - logger.debug(`Attempting a speech to text conversion...`) - let speechToTextConfig: ICommonObject = {} - if (chatflow.speechToText) { - const speechToTextProviders = JSON.parse(chatflow.speechToText) - for (const provider in speechToTextProviders) { - const providerObj = speechToTextProviders[provider] - if (providerObj.status) { - speechToTextConfig = providerObj - speechToTextConfig['name'] = provider - break - } - } - } - if (speechToTextConfig) { - const options: ICommonObject = { - chatId, - chatflowid, - appDataSource: appServer.AppDataSource, - databaseEntities: databaseEntities - } - const speechToTextResult = await convertSpeechToText(upload, speechToTextConfig, options) - logger.debug(`Speech to text result: ${speechToTextResult}`) - if (speechToTextResult) { - incomingInput.question = speechToTextResult - } - } - } - } - } - - let isStreamValid = false - - const files = (req.files as Express.Multer.File[]) || [] - - if (files.length) { - const overrideConfig: ICommonObject = { ...req.body } - const fileNames: string[] = [] - for (const file of files) { - const fileBuffer = fs.readFileSync(file.path) - - const storagePath = await addArrayFilesToStorage(file.mimetype, fileBuffer, file.originalname, fileNames, chatflowid) - - const fileInputField = mapMimeTypeToInputField(file.mimetype) - - overrideConfig[fileInputField] = storagePath - - fs.unlinkSync(file.path) - } - incomingInput = { - question: req.body.question ?? 'hello', - overrideConfig - } - } - - /*** Get chatflows and prepare data ***/ - const flowData = chatflow.flowData - const parsedFlowData: IReactFlowObject = JSON.parse(flowData) - const nodes = parsedFlowData.nodes - const edges = parsedFlowData.edges - - /*** Get session ID ***/ - const memoryNode = findMemoryNode(nodes, edges) - const memoryType = memoryNode?.data.label - let sessionId = getMemorySessionId(memoryNode, incomingInput, chatId, isInternal) - - /*** Get Ending Node with Directed Graph ***/ - const { graph, nodeDependencies } = constructGraphs(nodes, edges) - const directedGraph = graph - const endingNodes = getEndingNodes(nodeDependencies, directedGraph, nodes) - /*** If the graph is an agent graph, build the agent response ***/ - if (endingNodes.filter((node) => node.data.category === 'Multi Agents' || node.data.category === 'Sequential Agents').length) { - return await utilBuildAgentResponse( - chatflow, - isInternal, - chatId, - memoryType ?? '', - sessionId, - userMessageDateTime, - fileUploads, - incomingInput, - nodes, - edges, - baseURL, - appServer.sseStreamer, - true - ) - } - - // Get prepend messages - const prependMessages = incomingInput.history - - /* Reuse the flow without having to rebuild (to avoid duplicated upsert, recomputation, reinitialization of memory) when all these conditions met: - * - Reuse of flows is not disabled - * - Node Data already exists in pool - * - Still in sync (i.e the flow has not been modified since) - * - Existing overrideConfig and new overrideConfig are the same - * - Existing chatId and new chatId is the same - * - Flow doesn't start with/contain nodes that depend on incomingInput.question - ***/ - const isFlowReusable = () => { - return ( - process.env.DISABLE_CHATFLOW_REUSE !== 'true' && - Object.prototype.hasOwnProperty.call(appServer.chatflowPool.activeChatflows, chatflowid) && - appServer.chatflowPool.activeChatflows[chatflowid].inSync && - appServer.chatflowPool.activeChatflows[chatflowid].endingNodeData && - isSameChatId(appServer.chatflowPool.activeChatflows[chatflowid].chatId, chatId) && - isSameOverrideConfig( - isInternal, - appServer.chatflowPool.activeChatflows[chatflowid].overrideConfig, - incomingInput.overrideConfig - ) && - !isStartNodeDependOnInput(appServer.chatflowPool.activeChatflows[chatflowid].startingNodes, nodes) - ) - } - - if (isFlowReusable()) { - nodeToExecuteData = appServer.chatflowPool.activeChatflows[chatflowid].endingNodeData as INodeData - isStreamValid = isFlowValidForStream(nodes, nodeToExecuteData) - logger.debug( - `[server]: Reuse existing chatflow ${chatflowid} with ending node ${nodeToExecuteData.label} (${nodeToExecuteData.id})` - ) - } else { - const isCustomFunctionEndingNode = endingNodes.some((node) => node.data?.outputs?.output === 'EndingNode') - - for (const endingNode of endingNodes) { - const endingNodeData = endingNode.data - - const isEndingNode = endingNodeData?.outputs?.output === 'EndingNode' - - // Once custom function ending node exists, no need to do follow-up checks. - if (isEndingNode) continue - - if ( - endingNodeData.outputs && - Object.keys(endingNodeData.outputs).length && - !Object.values(endingNodeData.outputs ?? {}).includes(endingNodeData.name) - ) { - throw new InternalFlowiseError( - StatusCodes.INTERNAL_SERVER_ERROR, - `Output of ${endingNodeData.label} (${endingNodeData.id}) must be ${endingNodeData.label}, can't be an Output Prediction` - ) - } - - isStreamValid = isFlowValidForStream(nodes, endingNodeData) - } - - // Once custom function ending node exists, flow is always unavailable to stream - isStreamValid = isCustomFunctionEndingNode ? false : isStreamValid - - let chatHistory: IMessage[] = [] - - // When {{chat_history}} is used in Format Prompt Value, fetch the chat conversations from memory node - for (const endingNode of endingNodes) { - const endingNodeData = endingNode.data - - if (!endingNodeData.inputs?.memory) continue - - const memoryNodeId = endingNodeData.inputs?.memory.split('.')[0].replace('{{', '') - const memoryNode = nodes.find((node) => node.data.id === memoryNodeId) - - if (!memoryNode) continue - - chatHistory = await getSessionChatHistory( - chatflowid, - getMemorySessionId(memoryNode, incomingInput, chatId, isInternal), - memoryNode, - appServer.nodesPool.componentNodes, - appServer.AppDataSource, - databaseEntities, - logger, - prependMessages - ) - } - - /*** Get Starting Nodes with Reversed Graph ***/ - const constructedObj = constructGraphs(nodes, edges, { isReversed: true }) - const nonDirectedGraph = constructedObj.graph - let startingNodeIds: string[] = [] - let depthQueue: IDepthQueue = {} - const endingNodeIds = endingNodes.map((n) => n.id) - for (const endingNodeId of endingNodeIds) { - const resx = getStartingNodes(nonDirectedGraph, endingNodeId) - startingNodeIds.push(...resx.startingNodeIds) - depthQueue = Object.assign(depthQueue, resx.depthQueue) - } - startingNodeIds = [...new Set(startingNodeIds)] - - const startingNodes = nodes.filter((nd) => startingNodeIds.includes(nd.id)) - - logger.debug(`[server]: Start building chatflow ${chatflowid}`) - /*** BFS to traverse from Starting Nodes to Ending Node ***/ - const reactFlowNodes = await buildFlow({ - startingNodeIds, - reactFlowNodes: nodes, - reactFlowEdges: edges, - graph, - depthQueue, - componentNodes: appServer.nodesPool.componentNodes, - question: incomingInput.question, - chatHistory, - chatId, - sessionId: sessionId ?? '', - chatflowid, - appDataSource: appServer.AppDataSource, - overrideConfig: incomingInput?.overrideConfig, - cachePool: appServer.cachePool, - isUpsert: false, - uploads: incomingInput.uploads, - baseURL - }) - - const nodeToExecute = - endingNodeIds.length === 1 - ? reactFlowNodes.find((node: IReactFlowNode) => endingNodeIds[0] === node.id) - : reactFlowNodes[reactFlowNodes.length - 1] - if (!nodeToExecute) { - throw new InternalFlowiseError(StatusCodes.NOT_FOUND, `Node not found`) - } - - if (incomingInput.overrideConfig) { - nodeToExecute.data = replaceInputsWithConfig(nodeToExecute.data, incomingInput.overrideConfig) - } - - const flowData: ICommonObject = { - chatflowid, - chatId, - sessionId, - chatHistory, - ...incomingInput.overrideConfig - } - - const reactFlowNodeData: INodeData = await resolveVariables( - appServer.AppDataSource, - nodeToExecute.data, - reactFlowNodes, - incomingInput.question, - chatHistory, - flowData - ) - nodeToExecuteData = reactFlowNodeData - - appServer.chatflowPool.add(chatflowid, nodeToExecuteData, startingNodes, incomingInput?.overrideConfig, chatId) - } - - logger.debug(`[server]: Running ${nodeToExecuteData.label} (${nodeToExecuteData.id})`) - - const nodeInstanceFilePath = appServer.nodesPool.componentNodes[nodeToExecuteData.name].filePath as string - const nodeModule = await import(nodeInstanceFilePath) - const nodeInstance = new nodeModule.nodeClass({ sessionId }) - - let result = isStreamValid - ? await nodeInstance.run(nodeToExecuteData, incomingInput.question, { - chatId, - chatflowid, - logger, - appDataSource: appServer.AppDataSource, - databaseEntities, - analytic: chatflow.analytic, - uploads: incomingInput.uploads, - prependMessages, - sseStreamer: appServer.sseStreamer, - shouldStreamResponse: isStreamValid - }) - : await nodeInstance.run(nodeToExecuteData, incomingInput.question, { - chatId, - chatflowid, - logger, - appDataSource: appServer.AppDataSource, - databaseEntities, - analytic: chatflow.analytic, - uploads: incomingInput.uploads, - prependMessages - }) - result = typeof result === 'string' ? { text: result } : result - - // Retrieve threadId from assistant if exists - if (typeof result === 'object' && result.assistant) { - sessionId = result.assistant.threadId - } - - const userMessage: Omit = { - role: 'userMessage', - content: incomingInput.question, - chatflowid, - chatType: isInternal ? chatType.INTERNAL : chatType.EXTERNAL, - chatId, - memoryType, - sessionId, - createdDate: userMessageDateTime, - fileUploads: incomingInput.uploads ? JSON.stringify(fileUploads) : undefined, - leadEmail: incomingInput.leadEmail - } - await utilAddChatMessage(userMessage) - - let resultText = '' - if (result.text) resultText = result.text - else if (result.json) resultText = '```json\n' + JSON.stringify(result.json, null, 2) - else resultText = JSON.stringify(result, null, 2) - - const apiMessage: Omit = { - role: 'apiMessage', - content: resultText, - chatflowid, - chatType: isInternal ? chatType.INTERNAL : chatType.EXTERNAL, - chatId, - memoryType, - sessionId - } - if (result?.sourceDocuments) apiMessage.sourceDocuments = JSON.stringify(result.sourceDocuments) - if (result?.usedTools) apiMessage.usedTools = JSON.stringify(result.usedTools) - if (result?.fileAnnotations) apiMessage.fileAnnotations = JSON.stringify(result.fileAnnotations) - if (result?.artifacts) apiMessage.artifacts = JSON.stringify(result.artifacts) - - const chatMessage = await utilAddChatMessage(apiMessage) - - logger.debug(`[server]: Finished running ${nodeToExecuteData.label} (${nodeToExecuteData.id})`) - await appServer.telemetry.sendTelemetry('prediction_sent', { - version: await getAppVersion(), - chatflowId: chatflowid, - chatId, - type: isInternal ? chatType.INTERNAL : chatType.EXTERNAL, - flowGraph: getTelemetryFlowObj(nodes, edges) - }) - - // Prepare response - // return the question in the response - // this is used when input text is empty but question is in audio format - result.question = incomingInput.question - result.chatId = chatId - result.chatMessageId = chatMessage?.id - result.isStreamValid = isStreamValid - - if (sessionId) result.sessionId = sessionId - if (memoryType) result.memoryType = memoryType - - return result - } catch (e) { - logger.error('[server]: Error:', e) - if (e instanceof InternalFlowiseError && e.statusCode === StatusCodes.UNAUTHORIZED) { - throw e - } else { - throw new InternalFlowiseError(StatusCodes.INTERNAL_SERVER_ERROR, getErrorMessage(e)) - } - } -} - -const utilBuildAgentResponse = async ( - agentflow: IChatFlow, - isInternal: boolean, - chatId: string, - memoryType: string, - sessionId: string, - userMessageDateTime: Date, - fileUploads: IFileUpload[], - incomingInput: IncomingInput, - nodes: IReactFlowNode[], - edges: IReactFlowEdge[], - baseURL?: string, - sseStreamer?: IServerSideEventStreamer, - shouldStreamResponse?: boolean -) => { - try { - const appServer = getRunningExpressApp() - const streamResults = await buildAgentGraph( - agentflow, - chatId, - sessionId, - incomingInput, - isInternal, - baseURL, - sseStreamer, - shouldStreamResponse - ) - if (streamResults) { - const { finalResult, finalAction, sourceDocuments, artifacts, usedTools, agentReasoning } = streamResults - const userMessage: Omit = { - role: 'userMessage', - content: incomingInput.question, - chatflowid: agentflow.id, - chatType: isInternal ? chatType.INTERNAL : chatType.EXTERNAL, - chatId, - memoryType, - sessionId, - createdDate: userMessageDateTime, - fileUploads: incomingInput.uploads ? JSON.stringify(fileUploads) : undefined, - leadEmail: incomingInput.leadEmail - } - await utilAddChatMessage(userMessage) - - const apiMessage: Omit = { - role: 'apiMessage', - content: finalResult, - chatflowid: agentflow.id, - chatType: isInternal ? chatType.INTERNAL : chatType.EXTERNAL, - chatId, - memoryType, - sessionId - } - if (sourceDocuments?.length) apiMessage.sourceDocuments = JSON.stringify(sourceDocuments) - if (artifacts?.length) apiMessage.artifacts = JSON.stringify(artifacts) - if (usedTools?.length) apiMessage.usedTools = JSON.stringify(usedTools) - if (agentReasoning?.length) apiMessage.agentReasoning = JSON.stringify(agentReasoning) - if (finalAction && Object.keys(finalAction).length) apiMessage.action = JSON.stringify(finalAction) - const chatMessage = await utilAddChatMessage(apiMessage) - - await appServer.telemetry.sendTelemetry('agentflow_prediction_sent', { - version: await getAppVersion(), - agentflowId: agentflow.id, - chatId, - type: isInternal ? chatType.INTERNAL : chatType.EXTERNAL, - flowGraph: getTelemetryFlowObj(nodes, edges) - }) - - // Find the previous chat message with the same action id and remove the action - if (incomingInput.action && Object.keys(incomingInput.action).length) { - let query = await appServer.AppDataSource.getRepository(ChatMessage) - .createQueryBuilder('chat_message') - .where('chat_message.chatId = :chatId', { chatId }) - .orWhere('chat_message.sessionId = :sessionId', { sessionId }) - .orderBy('chat_message.createdDate', 'DESC') - .getMany() - - for (const result of query) { - if (result.action) { - try { - const action: IAction = JSON.parse(result.action) - if (action.id === incomingInput.action.id) { - const newChatMessage = new ChatMessage() - Object.assign(newChatMessage, result) - newChatMessage.action = null - const cm = await appServer.AppDataSource.getRepository(ChatMessage).create(newChatMessage) - await appServer.AppDataSource.getRepository(ChatMessage).save(cm) - break - } - } catch (e) { - // error converting action to JSON - } - } - } - } - - // Prepare response - let result: ICommonObject = {} - result.text = finalResult - result.question = incomingInput.question - result.chatId = chatId - result.chatMessageId = chatMessage?.id - if (sessionId) result.sessionId = sessionId - if (memoryType) result.memoryType = memoryType - if (agentReasoning?.length) result.agentReasoning = agentReasoning - if (finalAction && Object.keys(finalAction).length) result.action = finalAction - - return result - } - return undefined - } catch (e) { - logger.error('[server]: Error:', e) - throw new InternalFlowiseError(StatusCodes.INTERNAL_SERVER_ERROR, getErrorMessage(e)) - } -} diff --git a/studio-frontend/packages/server/src/utils/fileRepository.ts b/studio-frontend/packages/server/src/utils/fileRepository.ts deleted file mode 100644 index 245b9ec..0000000 --- a/studio-frontend/packages/server/src/utils/fileRepository.ts +++ /dev/null @@ -1,97 +0,0 @@ -import { ChatFlow } from '../database/entities/ChatFlow' -import { IReactFlowObject } from '../Interface' -import { addBase64FilesToStorage } from 'flowise-components' - -export const containsBase64File = (chatflow: ChatFlow) => { - const parsedFlowData: IReactFlowObject = JSON.parse(chatflow.flowData) - const re = new RegExp('^data.*;base64', 'i') - let found = false - const nodes = parsedFlowData.nodes - for (const node of nodes) { - if (node.data.category !== 'Document Loaders') { - continue - } - const inputs = node.data.inputs - if (inputs) { - const keys = Object.getOwnPropertyNames(inputs) - for (let i = 0; i < keys.length; i++) { - const input = inputs[keys[i]] - if (!input) { - continue - } - if (typeof input !== 'string') { - continue - } - if (input.startsWith('[')) { - try { - const files = JSON.parse(input) - for (let j = 0; j < files.length; j++) { - const file = files[j] - if (re.test(file)) { - found = true - break - } - } - } catch (e) { - continue - } - } - if (re.test(input)) { - found = true - break - } - } - } - } - return found -} - -export const updateFlowDataWithFilePaths = async (chatflowid: string, flowData: string, orgId: string = '') => { - try { - const parsedFlowData: IReactFlowObject = JSON.parse(flowData) - const re = new RegExp('^data.*;base64', 'i') - const nodes = parsedFlowData.nodes - for (let j = 0; j < nodes.length; j++) { - const node = nodes[j] - if (node.data.category !== 'Document Loaders') { - continue - } - if (node.data.inputs) { - const inputs = node.data.inputs - const keys = Object.getOwnPropertyNames(inputs) - for (let i = 0; i < keys.length; i++) { - const fileNames: string[] = [] - const key = keys[i] - const input = inputs?.[key] - if (!input) { - continue - } - if (typeof input !== 'string') { - continue - } - if (input.startsWith('[')) { - try { - const files = JSON.parse(input) - for (let j = 0; j < files.length; j++) { - const file = files[j] - if (re.test(file)) { - const result = await addBase64FilesToStorage(file, chatflowid, fileNames, orgId) - node.data.inputs[key] = result.path - } - } - } catch (e) { - continue - } - } else if (re.test(input)) { - const result = await addBase64FilesToStorage(input, chatflowid, fileNames, orgId) - node.data.inputs[key] = result.path - } - } - } - } - - return JSON.stringify(parsedFlowData) - } catch (e) { - return '' - } -} diff --git a/studio-frontend/packages/server/src/utils/getUploadsConfig.ts b/studio-frontend/packages/server/src/utils/getUploadsConfig.ts deleted file mode 100644 index 4688d68..0000000 --- a/studio-frontend/packages/server/src/utils/getUploadsConfig.ts +++ /dev/null @@ -1,121 +0,0 @@ -import { StatusCodes } from 'http-status-codes' -import { INodeParams } from 'flowise-components' -import { ChatFlow } from '../database/entities/ChatFlow' -import { getRunningExpressApp } from '../utils/getRunningExpressApp' -import { IUploadFileSizeAndTypes, IReactFlowNode, IReactFlowEdge } from '../Interface' -import { InternalFlowiseError } from '../errors/internalFlowiseError' - -type IUploadConfig = { - isSpeechToTextEnabled: boolean - isImageUploadAllowed: boolean - isFileUploadAllowed: boolean - imgUploadSizeAndTypes: IUploadFileSizeAndTypes[] - fileUploadSizeAndTypes: IUploadFileSizeAndTypes[] -} - -/** - * Method that checks if uploads are enabled in the chatflow - * @param {string} chatflowid - */ -export const utilGetUploadsConfig = async (chatflowid: string): Promise => { - const appServer = getRunningExpressApp() - const chatflow = await appServer.AppDataSource.getRepository(ChatFlow).findOneBy({ - id: chatflowid - }) - if (!chatflow) { - throw new InternalFlowiseError(StatusCodes.NOT_FOUND, `Chatflow ${chatflowid} not found`) - } - - const flowObj = JSON.parse(chatflow.flowData) - const nodes: IReactFlowNode[] = flowObj.nodes - const edges: IReactFlowEdge[] = flowObj.edges - - let isSpeechToTextEnabled = false - let isImageUploadAllowed = false - let isFileUploadAllowed = false - - /* - * Check for STT - */ - if (chatflow.speechToText) { - const speechToTextProviders = JSON.parse(chatflow.speechToText) - for (const provider in speechToTextProviders) { - if (provider !== 'none') { - const providerObj = speechToTextProviders[provider] - if (providerObj.status) { - isSpeechToTextEnabled = true - break - } - } - } - } - - /* - * Condition for isFileUploadAllowed - * 1.) vector store with fileUpload = true && connected to a document loader with fileType - */ - const fileUploadSizeAndTypes: IUploadFileSizeAndTypes[] = [] - for (const node of nodes) { - if (node.data.category === 'Vector Stores' && node.data.inputs?.fileUpload) { - // Get the connected document loader node fileTypes - const sourceDocumentEdges = edges.filter( - (edge) => edge.target === node.id && edge.targetHandle === `${node.id}-input-document-Document` - ) - for (const edge of sourceDocumentEdges) { - const sourceNode = nodes.find((node) => node.id === edge.source) - if (!sourceNode) continue - const fileType = sourceNode.data.inputParams.find((param) => param.type === 'file' && param.fileType)?.fileType - if (fileType) { - fileUploadSizeAndTypes.push({ - fileTypes: fileType.split(', '), - maxUploadSize: 500 - }) - isFileUploadAllowed = true - } - } - break - } - } - - /* - * Condition for isImageUploadAllowed - * 1.) one of the imgUploadAllowedNodes exists - * 2.) one of the imgUploadLLMNodes exists + allowImageUploads is ON - */ - const imgUploadSizeAndTypes: IUploadFileSizeAndTypes[] = [] - const imgUploadAllowedNodes = [ - 'llmChain', - 'conversationChain', - 'reactAgentChat', - 'conversationalAgent', - 'toolAgent', - 'supervisor', - 'seqStart' - ] - const imgUploadLLMNodes = ['chatOpenAI', 'chatAnthropic', 'awsChatBedrock', 'azureChatOpenAI', 'chatGoogleGenerativeAI'] - - if (nodes.some((node) => imgUploadAllowedNodes.includes(node.data.name))) { - nodes.forEach((node: IReactFlowNode) => { - if (imgUploadLLMNodes.indexOf(node.data.name) > -1) { - // TODO: for now the maxUploadSize is hardcoded to 5MB, we need to add it to the node properties - node.data.inputParams.map((param: INodeParams) => { - if (param.name === 'allowImageUploads' && node.data.inputs?.['allowImageUploads']) { - imgUploadSizeAndTypes.push({ - fileTypes: 'image/gif;image/jpeg;image/png;image/webp;'.split(';'), - maxUploadSize: 5 - }) - isImageUploadAllowed = true - } - }) - } - }) - } - - return { - isSpeechToTextEnabled, - isImageUploadAllowed, - isFileUploadAllowed, - imgUploadSizeAndTypes, - fileUploadSizeAndTypes - } -} diff --git a/studio-frontend/packages/server/src/utils/index.ts b/studio-frontend/packages/server/src/utils/index.ts index 874d194..ea178c5 100644 --- a/studio-frontend/packages/server/src/utils/index.ts +++ b/studio-frontend/packages/server/src/utils/index.ts @@ -16,30 +16,55 @@ import { IReactFlowEdge, IReactFlowNode, IVariableDict, - IncomingInput -} from '../Interface' -import { cloneDeep, get, isEqual } from 'lodash' -import { - convertChatHistoryToText, - getInputVariables, - handleEscapeCharacters, - getEncryptionKeyPath, + IncomingInput, ICommonObject, - IDatabaseEntity, IMessage, - FlowiseMemory, IFileUpload -} from 'flowise-components' +} from '../Interface' +import { cloneDeep, get, isEqual } from 'lodash' import { randomBytes } from 'crypto' import { AES, enc } from 'crypto-js' +// Local stubs for flowise-components utilities +export type IDatabaseEntity = { [key: string]: any } + +export interface FlowiseMemory { + getChatMessages(sessionId: string, returnBaseMessages?: boolean, prependMessages?: IMessage[]): Promise + clearChatMessages(sessionId: string): Promise +} + +export const handleEscapeCharacters = (input: string, toEscape: boolean): string => { + if (toEscape) { + return input.replace(/\n/g, 'FLOWISE_NEWLINE').replace(/\t/g, 'FLOWISE_TAB') + } else { + return input.replace(/FLOWISE_NEWLINE/g, '\n').replace(/FLOWISE_TAB/g, '\t') + } +} + +export const convertChatHistoryToText = (chatHistory: IMessage[]): string => { + return chatHistory.map((msg) => `${msg.role ?? msg.type}: ${msg.content ?? msg.message}`).join('\n') +} + +export const getInputVariables = (paramValue: string): string[] => { + if (typeof paramValue !== 'string') return [] + const matches = paramValue.match(/\{\{(.*?)\}\}/g) || [] + return matches.map((m) => m.slice(2, -2).trim()) +} + +export const getEncryptionKeyPath = (): string => { + if (process.env.SECRETKEY_PATH) { + return require('path').join(process.env.SECRETKEY_PATH, 'encryption.key') + } + const home = process.env[process.platform === 'win32' ? 'USERPROFILE' : 'HOME'] ?? process.cwd() + return require('path').join(home, '.flowise', 'encryption.key') +} + import { ChatFlow } from '../database/entities/ChatFlow' import { ChatMessage } from '../database/entities/ChatMessage' import { Credential } from '../database/entities/Credential' import { Tool } from '../database/entities/Tool' import { Assistant } from '../database/entities/Assistant' import { DataSource } from 'typeorm' -import { CachePool } from '../CachePool' import { Variable } from '../database/entities/Variable' import { DocumentStore } from '../database/entities/DocumentStore' import { DocumentStoreFileChunk } from '../database/entities/DocumentStoreFileChunk' @@ -430,7 +455,6 @@ type BuildFlowParams = { chatflowid: string appDataSource: DataSource overrideConfig?: ICommonObject - cachePool?: CachePool isUpsert?: boolean stopNodeId?: string uploads?: IFileUpload[] @@ -455,7 +479,6 @@ export const buildFlow = async ({ chatflowid, appDataSource, overrideConfig, - cachePool, isUpsert, stopNodeId, uploads, @@ -525,7 +548,6 @@ export const buildFlow = async ({ logger, appDataSource, databaseEntities, - cachePool, dynamicVariables, uploads, baseURL @@ -549,7 +571,6 @@ export const buildFlow = async ({ logger, appDataSource, databaseEntities, - cachePool, isUpsert, dynamicVariables, uploads, @@ -1116,7 +1137,7 @@ export const findAvailableConfigs = (reactFlowNodes: IReactFlowNode[], component obj = { node: flowNode.data.label, nodeId: flowNode.data.id, - label: inputParam.label, + label: inputParam.label ?? '', name: 'files', type: inputParam.fileType ?? inputParam.type } @@ -1124,7 +1145,7 @@ export const findAvailableConfigs = (reactFlowNodes: IReactFlowNode[], component obj = { node: flowNode.data.label, nodeId: flowNode.data.id, - label: inputParam.label, + label: inputParam.label ?? '', name: inputParam.name, type: inputParam.options ? inputParam.options @@ -1143,9 +1164,9 @@ export const findAvailableConfigs = (reactFlowNodes: IReactFlowNode[], component obj = { node: flowNode.data.label, nodeId: flowNode.data.id, - label: input.label, + label: input.label ?? '', name: input.name, - type: input.type === 'password' ? 'string' : input.type + type: input.type === 'password' ? 'string' : input.type ?? '' } configs.push(obj) } @@ -1156,7 +1177,7 @@ export const findAvailableConfigs = (reactFlowNodes: IReactFlowNode[], component obj = { node: flowNode.data.label, nodeId: flowNode.data.id, - label: inputParam.label, + label: inputParam.label ?? '', name: inputParam.name, type: inputParam.type === 'password' ? 'string' : inputParam.type } diff --git a/studio-frontend/packages/server/src/utils/telemetry.ts b/studio-frontend/packages/server/src/utils/telemetry.ts deleted file mode 100644 index 4b033f2..0000000 --- a/studio-frontend/packages/server/src/utils/telemetry.ts +++ /dev/null @@ -1,52 +0,0 @@ -import { v4 as uuidv4 } from 'uuid' -import { PostHog } from 'posthog-node' -import path from 'path' -import fs from 'fs' -import { getUserHome, getUserSettingsFilePath } from '.' - -export class Telemetry { - postHog?: PostHog - - constructor() { - if (process.env.DISABLE_FLOWISE_TELEMETRY !== 'true') { - this.postHog = new PostHog('phc_jEDuFYnOnuXsws986TLWzuisbRjwFqTl9JL8tDMgqme') - } else { - this.postHog = undefined - } - } - - async id(): Promise { - try { - const settingsContent = await fs.promises.readFile(getUserSettingsFilePath(), 'utf8') - const settings = JSON.parse(settingsContent) - return settings.instanceId - } catch (error) { - const instanceId = uuidv4() - const settings = { - instanceId - } - const defaultLocation = process.env.SECRETKEY_PATH - ? path.join(process.env.SECRETKEY_PATH, 'settings.json') - : path.join(getUserHome(), '.flowise', 'settings.json') - await fs.promises.writeFile(defaultLocation, JSON.stringify(settings, null, 2)) - return instanceId - } - } - - async sendTelemetry(event: string, properties = {}): Promise { - if (this.postHog) { - const distinctId = await this.id() - this.postHog.capture({ - event, - distinctId, - properties - }) - } - } - - async flush(): Promise { - if (this.postHog) { - await this.postHog.shutdownAsync() - } - } -} diff --git a/studio-frontend/packages/server/src/utils/upsertVector.ts b/studio-frontend/packages/server/src/utils/upsertVector.ts deleted file mode 100644 index a6f67e9..0000000 --- a/studio-frontend/packages/server/src/utils/upsertVector.ts +++ /dev/null @@ -1,182 +0,0 @@ -import { Request } from 'express' -import * as fs from 'fs' -import { cloneDeep, omit } from 'lodash' -import { ICommonObject, IMessage, addArrayFilesToStorage, mapMimeTypeToInputField } from 'flowise-components' -import telemetryService from '../services/telemetry' -import logger from '../utils/logger' -import { - buildFlow, - constructGraphs, - getAllConnectedNodes, - findMemoryNode, - getMemorySessionId, - getAppVersion, - getTelemetryFlowObj, - getStartingNodes -} from '../utils' -import { validateChatflowAPIKey } from './validateKey' -import { IncomingInput, INodeDirectedGraph, IReactFlowObject, chatType } from '../Interface' -import { ChatFlow } from '../database/entities/ChatFlow' -import { getRunningExpressApp } from '../utils/getRunningExpressApp' -import { UpsertHistory } from '../database/entities/UpsertHistory' -import { InternalFlowiseError } from '../errors/internalFlowiseError' -import { StatusCodes } from 'http-status-codes' -import { getErrorMessage } from '../errors/utils' - -/** - * Upsert documents - * @param {Request} req - * @param {boolean} isInternal - */ -export const upsertVector = async (req: Request, isInternal: boolean = false) => { - try { - const appServer = getRunningExpressApp() - const chatflowid = req.params.id - let incomingInput: IncomingInput = req.body - - const chatflow = await appServer.AppDataSource.getRepository(ChatFlow).findOneBy({ - id: chatflowid - }) - if (!chatflow) { - throw new InternalFlowiseError(StatusCodes.NOT_FOUND, `Chatflow ${chatflowid} not found`) - } - - if (!isInternal) { - const isKeyValidated = await validateChatflowAPIKey(req, chatflow) - if (!isKeyValidated) { - throw new InternalFlowiseError(StatusCodes.UNAUTHORIZED, `Unauthorized`) - } - } - - const files = (req.files as Express.Multer.File[]) || [] - - if (files.length) { - const overrideConfig: ICommonObject = { ...req.body } - const fileNames: string[] = [] - for (const file of files) { - const fileBuffer = fs.readFileSync(file.path) - - const storagePath = await addArrayFilesToStorage(file.mimetype, fileBuffer, file.originalname, fileNames, chatflowid) - - const fileInputField = mapMimeTypeToInputField(file.mimetype) - - overrideConfig[fileInputField] = storagePath - - fs.unlinkSync(file.path) - } - incomingInput = { - question: req.body.question ?? 'hello', - overrideConfig, - stopNodeId: req.body.stopNodeId - } - if (req.body.chatId) { - incomingInput.chatId = req.body.chatId - } - } - - /*** Get chatflows and prepare data ***/ - const flowData = chatflow.flowData - const parsedFlowData: IReactFlowObject = JSON.parse(flowData) - const nodes = parsedFlowData.nodes - const edges = parsedFlowData.edges - - let stopNodeId = incomingInput?.stopNodeId ?? '' - let chatHistory: IMessage[] = [] - let chatId = incomingInput.chatId ?? '' - let isUpsert = true - - // Get session ID - const memoryNode = findMemoryNode(nodes, edges) - let sessionId = getMemorySessionId(memoryNode, incomingInput, chatId, isInternal) - - const vsNodes = nodes.filter((node) => node.data.category === 'Vector Stores') - - // Get StopNodeId for vector store which has fielUpload - const vsNodesWithFileUpload = vsNodes.filter((node) => node.data.inputs?.fileUpload) - if (vsNodesWithFileUpload.length > 1) { - throw new InternalFlowiseError(StatusCodes.INTERNAL_SERVER_ERROR, 'Multiple vector store nodes with fileUpload enabled') - } else if (vsNodesWithFileUpload.length === 1 && !stopNodeId) { - stopNodeId = vsNodesWithFileUpload[0].data.id - } - - // Check if multiple vector store nodes exist, and if stopNodeId is specified - if (vsNodes.length > 1 && !stopNodeId) { - throw new InternalFlowiseError( - StatusCodes.INTERNAL_SERVER_ERROR, - 'There are multiple vector nodes, please provide stopNodeId in body request' - ) - } else if (vsNodes.length === 1 && !stopNodeId) { - stopNodeId = vsNodes[0].data.id - } else if (!vsNodes.length && !stopNodeId) { - throw new InternalFlowiseError(StatusCodes.NOT_FOUND, 'No vector node found') - } - - const { graph } = constructGraphs(nodes, edges, { isReversed: true }) - - const nodeIds = getAllConnectedNodes(graph, stopNodeId) - - const filteredGraph: INodeDirectedGraph = {} - for (const key of nodeIds) { - if (Object.prototype.hasOwnProperty.call(graph, key)) { - filteredGraph[key] = graph[key] - } - } - - const { startingNodeIds, depthQueue } = getStartingNodes(filteredGraph, stopNodeId) - - const upsertedResult = await buildFlow({ - startingNodeIds, - reactFlowNodes: nodes, - reactFlowEdges: edges, - graph: filteredGraph, - depthQueue, - componentNodes: appServer.nodesPool.componentNodes, - question: incomingInput.question, - chatHistory, - chatId, - sessionId: sessionId ?? '', - chatflowid, - appDataSource: appServer.AppDataSource, - overrideConfig: incomingInput?.overrideConfig, - cachePool: appServer.cachePool, - isUpsert, - stopNodeId - }) - - const startingNodes = nodes.filter((nd) => startingNodeIds.includes(nd.data.id)) - - await appServer.chatflowPool.add(chatflowid, undefined, startingNodes, incomingInput?.overrideConfig, chatId) - - // Save to DB - if (upsertedResult['flowData'] && upsertedResult['result']) { - const result = cloneDeep(upsertedResult) - result['flowData'] = JSON.stringify(result['flowData']) - result['result'] = JSON.stringify(omit(result['result'], ['totalKeys', 'addedDocs'])) - result.chatflowid = chatflowid - const newUpsertHistory = new UpsertHistory() - Object.assign(newUpsertHistory, result) - const upsertHistory = appServer.AppDataSource.getRepository(UpsertHistory).create(newUpsertHistory) - await appServer.AppDataSource.getRepository(UpsertHistory).save(upsertHistory) - } - - await telemetryService.createEvent({ - name: `vector_upserted`, - data: { - version: await getAppVersion(), - chatlowId: chatflowid, - type: isInternal ? chatType.INTERNAL : chatType.EXTERNAL, - flowGraph: getTelemetryFlowObj(nodes, edges), - stopNodeId - } - }) - - return upsertedResult['result'] ?? { result: 'Successfully Upserted' } - } catch (e) { - logger.error('[server]: Error:', e) - if (e instanceof InternalFlowiseError && e.statusCode === StatusCodes.UNAUTHORIZED) { - throw e - } else { - throw new InternalFlowiseError(StatusCodes.INTERNAL_SERVER_ERROR, getErrorMessage(e)) - } - } -} diff --git a/studio-frontend/packages/ui/package.json b/studio-frontend/packages/ui/package.json index 6051cb7..b3ada2e 100644 --- a/studio-frontend/packages/ui/package.json +++ b/studio-frontend/packages/ui/package.json @@ -42,7 +42,6 @@ "flowise-react-json-view": "*", "formik": "^2.2.6", "framer-motion": "^4.1.13", - "history": "^5.0.0", "html-react-parser": "^3.0.4", "keycloak-js": "^26.0.5", "lodash": "^4.17.21", @@ -59,7 +58,6 @@ "react-markdown": "^8.0.6", "react-perfect-scrollbar": "^1.5.8", "react-redux": "^8.0.5", - "react-rewards": "^2.1.0", "react-router": "~6.3.0", "react-router-dom": "~6.3.0", "react-syntax-highlighter": "^15.5.0", @@ -69,9 +67,7 @@ "rehype-mathjax": "^4.0.2", "remark-gfm": "^3.0.1", "remark-math": "^5.1.1", - "showdown": "^2.1.0", "socket.io-client": "^4.6.1", - "tippy.js": "^6.3.7", "uuid": "^9.0.1", "yup": "^0.32.9" }, diff --git a/studio-frontend/packages/ui/src/api/evaluation.js b/studio-frontend/packages/ui/src/api/evaluation.js new file mode 100644 index 0000000..9effd5b --- /dev/null +++ b/studio-frontend/packages/ui/src/api/evaluation.js @@ -0,0 +1,47 @@ +import client from './client' + +const evaluationApi = { + // ── Runs ────────────────────────────────────────────────────────────────── + + // List all evaluation runs + getRuns: () => client.get('/evaluation/runs'), + + // Get a single evaluation run (used for polling) + getRun: (runId) => client.get(`/evaluation/runs/${runId}`), + + // Create a new evaluation run + createRun: (payload) => client.post('/evaluation/runs', payload), + + // ── Datasets ────────────────────────────────────────────────────────────── + + // List all golden datasets + getDatasets: () => client.get('/evaluation/datasets'), + + // Get a single dataset + getDataset: (datasetId) => client.get(`/evaluation/datasets/${datasetId}`), + + // Create a new dataset (manual entries) + createDataset: (payload) => client.post('/evaluation/datasets', payload), + + // Synthesize a dataset from a sandbox + synthesizeDataset: (payload) => client.post('/evaluation/datasets/synthesize', payload), + + // ── Lookup helpers (for modal dropdowns) ───────────────────────────────── + + // List sandboxes available for evaluation + getSandboxes: () => client.get('/evaluation/sandboxes'), + + // List judge models available + getModels: () => client.get('/evaluation/models'), + + // Pull/register a new judge model + pullModel: (payload) => client.post('/evaluation/models/pull', payload), + + // Delete an evaluation run + deleteRun: (runId) => client.delete(`/evaluation/runs/${runId}`), + + // Delete a dataset + deleteDataset: (datasetId) => client.delete(`/evaluation/datasets/${datasetId}`), +} + +export default evaluationApi diff --git a/studio-frontend/packages/ui/src/menu-items/dashboard.js b/studio-frontend/packages/ui/src/menu-items/dashboard.js index ba88227..0d196bc 100644 --- a/studio-frontend/packages/ui/src/menu-items/dashboard.js +++ b/studio-frontend/packages/ui/src/menu-items/dashboard.js @@ -10,11 +10,12 @@ import { IconVariable, IconFiles, IconApps, - IconBrain + IconBrain, + IconListCheck } from '@tabler/icons-react' // constant -const icons = { IconUsersGroup, IconHierarchy, IconBuildingStore, IconKey, IconTool, IconLock, IconRobot, IconVariable, IconFiles, IconApps, IconBrain } +const icons = { IconUsersGroup, IconHierarchy, IconBuildingStore, IconKey, IconTool, IconLock, IconRobot, IconVariable, IconFiles, IconApps, IconBrain, IconListCheck } // ==============================|| DASHBOARD MENU ITEMS ||============================== // @@ -38,6 +39,14 @@ const dashboard = { url: '/finetuning', icon: icons.IconBrain, breadcrumbs: true + }, + { + id: 'evaluation', + title: 'Evaluation', + type: 'item', + url: '/evaluation', + icon: icons.IconListCheck, + breadcrumbs: true } // { // id: 'chatflows', diff --git a/studio-frontend/packages/ui/src/routes/MainRoutes.jsx b/studio-frontend/packages/ui/src/routes/MainRoutes.jsx index 6b7a388..fd9bacc 100644 --- a/studio-frontend/packages/ui/src/routes/MainRoutes.jsx +++ b/studio-frontend/packages/ui/src/routes/MainRoutes.jsx @@ -10,6 +10,9 @@ const Opeaflows = Loadable(lazy(() => import('@/views/opeaflows'))) // finetuning routing const Finetuning = Loadable(lazy(() => import('@/views/finetuning'))) +// evaluation routing +const Evaluation = Loadable(lazy(() => import('@/views/evaluation'))) + // tracer routing const Tracer = Loadable(lazy(() => import('@/views/tracer'))) @@ -66,6 +69,10 @@ const MainRoutes = { path: '/finetuning', element: }, + { + path: '/evaluation', + element: + }, { path:'/tracer/:ns', element: diff --git a/studio-frontend/packages/ui/src/views/evaluation/CreateRunModal.jsx b/studio-frontend/packages/ui/src/views/evaluation/CreateRunModal.jsx new file mode 100644 index 0000000..7454c76 --- /dev/null +++ b/studio-frontend/packages/ui/src/views/evaluation/CreateRunModal.jsx @@ -0,0 +1,232 @@ +import { useState, useEffect } from 'react' +import PropTypes from 'prop-types' + +// material-ui +import { + Box, + Button, + Checkbox, + CircularProgress, + Dialog, + DialogActions, + DialogContent, + DialogTitle, + FormControl, + FormControlLabel, + FormGroup, + FormLabel, + IconButton, + InputLabel, + MenuItem, + Select, + Stack, + Typography +} from '@mui/material' +import { useTheme } from '@mui/material/styles' + +// icons +import { IconX } from '@tabler/icons-react' + +// API +import evaluationApi from '@/api/evaluation' + +const METRICS = [ + 'AnswerRelevancy', + 'Faithfulness', + 'ContextualPrecision', + 'ContextualRecall', + 'ContextualRelevancy', + 'Hallucination' +] + +const CreateRunModal = ({ open, onClose, onRunCreated }) => { + const theme = useTheme() + + const [sandboxes, setSandboxes] = useState([]) + const [datasets, setDatasets] = useState([]) + const [models, setModels] = useState([]) + const [loadingOptions, setLoadingOptions] = useState(false) + + const [sandboxId, setSandboxId] = useState('') + const [datasetId, setDatasetId] = useState('') + const [judgeModel, setJudgeModel] = useState('') + const [selectedMetrics, setSelectedMetrics] = useState(['AnswerRelevancy', 'Faithfulness']) + const [submitting, setSubmitting] = useState(false) + const [error, setError] = useState('') + + useEffect(() => { + if (!open) return + setLoadingOptions(true) + setError('') + Promise.all([ + evaluationApi.getSandboxes(), + evaluationApi.getDatasets(), + evaluationApi.getModels() + ]) + .then(([sbRes, dsRes, mdRes]) => { + setSandboxes(sbRes.data || []) + setDatasets(dsRes.data || []) + setModels(mdRes.data || []) + }) + .catch(() => setError('Failed to load options.')) + .finally(() => setLoadingOptions(false)) + }, [open]) + + const handleMetricToggle = (metric) => { + setSelectedMetrics((prev) => + prev.includes(metric) ? prev.filter((m) => m !== metric) : [...prev, metric] + ) + } + + const handleSubmit = async () => { + if (!sandboxId || !datasetId || !judgeModel || selectedMetrics.length === 0) { + setError('Please fill in all fields and select at least one metric.') + return + } + setSubmitting(true) + setError('') + try { + const res = await evaluationApi.createRun({ + sandbox_id: sandboxId, + dataset_id: datasetId, + judge_model: judgeModel, + metrics: selectedMetrics + }) + onRunCreated && onRunCreated(res.data) + handleClose() + } catch (err) { + setError(err?.response?.data?.message || 'Failed to create run.') + } finally { + setSubmitting(false) + } + } + + const handleClose = () => { + setSandboxId('') + setDatasetId('') + setJudgeModel('') + setSelectedMetrics(['AnswerRelevancy', 'Faithfulness']) + setError('') + onClose() + } + + return ( + + + New Evaluation Run + + + + + + + {loadingOptions ? ( + + + + ) : ( + + {/* Sandbox */} + + Sandbox + + + + {/* Dataset */} + + Dataset + + + + {/* Judge Model */} + + Judge Model + + + + {/* Metrics */} + + + Metrics + + + {METRICS.map((metric) => ( + handleMetricToggle(metric)} + /> + } + label={{metric}} + sx={{ width: '50%' }} + /> + ))} + + + + {error && ( + + {error} + + )} + + )} + + + + + + + + ) +} + +CreateRunModal.propTypes = { + open: PropTypes.bool.isRequired, + onClose: PropTypes.func.isRequired, + onRunCreated: PropTypes.func +} + +export default CreateRunModal diff --git a/studio-frontend/packages/ui/src/views/evaluation/DatasetTab.jsx b/studio-frontend/packages/ui/src/views/evaluation/DatasetTab.jsx new file mode 100644 index 0000000..812a408 --- /dev/null +++ b/studio-frontend/packages/ui/src/views/evaluation/DatasetTab.jsx @@ -0,0 +1,490 @@ +import { useState, useEffect, useCallback } from 'react' +import PropTypes from 'prop-types' + +// material-ui +import { + Box, + Button, + CircularProgress, + Dialog, + DialogActions, + DialogContent, + DialogTitle, + FormControl, + IconButton, + InputLabel, + MenuItem, + Paper, + Select, + Stack, + Table, + TableBody, + TableCell, + TableContainer, + TableHead, + TableRow, + TableSortLabel, + TextField, + Tooltip, + Typography +} from '@mui/material' +import { useTheme, styled } from '@mui/material/styles' +import { tableCellClasses } from '@mui/material/TableCell' + +// icons +import { IconPlus, IconRefresh, IconWand, IconX } from '@tabler/icons-react' + +// API +import evaluationApi from '@/api/evaluation' + +const StyledTableCell = styled(TableCell)(({ theme }) => ({ + borderColor: theme.palette.grey[900] + 25, + [`&.${tableCellClasses.head}`]: { + color: theme.palette.grey[900] + }, + [`&.${tableCellClasses.body}`]: { + fontSize: 14, + height: 56 + } +})) + +const StyledTableRow = styled(TableRow)(() => ({ + '&:last-child td, &:last-child th': { + border: 0 + } +})) + +const formatDate = (dateStr) => { + if (!dateStr) return '—' + try { + const dt = new Date(dateStr) + if (isNaN(dt.getTime())) return dateStr + return dt.toLocaleString() + } catch { + return dateStr + } +} + +// ── New Dataset Modal ───────────────────────────────────────────────────────── + +const NewDatasetModal = ({ open, onClose, onCreated }) => { + const [name, setName] = useState('') + const [description, setDescription] = useState('') + const [entries, setEntries] = useState('') + const [submitting, setSubmitting] = useState(false) + const [error, setError] = useState('') + + const handleClose = () => { + setName('') + setDescription('') + setEntries('') + setError('') + onClose() + } + + const handleSubmit = async () => { + if (!name.trim()) { + setError('Dataset name is required.') + return + } + setSubmitting(true) + setError('') + try { + // Parse entries: newline-separated JSON objects or a JSON array + let parsedEntries = [] + const trimmed = entries.trim() + if (trimmed) { + try { + parsedEntries = JSON.parse(trimmed) + if (!Array.isArray(parsedEntries)) parsedEntries = [parsedEntries] + } catch { + // Try newline-separated JSON objects + parsedEntries = trimmed + .split('\n') + .map((l) => l.trim()) + .filter(Boolean) + .map((l) => JSON.parse(l)) + } + } + const res = await evaluationApi.createDataset({ + name: name.trim(), + description: description.trim(), + entries: parsedEntries + }) + onCreated && onCreated(res.data) + handleClose() + } catch (err) { + setError(err?.response?.data?.message || 'Failed to create dataset.') + } finally { + setSubmitting(false) + } + } + + return ( + + + New Dataset + + + + + + + setName(e.target.value)} + required + /> + setDescription(e.target.value)} + /> + setEntries(e.target.value)} + placeholder={'[{"question": "...", "expected": "..."}]'} + inputProps={{ style: { fontFamily: 'monospace', fontSize: 13 } }} + /> + {error && ( + + {error} + + )} + + + + + + + + ) +} + +NewDatasetModal.propTypes = { + open: PropTypes.bool.isRequired, + onClose: PropTypes.func.isRequired, + onCreated: PropTypes.func +} + +// ── Synthesize Modal ────────────────────────────────────────────────────────── + +const SynthesizeModal = ({ open, onClose, onCreated }) => { + const [sandboxes, setSandboxes] = useState([]) + const [sandboxId, setSandboxId] = useState('') + const [datasetName, setDatasetName] = useState('') + const [numSamples, setNumSamples] = useState(20) + const [loadingOptions, setLoadingOptions] = useState(false) + const [submitting, setSubmitting] = useState(false) + const [error, setError] = useState('') + + useEffect(() => { + if (!open) return + setLoadingOptions(true) + evaluationApi + .getSandboxes() + .then((res) => setSandboxes(res.data || [])) + .catch(() => setError('Failed to load sandboxes.')) + .finally(() => setLoadingOptions(false)) + }, [open]) + + const handleClose = () => { + setSandboxId('') + setDatasetName('') + setNumSamples(20) + setError('') + onClose() + } + + const handleSubmit = async () => { + if (!sandboxId || !datasetName.trim()) { + setError('Sandbox and dataset name are required.') + return + } + setSubmitting(true) + setError('') + try { + const res = await evaluationApi.synthesizeDataset({ + sandbox_id: sandboxId, + name: datasetName.trim(), + num_samples: numSamples + }) + onCreated && onCreated(res.data) + handleClose() + } catch (err) { + setError(err?.response?.data?.message || 'Failed to synthesize dataset.') + } finally { + setSubmitting(false) + } + } + + return ( + + + Synthesize Dataset + + + + + + {loadingOptions ? ( + + + + ) : ( + + + Sandbox + + + setDatasetName(e.target.value)} + required + /> + setNumSamples(Number(e.target.value))} + /> + {error && ( + + {error} + + )} + + )} + + + + + + + ) +} + +SynthesizeModal.propTypes = { + open: PropTypes.bool.isRequired, + onClose: PropTypes.func.isRequired, + onCreated: PropTypes.func +} + +// ── DatasetTab ──────────────────────────────────────────────────────────────── + +const DatasetTab = ({ isVisible }) => { + const theme = useTheme() + + const [datasets, setDatasets] = useState([]) + const [isLoading, setIsLoading] = useState(true) + const [error, setError] = useState(null) + const [order, setOrder] = useState('desc') + const [orderBy, setOrderBy] = useState('created_at') + const [newDatasetOpen, setNewDatasetOpen] = useState(false) + const [synthesizeOpen, setSynthesizeOpen] = useState(false) + + const loadDatasets = useCallback(async () => { + try { + setIsLoading(true) + setError(null) + const res = await evaluationApi.getDatasets() + setDatasets(res.data || []) + } catch (err) { + setError(err?.response?.data?.message || 'Failed to load datasets.') + } finally { + setIsLoading(false) + } + }, []) + + useEffect(() => { + if (isVisible) loadDatasets() + }, [isVisible, loadDatasets]) + + const handleDatasetCreated = (ds) => { + setDatasets((prev) => [ds, ...prev]) + } + + const handleRequestSort = (property) => { + const isAsc = orderBy === property && order === 'asc' + setOrder(isAsc ? 'desc' : 'asc') + setOrderBy(property) + } + + const sortedDatasets = [...datasets].sort((a, b) => { + let cmp = 0 + if (orderBy === 'created_at') { + cmp = new Date(a.created_at) - new Date(b.created_at) + } else if (orderBy === 'name') { + cmp = String(a.name).localeCompare(String(b.name)) + } else if (orderBy === 'entry_count') { + cmp = (a.entry_count || 0) - (b.entry_count || 0) + } + return order === 'asc' ? cmp : -cmp + }) + + const columns = [ + { id: 'name', label: 'Name' }, + { id: 'description', label: 'Description', sortable: false }, + { id: 'entry_count', label: 'Entries' }, + { id: 'created_at', label: 'Created At' } + ] + + return ( + + + + + + + + + + {error && ( + + {error} + + )} + + + + + + {columns.map((col) => ( + + {col.sortable === false ? ( + col.label + ) : ( + handleRequestSort(col.id)} + > + {col.label} + + )} + + ))} + + + + {isLoading ? ( + + + + + + ) : sortedDatasets.length === 0 ? ( + + + + No datasets yet. Create one manually or synthesize from a sandbox. + + + + ) : ( + sortedDatasets.map((ds) => ( + + + + {ds.name} + + + + + {ds.description || '—'} + + + {ds.entry_count ?? '—'} + {formatDate(ds.created_at)} + + )) + )} + +
+
+ + setNewDatasetOpen(false)} + onCreated={handleDatasetCreated} + /> + setSynthesizeOpen(false)} + onCreated={handleDatasetCreated} + /> +
+ ) +} + +DatasetTab.propTypes = { + isVisible: PropTypes.bool +} + +export default DatasetTab diff --git a/studio-frontend/packages/ui/src/views/evaluation/ExecutionTab.jsx b/studio-frontend/packages/ui/src/views/evaluation/ExecutionTab.jsx new file mode 100644 index 0000000..1629d1a --- /dev/null +++ b/studio-frontend/packages/ui/src/views/evaluation/ExecutionTab.jsx @@ -0,0 +1,302 @@ +import { useState, useEffect, useRef, useCallback } from 'react' +import PropTypes from 'prop-types' + +// material-ui +import { + Box, + Button, + Chip, + CircularProgress, + Paper, + Stack, + Table, + TableBody, + TableCell, + TableContainer, + TableHead, + TableRow, + TableSortLabel, + Tooltip, + Typography +} from '@mui/material' +import { useTheme, styled } from '@mui/material/styles' +import { tableCellClasses } from '@mui/material/TableCell' + +// icons +import { IconPlus, IconRefresh } from '@tabler/icons-react' + +// API +import evaluationApi from '@/api/evaluation' + +// components +import CreateRunModal from './CreateRunModal' + +const POLL_INTERVAL_MS = 5000 +const ACTIVE_STATUSES = ['pending', 'running'] + +const StyledTableCell = styled(TableCell)(({ theme }) => ({ + borderColor: theme.palette.grey[900] + 25, + [`&.${tableCellClasses.head}`]: { + color: theme.palette.grey[900] + }, + [`&.${tableCellClasses.body}`]: { + fontSize: 14, + height: 56 + } +})) + +const StyledTableRow = styled(TableRow)(() => ({ + '&:last-child td, &:last-child th': { + border: 0 + } +})) + +const statusColor = (status) => { + switch (status) { + case 'completed': + return 'success' + case 'failed': + return 'error' + case 'running': + return 'primary' + case 'pending': + default: + return 'default' + } +} + +const formatDate = (dateStr) => { + if (!dateStr) return '—' + try { + const dt = new Date(dateStr) + if (isNaN(dt.getTime())) return dateStr + return dt.toLocaleString() + } catch { + return dateStr + } +} + +const formatScore = (scoreSummary) => { + if (!scoreSummary || typeof scoreSummary !== 'object') return '—' + const entries = Object.entries(scoreSummary) + if (entries.length === 0) return '—' + return entries.map(([k, v]) => `${k}: ${typeof v === 'number' ? v.toFixed(3) : v}`).join(', ') +} + +const ExecutionTab = ({ isVisible }) => { + const theme = useTheme() + + const [runs, setRuns] = useState([]) + const [isLoading, setIsLoading] = useState(true) + const [error, setError] = useState(null) + const [order, setOrder] = useState('desc') + const [orderBy, setOrderBy] = useState('created_at') + const [createModalOpen, setCreateModalOpen] = useState(false) + + // Track interval ids per run_id + const pollTimersRef = useRef({}) + + const stopPolling = useCallback((runId) => { + if (pollTimersRef.current[runId]) { + clearInterval(pollTimersRef.current[runId]) + delete pollTimersRef.current[runId] + } + }, []) + + const startPolling = useCallback( + (runId) => { + if (pollTimersRef.current[runId]) return + pollTimersRef.current[runId] = setInterval(async () => { + try { + const res = await evaluationApi.getRun(runId) + const updated = res.data + setRuns((prev) => + prev.map((r) => (r.run_id === runId ? { ...r, ...updated } : r)) + ) + if (!ACTIVE_STATUSES.includes(updated?.status)) { + stopPolling(runId) + } + } catch { + stopPolling(runId) + } + }, POLL_INTERVAL_MS) + }, + [stopPolling] + ) + + const loadRuns = useCallback(async () => { + try { + setIsLoading(true) + setError(null) + const res = await evaluationApi.getRuns() + const data = res.data || [] + setRuns(data) + // Start polling for any active runs + data.forEach((run) => { + if (ACTIVE_STATUSES.includes(run.status)) { + startPolling(run.run_id) + } + }) + } catch (err) { + setError(err?.response?.data?.message || 'Failed to load evaluation runs.') + } finally { + setIsLoading(false) + } + }, [startPolling]) + + useEffect(() => { + if (isVisible) loadRuns() + }, [isVisible, loadRuns]) + + // Cleanup all poll timers on unmount + useEffect(() => { + return () => { + Object.keys(pollTimersRef.current).forEach((id) => clearInterval(pollTimersRef.current[id])) + pollTimersRef.current = {} + } + }, []) + + const handleRunCreated = (newRun) => { + setRuns((prev) => [newRun, ...prev]) + if (ACTIVE_STATUSES.includes(newRun.status)) { + startPolling(newRun.run_id) + } + } + + const handleRequestSort = (property) => { + const isAsc = orderBy === property && order === 'asc' + setOrder(isAsc ? 'desc' : 'asc') + setOrderBy(property) + } + + const sortedRuns = [...runs].sort((a, b) => { + let cmp = 0 + if (orderBy === 'created_at') { + cmp = new Date(a.created_at) - new Date(b.created_at) + } else if (orderBy === 'run_id') { + cmp = String(a.run_id).localeCompare(String(b.run_id)) + } else if (orderBy === 'status') { + cmp = String(a.status).localeCompare(String(b.status)) + } + return order === 'asc' ? cmp : -cmp + }) + + const columns = [ + { id: 'run_id', label: 'Run ID' }, + { id: 'sandbox_id', label: 'Sandbox', sortable: false }, + { id: 'status', label: 'Status' }, + { id: 'score_summary', label: 'Score Summary', sortable: false }, + { id: 'created_at', label: 'Created At' } + ] + + return ( + + + + + + + + + {error && ( + + {error} + + )} + + + + + + {columns.map((col) => ( + + {col.sortable === false ? ( + col.label + ) : ( + handleRequestSort(col.id)} + > + {col.label} + + )} + + ))} + + + + {isLoading ? ( + + + + + + ) : sortedRuns.length === 0 ? ( + + + + No evaluation runs yet. Click "New Run" to get started. + + + + ) : ( + sortedRuns.map((run) => ( + + + + {run.run_id} + + + {run.sandbox_id || '—'} + + + ) : undefined + } + /> + + + {formatScore(run.score_summary)} + + {formatDate(run.created_at)} + + )) + )} + +
+
+ + setCreateModalOpen(false)} + onRunCreated={handleRunCreated} + /> +
+ ) +} + +ExecutionTab.propTypes = { + isVisible: PropTypes.bool +} + +export default ExecutionTab diff --git a/studio-frontend/packages/ui/src/views/evaluation/index.jsx b/studio-frontend/packages/ui/src/views/evaluation/index.jsx new file mode 100644 index 0000000..b9e44a8 --- /dev/null +++ b/studio-frontend/packages/ui/src/views/evaluation/index.jsx @@ -0,0 +1,71 @@ +import { useState } from 'react' + +// material-ui +import { Box, Tab, Tabs, Typography } from '@mui/material' +import { useTheme } from '@mui/material/styles' + +// project imports +import MainCard from '@/ui-component/cards/MainCard' + +// views +import ExecutionTab from './ExecutionTab' +import DatasetTab from './DatasetTab' + +// ==============================|| Evaluation ||============================== // + +const Evaluation = () => { + const theme = useTheme() + const [activeTab, setActiveTab] = useState(0) + + const handleTabChange = (_event, newValue) => { + setActiveTab(newValue) + } + + return ( + + + + Evaluation + + + + + + + + + + + + + ) +} + +export default Evaluation diff --git a/studio-frontend/pnpm-lock.yaml b/studio-frontend/pnpm-lock.yaml new file mode 100644 index 0000000..03a539a --- /dev/null +++ b/studio-frontend/pnpm-lock.yaml @@ -0,0 +1,23520 @@ +lockfileVersion: '9.0' + +settings: + autoInstallPeers: true + excludeLinksFromLockfile: false + +overrides: + '@google/generative-ai': ^0.24.0 + '@grpc/grpc-js': ^1.10.10 + '@langchain/core': 0.3.61 + '@qdrant/openapi-typescript-fetch': 1.2.6 + openai: 4.96.0 + protobufjs: 7.4.0 + '@modelcontextprotocol/sdk': '>=1.24.0' + axios: 1.12.0 + body-parser: 2.0.2 + braces: 3.0.3 + cross-spawn: 7.0.6 + esbuild: 0.27.1 + form-data: 4.0.4 + glob: 10.5.0 + glob-parent: 6.0.2 + http-proxy-middleware: 3.0.3 + json5: 2.2.3 + nth-check: 2.1.1 + path-to-regexp: 0.1.12 + prismjs: 1.29.0 + rollup: 4.45.0 + semver: 7.7.1 + set-value: 4.1.0 + solid-js: 1.9.7 + tar-fs: '>=3.1.1' + unset-value: 2.0.1 + webpack-dev-middleware: 7.4.2 + ws: 8.18.3 + xlsx: https://cdn.sheetjs.com/xlsx-0.20.3/xlsx-0.20.3.tgz + zod: ^3.25.0 + +importers: + + .: + dependencies: + '@opentelemetry/exporter-trace-otlp-grpc': + specifier: ^0.57.2 + version: 0.57.2(@opentelemetry/api@1.9.0) + '@opentelemetry/exporter-trace-otlp-proto': + specifier: ^0.57.2 + version: 0.57.2(@opentelemetry/api@1.9.0) + '@opentelemetry/sdk-trace-node': + specifier: ^1.30.1 + version: 1.30.1(@opentelemetry/api@1.9.0) + react-toastify: + specifier: ^11.0.5 + version: 11.0.5(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + devDependencies: + '@babel/preset-env': + specifier: ^7.19.4 + version: 7.28.3(@babel/core@7.28.4) + '@babel/preset-typescript': + specifier: 7.18.6 + version: 7.18.6(@babel/core@7.28.4) + '@types/express': + specifier: ^4.17.13 + version: 4.17.23 + '@typescript-eslint/typescript-estree': + specifier: ^7.13.1 + version: 7.18.0(typescript@5.9.3) + cross-spawn: + specifier: 7.0.6 + version: 7.0.6 + eslint: + specifier: ^8.24.0 + version: 8.57.1 + eslint-config-prettier: + specifier: ^8.3.0 + version: 8.10.2(eslint@8.57.1) + eslint-config-react-app: + specifier: ^7.0.1 + version: 7.0.1(@babel/plugin-syntax-flow@7.27.1(@babel/core@7.28.4))(@babel/plugin-transform-react-jsx@7.27.1(@babel/core@7.28.4))(eslint@8.57.1)(jest@27.5.1(bufferutil@4.0.9)(canvas@2.11.2(encoding@0.1.13))(ts-node@10.9.2(@types/node@24.7.2)(typescript@5.9.3))(utf-8-validate@6.0.5))(typescript@5.9.3) + eslint-plugin-jsx-a11y: + specifier: ^6.6.1 + version: 6.10.2(eslint@8.57.1) + eslint-plugin-markdown: + specifier: ^3.0.0 + version: 3.0.1(eslint@8.57.1) + eslint-plugin-prettier: + specifier: ^3.4.0 + version: 3.4.1(eslint-config-prettier@8.10.2(eslint@8.57.1))(eslint@8.57.1)(prettier@2.8.8) + eslint-plugin-react: + specifier: ^7.26.1 + version: 7.37.5(eslint@8.57.1) + eslint-plugin-react-hooks: + specifier: ^4.6.0 + version: 4.6.2(eslint@8.57.1) + eslint-plugin-unused-imports: + specifier: ^2.0.0 + version: 2.0.0(@typescript-eslint/eslint-plugin@5.62.0(@typescript-eslint/parser@5.62.0(eslint@8.57.1)(typescript@5.9.3))(eslint@8.57.1)(typescript@5.9.3))(eslint@8.57.1) + glob: + specifier: 10.5.0 + version: 10.5.0 + husky: + specifier: ^8.0.1 + version: 8.0.3 + kill-port: + specifier: ^2.0.1 + version: 2.0.1 + lint-staged: + specifier: ^13.0.3 + version: 13.3.0(enquirer@2.4.1) + prettier: + specifier: ^2.7.1 + version: 2.8.8 + pretty-quick: + specifier: ^3.1.3 + version: 3.3.1(prettier@2.8.8) + rimraf: + specifier: ^3.0.2 + version: 3.0.2 + run-script-os: + specifier: ^1.1.6 + version: 1.1.6 + tar-fs: + specifier: '>=3.1.1' + version: 3.1.1 + turbo: + specifier: ^2.3.3 + version: 2.5.8 + typescript: + specifier: ^5.4.5 + version: 5.9.3 + + packages/server: + dependencies: + '@types/lodash': + specifier: ^4.17.20 + version: 4.17.20 + '@types/uuid': + specifier: ^9.0.7 + version: 9.0.8 + archiver: + specifier: ^6.0.1 + version: 6.0.2 + async-mutex: + specifier: ^0.4.0 + version: 0.4.1 + axios: + specifier: 1.12.0 + version: 1.12.0(debug@4.4.3) + content-disposition: + specifier: 0.5.4 + version: 0.5.4 + cors: + specifier: ^2.8.5 + version: 2.8.5 + crypto-js: + specifier: ^4.1.1 + version: 4.2.0 + dotenv: + specifier: ^16.0.0 + version: 16.6.1 + express: + specifier: ^4.17.3 + version: 4.21.2 + express-basic-auth: + specifier: ^1.2.1 + version: 1.2.1 + express-rate-limit: + specifier: ^6.9.0 + version: 6.11.2(express@4.21.2) + flowise-ui: + specifier: workspace:^ + version: link:../ui + form-data: + specifier: 4.0.4 + version: 4.0.4 + http-errors: + specifier: ^2.0.0 + version: 2.0.0 + http-status-codes: + specifier: ^2.3.0 + version: 2.3.0 + https-proxy-agent: + specifier: ^7.0.4 + version: 7.0.6 + lodash: + specifier: ^4.17.21 + version: 4.17.21 + moment: + specifier: ^2.29.3 + version: 2.30.1 + moment-timezone: + specifier: ^0.5.34 + version: 0.5.48 + multer: + specifier: ^1.4.5-lts.1 + version: 1.4.5-lts.2 + mysql2: + specifier: ^3.11.3 + version: 3.15.2 + pg: + specifier: ^8.11.1 + version: 8.16.3 + reflect-metadata: + specifier: ^0.1.13 + version: 0.1.14 + sanitize-html: + specifier: ^2.11.0 + version: 2.17.0 + socket.io: + specifier: ^4.6.1 + version: 4.8.1(bufferutil@4.0.9)(utf-8-validate@6.0.5) + sqlite3: + specifier: ^5.1.6 + version: 5.1.7 + typeorm: + specifier: ^0.3.6 + version: 0.3.27(babel-plugin-macros@3.1.0)(ioredis@5.8.1)(mongodb@6.20.0(@aws-sdk/credential-providers@3.908.0)(socks@2.8.7))(mysql2@3.15.2)(pg@8.16.3)(redis@4.7.1)(reflect-metadata@0.1.14)(sqlite3@5.1.7)(ts-node@10.9.2(@types/node@24.7.2)(typescript@5.9.3)) + uuid: + specifier: ^9.0.1 + version: 9.0.1 + winston: + specifier: ^3.9.0 + version: 3.18.3 + winston-daily-rotate-file: + specifier: ^5.0.0 + version: 5.0.0(winston@3.18.3) + devDependencies: + '@types/archiver': + specifier: ^6.0.2 + version: 6.0.4 + '@types/content-disposition': + specifier: 0.5.8 + version: 0.5.8 + '@types/cors': + specifier: ^2.8.12 + version: 2.8.19 + '@types/crypto-js': + specifier: ^4.1.1 + version: 4.2.2 + '@types/express': + specifier: ^4.17.17 + version: 4.17.23 + '@types/multer': + specifier: ^1.4.7 + version: 1.4.13 + '@types/sanitize-html': + specifier: ^2.9.5 + version: 2.16.0 + concurrently: + specifier: ^7.1.0 + version: 7.6.0 + cypress: + specifier: ^13.13.0 + version: 13.17.0 + nodemon: + specifier: ^2.0.22 + version: 2.0.22 + rimraf: + specifier: ^5.0.5 + version: 5.0.10 + run-script-os: + specifier: ^1.1.6 + version: 1.1.6 + shx: + specifier: ^0.3.3 + version: 0.3.4 + start-server-and-test: + specifier: ^2.0.3 + version: 2.1.2 + ts-node: + specifier: ^10.7.0 + version: 10.9.2(@types/node@24.7.2)(typescript@5.9.3) + tsc-watch: + specifier: ^6.0.4 + version: 6.3.1(typescript@5.9.3) + typescript: + specifier: ^5.4.5 + version: 5.9.3 + + packages/ui: + dependencies: + '@codemirror/lang-javascript': + specifier: ^6.2.1 + version: 6.2.4 + '@codemirror/lang-json': + specifier: ^6.0.1 + version: 6.0.2 + '@codemirror/lang-markdown': + specifier: ^6.2.5 + version: 6.5.0 + '@codemirror/view': + specifier: ^6.26.3 + version: 6.38.5 + '@emotion/cache': + specifier: ^11.4.0 + version: 11.14.0 + '@emotion/react': + specifier: ^11.10.6 + version: 11.14.0(@types/react@18.3.26)(react@18.3.1) + '@emotion/styled': + specifier: ^11.10.6 + version: 11.14.1(@emotion/react@11.14.0(@types/react@18.3.26)(react@18.3.1))(@types/react@18.3.26)(react@18.3.1) + '@lezer/highlight': + specifier: ^1.2.1 + version: 1.2.1 + '@microsoft/fetch-event-source': + specifier: ^2.0.1 + version: 2.0.1 + '@mui/base': + specifier: 5.0.0-beta.40 + version: 5.0.0-beta.40(@types/react@18.3.26)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@mui/icons-material': + specifier: ^5.15.0 + version: 5.18.0(@mui/material@5.15.0(@emotion/react@11.14.0(@types/react@18.3.26)(react@18.3.1))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@18.3.26)(react@18.3.1))(@types/react@18.3.26)(react@18.3.1))(@types/react@18.3.26)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(@types/react@18.3.26)(react@18.3.1) + '@mui/lab': + specifier: 5.0.0-alpha.156 + version: 5.0.0-alpha.156(@emotion/react@11.14.0(@types/react@18.3.26)(react@18.3.1))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@18.3.26)(react@18.3.1))(@types/react@18.3.26)(react@18.3.1))(@mui/material@5.15.0(@emotion/react@11.14.0(@types/react@18.3.26)(react@18.3.1))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@18.3.26)(react@18.3.1))(@types/react@18.3.26)(react@18.3.1))(@types/react@18.3.26)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(@types/react@18.3.26)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@mui/material': + specifier: 5.15.0 + version: 5.15.0(@emotion/react@11.14.0(@types/react@18.3.26)(react@18.3.1))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@18.3.26)(react@18.3.1))(@types/react@18.3.26)(react@18.3.1))(@types/react@18.3.26)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@mui/system': + specifier: ^5.15.0 + version: 5.18.0(@emotion/react@11.14.0(@types/react@18.3.26)(react@18.3.1))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@18.3.26)(react@18.3.1))(@types/react@18.3.26)(react@18.3.1))(@types/react@18.3.26)(react@18.3.1) + '@mui/x-data-grid': + specifier: 6.8.0 + version: 6.8.0(@mui/material@5.15.0(@emotion/react@11.14.0(@types/react@18.3.26)(react@18.3.1))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@18.3.26)(react@18.3.1))(@types/react@18.3.26)(react@18.3.1))(@types/react@18.3.26)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(@mui/system@5.18.0(@emotion/react@11.14.0(@types/react@18.3.26)(react@18.3.1))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@18.3.26)(react@18.3.1))(@types/react@18.3.26)(react@18.3.1))(@types/react@18.3.26)(react@18.3.1))(@types/react@18.3.26)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@mui/x-tree-view': + specifier: ^6.17.0 + version: 6.17.0(@emotion/react@11.14.0(@types/react@18.3.26)(react@18.3.1))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@18.3.26)(react@18.3.1))(@types/react@18.3.26)(react@18.3.1))(@mui/material@5.15.0(@emotion/react@11.14.0(@types/react@18.3.26)(react@18.3.1))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@18.3.26)(react@18.3.1))(@types/react@18.3.26)(react@18.3.1))(@types/react@18.3.26)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(@mui/system@5.18.0(@emotion/react@11.14.0(@types/react@18.3.26)(react@18.3.1))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@18.3.26)(react@18.3.1))(@types/react@18.3.26)(react@18.3.1))(@types/react@18.3.26)(react@18.3.1))(@types/react@18.3.26)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@opentelemetry/exporter-trace-otlp-grpc': + specifier: latest + version: 0.214.0(@opentelemetry/api@1.9.0) + '@opentelemetry/exporter-trace-otlp-proto': + specifier: latest + version: 0.214.0(@opentelemetry/api@1.9.0) + '@opentelemetry/sdk-trace-node': + specifier: latest + version: 2.6.1(@opentelemetry/api@1.9.0) + '@react-keycloak/web': + specifier: ^3.4.0 + version: 3.4.0(keycloak-js@26.2.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.9.3) + '@reduxjs/toolkit': + specifier: ^2.2.7 + version: 2.11.2(react-redux@8.1.3(@types/react-dom@18.3.7(@types/react@18.3.26))(@types/react@18.3.26)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(redux@4.2.1))(react@18.3.1) + '@tabler/icons-react': + specifier: ^3.30.0 + version: 3.41.1(react@18.3.1) + '@uiw/codemirror-theme-sublime': + specifier: ^4.21.21 + version: 4.25.2(@codemirror/language@6.11.3)(@codemirror/state@6.5.2)(@codemirror/view@6.38.5) + '@uiw/codemirror-theme-vscode': + specifier: ^4.21.21 + version: 4.25.2(@codemirror/language@6.11.3)(@codemirror/state@6.5.2)(@codemirror/view@6.38.5) + '@uiw/react-codemirror': + specifier: ^4.21.21 + version: 4.25.2(@babel/runtime@7.28.4)(@codemirror/autocomplete@6.19.0)(@codemirror/language@6.11.3)(@codemirror/lint@6.9.0)(@codemirror/search@6.5.11)(@codemirror/state@6.5.2)(@codemirror/theme-one-dark@6.1.3)(@codemirror/view@6.38.5)(codemirror@6.0.2)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + axios: + specifier: 1.12.0 + version: 1.12.0(debug@4.4.3) + clsx: + specifier: ^1.1.1 + version: 1.2.1 + dompurify: + specifier: ^3.2.6 + version: 3.3.3 + dotenv: + specifier: ^16.0.0 + version: 16.6.1 + flowise-embed: + specifier: latest + version: 3.1.5 + flowise-embed-react: + specifier: latest + version: 3.1.5(@types/node@24.7.2)(flowise-embed@3.1.5)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.93.2)(terser@5.44.0)(typescript@5.9.3) + flowise-react-json-view: + specifier: '*' + version: 1.21.7(@types/react@18.3.26)(encoding@0.1.13)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + formik: + specifier: ^2.2.6 + version: 2.4.6(@types/react@18.3.26)(react@18.3.1) + framer-motion: + specifier: ^4.1.13 + version: 4.1.17(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + html-react-parser: + specifier: ^3.0.4 + version: 3.0.16(react@18.3.1) + keycloak-js: + specifier: ^26.0.5 + version: 26.2.1 + lodash: + specifier: ^4.17.21 + version: 4.17.21 + lowlight: + specifier: ^3.3.0 + version: 3.3.0 + moment: + specifier: ^2.29.3 + version: 2.30.1 + notistack: + specifier: ^2.0.4 + version: 2.0.8(@emotion/react@11.14.0(@types/react@18.3.26)(react@18.3.1))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@18.3.26)(react@18.3.1))(@types/react@18.3.26)(react@18.3.1))(@mui/material@5.15.0(@emotion/react@11.14.0(@types/react@18.3.26)(react@18.3.1))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@18.3.26)(react@18.3.1))(@types/react@18.3.26)(react@18.3.1))(@types/react@18.3.26)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + prop-types: + specifier: ^15.7.2 + version: 15.8.1 + react: + specifier: ^18.2.0 + version: 18.3.1 + react-code-blocks: + specifier: ^0.1.6 + version: 0.1.6(css-to-react-native@3.2.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + react-color: + specifier: ^2.19.3 + version: 2.19.3(react@18.3.1) + react-datepicker: + specifier: ^4.21.0 + version: 4.25.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + react-device-detect: + specifier: ^1.17.0 + version: 1.17.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + react-dom: + specifier: ^18.2.0 + version: 18.3.1(react@18.3.1) + react-markdown: + specifier: ^8.0.6 + version: 8.0.7(@types/react@18.3.26)(react@18.3.1) + react-perfect-scrollbar: + specifier: ^1.5.8 + version: 1.5.8(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + react-redux: + specifier: ^8.0.5 + version: 8.1.3(@types/react-dom@18.3.7(@types/react@18.3.26))(@types/react@18.3.26)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(redux@4.2.1) + react-router: + specifier: ~6.3.0 + version: 6.3.0(react@18.3.1) + react-router-dom: + specifier: ~6.3.0 + version: 6.3.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + react-syntax-highlighter: + specifier: ^15.5.0 + version: 15.6.6(react@18.3.1) + reactflow: + specifier: ^11.5.6 + version: 11.11.4(@types/react@18.3.26)(immer@11.1.4)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + recharts: + specifier: ^2.12.6 + version: 2.15.4(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + redux: + specifier: ^4.0.5 + version: 4.2.1 + rehype-mathjax: + specifier: ^4.0.2 + version: 4.0.3(bufferutil@4.0.9)(canvas@2.11.2(encoding@0.1.13))(utf-8-validate@6.0.5) + remark-gfm: + specifier: ^3.0.1 + version: 3.0.1 + remark-math: + specifier: ^5.1.1 + version: 5.1.1 + socket.io-client: + specifier: ^4.6.1 + version: 4.8.1(bufferutil@4.0.9)(utf-8-validate@6.0.5) + uuid: + specifier: ^9.0.1 + version: 9.0.1 + yup: + specifier: ^0.32.9 + version: 0.32.11 + devDependencies: + '@babel/eslint-parser': + specifier: ^7.15.8 + version: 7.28.4(@babel/core@7.28.4)(eslint@8.57.1) + '@babel/plugin-proposal-private-property-in-object': + specifier: ^7.21.11 + version: 7.21.11(@babel/core@7.28.4) + '@testing-library/jest-dom': + specifier: ^5.11.10 + version: 5.17.0 + '@testing-library/react': + specifier: ^14.0.0 + version: 14.3.1(@types/react@18.3.26)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@testing-library/user-event': + specifier: ^12.8.3 + version: 12.8.3(@testing-library/dom@9.3.4) + '@vitejs/plugin-react': + specifier: ^4.2.0 + version: 4.7.0(vite@5.4.20(@types/node@24.7.2)(sass@1.93.2)(terser@5.44.0)) + pretty-quick: + specifier: ^3.1.3 + version: 3.3.1(prettier@3.6.2) + react-scripts: + specifier: ^5.0.1 + version: 5.0.1(@babel/plugin-syntax-flow@7.27.1(@babel/core@7.28.4))(@babel/plugin-transform-react-jsx@7.27.1(@babel/core@7.28.4))(@types/babel__core@7.20.5)(bufferutil@4.0.9)(canvas@2.11.2(encoding@0.1.13))(eslint@8.57.1)(react@18.3.1)(sass@1.93.2)(ts-node@10.9.2(@types/node@24.7.2)(typescript@5.9.3))(tslib@2.8.1)(type-fest@4.41.0)(typescript@5.9.3)(utf-8-validate@6.0.5)(yaml@2.8.1) + rimraf: + specifier: ^5.0.5 + version: 5.0.10 + sass: + specifier: ^1.42.1 + version: 1.93.2 + typescript: + specifier: ^5.4.5 + version: 5.9.3 + vite: + specifier: ^5.0.2 + version: 5.4.20(@types/node@24.7.2)(sass@1.93.2)(terser@5.44.0) + vite-plugin-pwa: + specifier: ^0.17.0 + version: 0.17.5(vite@5.4.20(@types/node@24.7.2)(sass@1.93.2)(terser@5.44.0))(workbox-build@6.6.0(@types/babel__core@7.20.5))(workbox-window@6.6.0) + vite-plugin-react-js-support: + specifier: ^1.0.7 + version: 1.0.7 + +packages: + + '@adobe/css-tools@4.4.4': + resolution: {integrity: sha512-Elp+iwUx5rN5+Y8xLt5/GRoG20WGoDCQ/1Fb+1LiGtvwbDavuSk0jhD/eZdckHAuzcDzccnkv+rEjyWfRx18gg==} + + '@alloc/quick-lru@5.2.0': + resolution: {integrity: sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw==} + engines: {node: '>=10'} + + '@apideck/better-ajv-errors@0.3.6': + resolution: {integrity: sha512-P+ZygBLZtkp0qqOAJJVX4oX/sFo5JR3eBWwwuqHHhK0GIgQOKWrAfiAaWX0aArHkRWHMuggFEgAZNxVPwPZYaA==} + engines: {node: '>=10'} + peerDependencies: + ajv: '>=8' + + '@aws-crypto/sha256-browser@5.2.0': + resolution: {integrity: sha512-AXfN/lGotSQwu6HNcEsIASo7kWXZ5HYWvfOmSNKDsEqC4OashTp8alTmaz+F7TC2L083SFv5RdB+qU3Vs1kZqw==} + + '@aws-crypto/sha256-js@5.2.0': + resolution: {integrity: sha512-FFQQyu7edu4ufvIZ+OadFpHHOt+eSTBaYaki44c+akjg7qZg9oOQeLlk77F6tSYqjDAFClrHJk9tMf0HdVyOvA==} + engines: {node: '>=16.0.0'} + + '@aws-crypto/supports-web-crypto@5.2.0': + resolution: {integrity: sha512-iAvUotm021kM33eCdNfwIN//F77/IADDSs58i+MDaOqFrVjZo9bAal0NK7HurRuWLLpF1iLX7gbWrjHjeo+YFg==} + + '@aws-crypto/util@5.2.0': + resolution: {integrity: sha512-4RkU9EsI6ZpBve5fseQlGNUWKMa1RLPQ1dnjnQoe07ldfIzcsGb5hC5W0Dm7u423KWzawlrpbjXBrXCEv9zazQ==} + + '@aws-sdk/client-cognito-identity@3.908.0': + resolution: {integrity: sha512-XEva6l07dtF+6QNzTZHB+PEOX03sdcOudh+XVa4UjlUPlqrl/LAk/MzRxbw6pHScG5DPMx6Iyeicm7pJBOTdYg==} + engines: {node: '>=18.0.0'} + + '@aws-sdk/client-sso@3.908.0': + resolution: {integrity: sha512-PseFMWvtac+Q+zaY9DMISE+2+glNh0ROJ1yR4gMzeafNHSwkdYu4qcgxLWIOnIodGydBv/tQ6nzHPzExXnUUgw==} + engines: {node: '>=18.0.0'} + + '@aws-sdk/core@3.908.0': + resolution: {integrity: sha512-okl6FC2cQT1Oidvmnmvyp/IEvqENBagKO0ww4YV5UtBkf0VlhAymCWkZqhovtklsqgq0otag2VRPAgnrMt6nVQ==} + engines: {node: '>=18.0.0'} + + '@aws-sdk/credential-provider-cognito-identity@3.908.0': + resolution: {integrity: sha512-Lmb5GatPNDY5cODfwH3XGWpKZqg/lh7ghCn4Y9BTMi5W9Z/E2Jq5YF7yaLobOjOpx+lVoxS+EsVrtSm6p3ffuw==} + engines: {node: '>=18.0.0'} + + '@aws-sdk/credential-provider-env@3.908.0': + resolution: {integrity: sha512-FK2YuxoI5CxUflPOIMbVAwDbi6Xvu+2sXopXLmrHc2PfI39M3vmjEoQwYCP8WuQSRb+TbAP3xAkxHjFSBFR35w==} + engines: {node: '>=18.0.0'} + + '@aws-sdk/credential-provider-http@3.908.0': + resolution: {integrity: sha512-eLbz0geVW9EykujQNnYfR35Of8MreI6pau5K6XDFDUSWO9GF8wqH7CQwbXpXHBlCTHtq4QSLxzorD8U5CROhUw==} + engines: {node: '>=18.0.0'} + + '@aws-sdk/credential-provider-ini@3.908.0': + resolution: {integrity: sha512-7Cgnv5wabgFtsgr+Uc/76EfPNGyxmbG8aICn3g3D3iJlcO4uuOZI8a77i0afoDdchZrTC6TG6UusS/NAW6zEoQ==} + engines: {node: '>=18.0.0'} + + '@aws-sdk/credential-provider-node@3.908.0': + resolution: {integrity: sha512-8OKbykpGw5bdfF/pLTf8YfUi1Kl8o1CTjBqWQTsLOkE3Ho3hsp1eQx8Cz4ttrpv0919kb+lox62DgmAOEmTr1w==} + engines: {node: '>=18.0.0'} + + '@aws-sdk/credential-provider-process@3.908.0': + resolution: {integrity: sha512-sWnbkGjDPBi6sODUzrAh5BCDpnPw0wpK8UC/hWI13Q8KGfyatAmCBfr+9OeO3+xBHa8N5AskMncr7C4qS846yQ==} + engines: {node: '>=18.0.0'} + + '@aws-sdk/credential-provider-sso@3.908.0': + resolution: {integrity: sha512-WV/aOzuS6ZZhrkPty6TJ3ZG24iS8NXP0m3GuTVuZ5tKi9Guss31/PJ1CrKPRCYGm15CsIjf+mrUxVnNYv9ap5g==} + engines: {node: '>=18.0.0'} + + '@aws-sdk/credential-provider-web-identity@3.908.0': + resolution: {integrity: sha512-9xWrFn6nWlF5KlV4XYW+7E6F33S3wUUEGRZ/+pgDhkIZd527ycT2nPG2dZ3fWUZMlRmzijP20QIJDqEbbGWe1Q==} + engines: {node: '>=18.0.0'} + + '@aws-sdk/credential-providers@3.908.0': + resolution: {integrity: sha512-WuAttxDemeKyMAgNEwBdgz11/W8IOosDxAU9GOk8ZbNJI/Cp0SiCp5p9bu0hlroq2V4HxECIRIMNeJZLh7fIgg==} + engines: {node: '>=18.0.0'} + + '@aws-sdk/middleware-host-header@3.901.0': + resolution: {integrity: sha512-yWX7GvRmqBtbNnUW7qbre3GvZmyYwU0WHefpZzDTYDoNgatuYq6LgUIQ+z5C04/kCRoFkAFrHag8a3BXqFzq5A==} + engines: {node: '>=18.0.0'} + + '@aws-sdk/middleware-logger@3.901.0': + resolution: {integrity: sha512-UoHebjE7el/tfRo8/CQTj91oNUm+5Heus5/a4ECdmWaSCHCS/hXTsU3PTTHAY67oAQR8wBLFPfp3mMvXjB+L2A==} + engines: {node: '>=18.0.0'} + + '@aws-sdk/middleware-recursion-detection@3.901.0': + resolution: {integrity: sha512-Wd2t8qa/4OL0v/oDpCHHYkgsXJr8/ttCxrvCKAt0H1zZe2LlRhY9gpDVKqdertfHrHDj786fOvEQA28G1L75Dg==} + engines: {node: '>=18.0.0'} + + '@aws-sdk/middleware-user-agent@3.908.0': + resolution: {integrity: sha512-R0ePEOku72EvyJWy/D0Z5f/Ifpfxa0U9gySO3stpNhOox87XhsILpcIsCHPy0OHz1a7cMoZsF6rMKSzDeCnogQ==} + engines: {node: '>=18.0.0'} + + '@aws-sdk/nested-clients@3.908.0': + resolution: {integrity: sha512-ZxDYrfxOKXNFHLyvJtT96TJ0p4brZOhwRE4csRXrezEVUN+pNgxuem95YvMALPVhlVqON2CTzr8BX+CcBKvX9Q==} + engines: {node: '>=18.0.0'} + + '@aws-sdk/region-config-resolver@3.901.0': + resolution: {integrity: sha512-7F0N888qVLHo4CSQOsnkZ4QAp8uHLKJ4v3u09Ly5k4AEStrSlFpckTPyUx6elwGL+fxGjNE2aakK8vEgzzCV0A==} + engines: {node: '>=18.0.0'} + + '@aws-sdk/token-providers@3.908.0': + resolution: {integrity: sha512-4SosHWRQ8hj1X2yDenCYHParcCjHcd7S+Mdb/lelwF0JBFCNC+dNCI9ws3cP/dFdZO/AIhJQGUBzEQtieloixw==} + engines: {node: '>=18.0.0'} + + '@aws-sdk/types@3.901.0': + resolution: {integrity: sha512-FfEM25hLEs4LoXsLXQ/q6X6L4JmKkKkbVFpKD4mwfVHtRVQG6QxJiCPcrkcPISquiy6esbwK2eh64TWbiD60cg==} + engines: {node: '>=18.0.0'} + + '@aws-sdk/types@3.973.7': + resolution: {integrity: sha512-reXRwoJ6CfChoqAsBszUYajAF8Z2LRE+CRcKocvFSMpIiLOtYU3aJ9trmn6VVPAzbbY5LXF+FfmUslbXk1SYFg==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/util-endpoints@3.901.0': + resolution: {integrity: sha512-5nZP3hGA8FHEtKvEQf4Aww5QZOkjLW1Z+NixSd+0XKfHvA39Ah5sZboScjLx0C9kti/K3OGW1RCx5K9Zc3bZqg==} + engines: {node: '>=18.0.0'} + + '@aws-sdk/util-locate-window@3.893.0': + resolution: {integrity: sha512-T89pFfgat6c8nMmpI8eKjBcDcgJq36+m9oiXbcUzeU55MP9ZuGgBomGjGnHaEyF36jenW9gmg3NfZDm0AO2XPg==} + engines: {node: '>=18.0.0'} + + '@aws-sdk/util-user-agent-browser@3.907.0': + resolution: {integrity: sha512-Hus/2YCQmtCEfr4Ls88d07Q99Ex59uvtktiPTV963Q7w7LHuIT/JBjrbwNxtSm2KlJR9PHNdqxwN+fSuNsMGMQ==} + + '@aws-sdk/util-user-agent-node@3.908.0': + resolution: {integrity: sha512-l6AEaKUAYarcEy8T8NZ+dNZ00VGLs3fW2Cqu1AuPENaSad0/ahEU+VU7MpXS8FhMRGPgplxKVgCTLyTY0Lbssw==} + engines: {node: '>=18.0.0'} + peerDependencies: + aws-crt: '>=1.0.0' + peerDependenciesMeta: + aws-crt: + optional: true + + '@aws-sdk/xml-builder@3.901.0': + resolution: {integrity: sha512-pxFCkuAP7Q94wMTNPAwi6hEtNrp/BdFf+HOrIEeFQsk4EoOmpKY3I6S+u6A9Wg295J80Kh74LqDWM22ux3z6Aw==} + engines: {node: '>=18.0.0'} + + '@aws/lambda-invoke-store@0.0.1': + resolution: {integrity: sha512-ORHRQ2tmvnBXc8t/X9Z8IcSbBA4xTLKuN873FopzklHMeqBst7YG0d+AX97inkvDX+NChYtSr+qGfcqGFaI8Zw==} + engines: {node: '>=18.0.0'} + + '@babel/code-frame@7.27.1': + resolution: {integrity: sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg==} + engines: {node: '>=6.9.0'} + + '@babel/compat-data@7.28.4': + resolution: {integrity: sha512-YsmSKC29MJwf0gF8Rjjrg5LQCmyh+j/nD8/eP7f+BeoQTKYqs9RoWbjGOdy0+1Ekr68RJZMUOPVQaQisnIo4Rw==} + engines: {node: '>=6.9.0'} + + '@babel/core@7.28.4': + resolution: {integrity: sha512-2BCOP7TN8M+gVDj7/ht3hsaO/B/n5oDbiAyyvnRlNOs+u1o+JWNYTQrmpuNp1/Wq2gcFrI01JAW+paEKDMx/CA==} + engines: {node: '>=6.9.0'} + + '@babel/eslint-parser@7.28.4': + resolution: {integrity: sha512-Aa+yDiH87980jR6zvRfFuCR1+dLb00vBydhTL+zI992Rz/wQhSvuxjmOOuJOgO3XmakO6RykRGD2S1mq1AtgHA==} + engines: {node: ^10.13.0 || ^12.13.0 || >=14.0.0} + peerDependencies: + '@babel/core': ^7.11.0 + eslint: ^7.5.0 || ^8.0.0 || ^9.0.0 + + '@babel/generator@7.28.3': + resolution: {integrity: sha512-3lSpxGgvnmZznmBkCRnVREPUFJv2wrv9iAoFDvADJc0ypmdOxdUtcLeBgBJ6zE0PMeTKnxeQzyk0xTBq4Ep7zw==} + engines: {node: '>=6.9.0'} + + '@babel/helper-annotate-as-pure@7.27.3': + resolution: {integrity: sha512-fXSwMQqitTGeHLBC08Eq5yXz2m37E4pJX1qAU1+2cNedz/ifv/bVXft90VeSav5nFO61EcNgwr0aJxbyPaWBPg==} + engines: {node: '>=6.9.0'} + + '@babel/helper-compilation-targets@7.27.2': + resolution: {integrity: sha512-2+1thGUUWWjLTYTHZWK1n8Yga0ijBz1XAhUXcKy81rd5g6yh7hGqMp45v7cadSbEHc9G3OTv45SyneRN3ps4DQ==} + engines: {node: '>=6.9.0'} + + '@babel/helper-create-class-features-plugin@7.28.3': + resolution: {integrity: sha512-V9f6ZFIYSLNEbuGA/92uOvYsGCJNsuA8ESZ4ldc09bWk/j8H8TKiPw8Mk1eG6olpnO0ALHJmYfZvF4MEE4gajg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + + '@babel/helper-create-regexp-features-plugin@7.27.1': + resolution: {integrity: sha512-uVDC72XVf8UbrH5qQTc18Agb8emwjTiZrQE11Nv3CuBEZmVvTwwE9CBUEvHku06gQCAyYf8Nv6ja1IN+6LMbxQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + + '@babel/helper-define-polyfill-provider@0.6.5': + resolution: {integrity: sha512-uJnGFcPsWQK8fvjgGP5LZUZZsYGIoPeRjSF5PGwrelYgq7Q15/Ft9NGFp1zglwgIv//W0uG4BevRuSJRyylZPg==} + peerDependencies: + '@babel/core': ^7.4.0 || ^8.0.0-0 <8.0.0 + + '@babel/helper-globals@7.28.0': + resolution: {integrity: sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw==} + engines: {node: '>=6.9.0'} + + '@babel/helper-member-expression-to-functions@7.27.1': + resolution: {integrity: sha512-E5chM8eWjTp/aNoVpcbfM7mLxu9XGLWYise2eBKGQomAk/Mb4XoxyqXTZbuTohbsl8EKqdlMhnDI2CCLfcs9wA==} + engines: {node: '>=6.9.0'} + + '@babel/helper-module-imports@7.27.1': + resolution: {integrity: sha512-0gSFWUPNXNopqtIPQvlD5WgXYI5GY2kP2cCvoT8kczjbfcfuIljTbcWrulD1CIPIX2gt1wghbDy08yE1p+/r3w==} + engines: {node: '>=6.9.0'} + + '@babel/helper-module-transforms@7.28.3': + resolution: {integrity: sha512-gytXUbs8k2sXS9PnQptz5o0QnpLL51SwASIORY6XaBKF88nsOT0Zw9szLqlSGQDP/4TljBAD5y98p2U1fqkdsw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + + '@babel/helper-optimise-call-expression@7.27.1': + resolution: {integrity: sha512-URMGH08NzYFhubNSGJrpUEphGKQwMQYBySzat5cAByY1/YgIRkULnIy3tAMeszlL/so2HbeilYloUmSpd7GdVw==} + engines: {node: '>=6.9.0'} + + '@babel/helper-plugin-utils@7.27.1': + resolution: {integrity: sha512-1gn1Up5YXka3YYAHGKpbideQ5Yjf1tDa9qYcgysz+cNCXukyLl6DjPXhD3VRwSb8c0J9tA4b2+rHEZtc6R0tlw==} + engines: {node: '>=6.9.0'} + + '@babel/helper-remap-async-to-generator@7.27.1': + resolution: {integrity: sha512-7fiA521aVw8lSPeI4ZOD3vRFkoqkJcS+z4hFo82bFSH/2tNd6eJ5qCVMS5OzDmZh/kaHQeBaeyxK6wljcPtveA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + + '@babel/helper-replace-supers@7.27.1': + resolution: {integrity: sha512-7EHz6qDZc8RYS5ElPoShMheWvEgERonFCs7IAonWLLUTXW59DP14bCZt89/GKyreYn8g3S83m21FelHKbeDCKA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + + '@babel/helper-skip-transparent-expression-wrappers@7.27.1': + resolution: {integrity: sha512-Tub4ZKEXqbPjXgWLl2+3JpQAYBJ8+ikpQ2Ocj/q/r0LwE3UhENh7EUabyHjz2kCEsrRY83ew2DQdHluuiDQFzg==} + engines: {node: '>=6.9.0'} + + '@babel/helper-string-parser@7.27.1': + resolution: {integrity: sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==} + engines: {node: '>=6.9.0'} + + '@babel/helper-validator-identifier@7.27.1': + resolution: {integrity: sha512-D2hP9eA+Sqx1kBZgzxZh0y1trbuU+JoDkiEwqhQ36nodYqJwyEIhPSdMNd7lOm/4io72luTPWH20Yda0xOuUow==} + engines: {node: '>=6.9.0'} + + '@babel/helper-validator-option@7.27.1': + resolution: {integrity: sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==} + engines: {node: '>=6.9.0'} + + '@babel/helper-wrap-function@7.28.3': + resolution: {integrity: sha512-zdf983tNfLZFletc0RRXYrHrucBEg95NIFMkn6K9dbeMYnsgHaSBGcQqdsCSStG2PYwRre0Qc2NNSCXbG+xc6g==} + engines: {node: '>=6.9.0'} + + '@babel/helpers@7.28.4': + resolution: {integrity: sha512-HFN59MmQXGHVyYadKLVumYsA9dBFun/ldYxipEjzA4196jpLZd8UjEEBLkbEkvfYreDqJhZxYAWFPtrfhNpj4w==} + engines: {node: '>=6.9.0'} + + '@babel/parser@7.28.4': + resolution: {integrity: sha512-yZbBqeM6TkpP9du/I2pUZnJsRMGGvOuIrhjzC1AwHwW+6he4mni6Bp/m8ijn0iOuZuPI2BfkCoSRunpyjnrQKg==} + engines: {node: '>=6.0.0'} + hasBin: true + + '@babel/plugin-bugfix-firefox-class-in-computed-class-key@7.27.1': + resolution: {integrity: sha512-QPG3C9cCVRQLxAVwmefEmwdTanECuUBMQZ/ym5kiw3XKCGA7qkuQLcjWWHcrD/GKbn/WmJwaezfuuAOcyKlRPA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + + '@babel/plugin-bugfix-safari-class-field-initializer-scope@7.27.1': + resolution: {integrity: sha512-qNeq3bCKnGgLkEXUuFry6dPlGfCdQNZbn7yUAPCInwAJHMU7THJfrBSozkcWq5sNM6RcF3S8XyQL2A52KNR9IA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + + '@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@7.27.1': + resolution: {integrity: sha512-g4L7OYun04N1WyqMNjldFwlfPCLVkgB54A/YCXICZYBsvJJE3kByKv9c9+R/nAfmIfjl2rKYLNyMHboYbZaWaA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + + '@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining@7.27.1': + resolution: {integrity: sha512-oO02gcONcD5O1iTLi/6frMJBIwWEHceWGSGqrpCmEL8nogiS6J9PBlE48CaK20/Jx1LuRml9aDftLgdjXT8+Cw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.13.0 + + '@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly@7.28.3': + resolution: {integrity: sha512-b6YTX108evsvE4YgWyQ921ZAFFQm3Bn+CA3+ZXlNVnPhx+UfsVURoPjfGAPCjBgrqo30yX/C2nZGX96DxvR9Iw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + + '@babel/plugin-proposal-class-properties@7.18.6': + resolution: {integrity: sha512-cumfXOF0+nzZrrN8Rf0t7M+tF6sZc7vhQwYQck9q1/5w2OExlD+b4v4RpMJFaV1Z7WcDRgO6FqvxqxGlwo+RHQ==} + engines: {node: '>=6.9.0'} + deprecated: This proposal has been merged to the ECMAScript standard and thus this plugin is no longer maintained. Please use @babel/plugin-transform-class-properties instead. + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-proposal-decorators@7.28.0': + resolution: {integrity: sha512-zOiZqvANjWDUaUS9xMxbMcK/Zccztbe/6ikvUXaG9nsPH3w6qh5UaPGAnirI/WhIbZ8m3OHU0ReyPrknG+ZKeg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-proposal-nullish-coalescing-operator@7.18.6': + resolution: {integrity: sha512-wQxQzxYeJqHcfppzBDnm1yAY0jSRkUXR2z8RePZYrKwMKgMlE8+Z6LUno+bd6LvbGh8Gltvy74+9pIYkr+XkKA==} + engines: {node: '>=6.9.0'} + deprecated: This proposal has been merged to the ECMAScript standard and thus this plugin is no longer maintained. Please use @babel/plugin-transform-nullish-coalescing-operator instead. + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-proposal-numeric-separator@7.18.6': + resolution: {integrity: sha512-ozlZFogPqoLm8WBr5Z8UckIoE4YQ5KESVcNudyXOR8uqIkliTEgJ3RoketfG6pmzLdeZF0H/wjE9/cCEitBl7Q==} + engines: {node: '>=6.9.0'} + deprecated: This proposal has been merged to the ECMAScript standard and thus this plugin is no longer maintained. Please use @babel/plugin-transform-numeric-separator instead. + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-proposal-optional-chaining@7.21.0': + resolution: {integrity: sha512-p4zeefM72gpmEe2fkUr/OnOXpWEf8nAgk7ZYVqqfFiyIG7oFfVZcCrU64hWn5xp4tQ9LkV4bTIa5rD0KANpKNA==} + engines: {node: '>=6.9.0'} + deprecated: This proposal has been merged to the ECMAScript standard and thus this plugin is no longer maintained. Please use @babel/plugin-transform-optional-chaining instead. + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-proposal-private-methods@7.18.6': + resolution: {integrity: sha512-nutsvktDItsNn4rpGItSNV2sz1XwS+nfU0Rg8aCx3W3NOKVzdMjJRu0O5OkgDp3ZGICSTbgRpxZoWsxoKRvbeA==} + engines: {node: '>=6.9.0'} + deprecated: This proposal has been merged to the ECMAScript standard and thus this plugin is no longer maintained. Please use @babel/plugin-transform-private-methods instead. + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-proposal-private-property-in-object@7.21.0-placeholder-for-preset-env.2': + resolution: {integrity: sha512-SOSkfJDddaM7mak6cPEpswyTRnuRltl429hMraQEglW+OkovnCzsiszTmsrlY//qLFjCpQDFRvjdm2wA5pPm9w==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-proposal-private-property-in-object@7.21.11': + resolution: {integrity: sha512-0QZ8qP/3RLDVBwBFoWAwCtgcDZJVwA5LUJRZU8x2YFfKNuFq161wK3cuGrALu5yiPu+vzwTAg/sMWVNeWeNyaw==} + engines: {node: '>=6.9.0'} + deprecated: This proposal has been merged to the ECMAScript standard and thus this plugin is no longer maintained. Please use @babel/plugin-transform-private-property-in-object instead. + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-syntax-async-generators@7.8.4': + resolution: {integrity: sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw==} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-syntax-bigint@7.8.3': + resolution: {integrity: sha512-wnTnFlG+YxQm3vDxpGE57Pj0srRU4sHE/mDkt1qv2YJJSeUAec2ma4WLUnUPeKjyrfntVwe/N6dCXpU+zL3Npg==} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-syntax-class-properties@7.12.13': + resolution: {integrity: sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA==} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-syntax-class-static-block@7.14.5': + resolution: {integrity: sha512-b+YyPmr6ldyNnM6sqYeMWE+bgJcJpO6yS4QD7ymxgH34GBPNDM/THBh8iunyvKIZztiwLH4CJZ0RxTk9emgpjw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-syntax-decorators@7.27.1': + resolution: {integrity: sha512-YMq8Z87Lhl8EGkmb0MwYkt36QnxC+fzCgrl66ereamPlYToRpIk5nUjKUY3QKLWq8mwUB1BgbeXcTJhZOCDg5A==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-syntax-flow@7.27.1': + resolution: {integrity: sha512-p9OkPbZ5G7UT1MofwYFigGebnrzGJacoBSQM0/6bi/PUMVE+qlWDD/OalvQKbwgQzU6dl0xAv6r4X7Jme0RYxA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-syntax-import-assertions@7.27.1': + resolution: {integrity: sha512-UT/Jrhw57xg4ILHLFnzFpPDlMbcdEicaAtjPQpbj9wa8T4r5KVWCimHcL/460g8Ht0DMxDyjsLgiWSkVjnwPFg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-syntax-import-attributes@7.27.1': + resolution: {integrity: sha512-oFT0FrKHgF53f4vOsZGi2Hh3I35PfSmVs4IBFLFj4dnafP+hIWDLg3VyKmUHfLoLHlyxY4C7DGtmHuJgn+IGww==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-syntax-import-meta@7.10.4': + resolution: {integrity: sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g==} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-syntax-json-strings@7.8.3': + resolution: {integrity: sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA==} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-syntax-jsx@7.27.1': + resolution: {integrity: sha512-y8YTNIeKoyhGd9O0Jiyzyyqk8gdjnumGTQPsz0xOZOQ2RmkVJeZ1vmmfIvFEKqucBG6axJGBZDE/7iI5suUI/w==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-syntax-logical-assignment-operators@7.10.4': + resolution: {integrity: sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig==} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-syntax-nullish-coalescing-operator@7.8.3': + resolution: {integrity: sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ==} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-syntax-numeric-separator@7.10.4': + resolution: {integrity: sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug==} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-syntax-object-rest-spread@7.8.3': + resolution: {integrity: sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA==} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-syntax-optional-catch-binding@7.8.3': + resolution: {integrity: sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q==} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-syntax-optional-chaining@7.8.3': + resolution: {integrity: sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg==} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-syntax-private-property-in-object@7.14.5': + resolution: {integrity: sha512-0wVnp9dxJ72ZUJDV27ZfbSj6iHLoytYZmh3rFcxNnvsJF3ktkzLDZPy/mA17HGsaQT3/DQsWYX1f1QGWkCoVUg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-syntax-top-level-await@7.14.5': + resolution: {integrity: sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-syntax-typescript@7.27.1': + resolution: {integrity: sha512-xfYCBMxveHrRMnAWl1ZlPXOZjzkN82THFvLhQhFXFt81Z5HnN+EtUkZhv/zcKpmT3fzmWZB0ywiBrbC3vogbwQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-syntax-unicode-sets-regex@7.18.6': + resolution: {integrity: sha512-727YkEAPwSIQTv5im8QHz3upqp92JTWhidIC81Tdx4VJYIte/VndKf1qKrfnnhPLiPghStWfvC/iFaMCQu7Nqg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + + '@babel/plugin-transform-arrow-functions@7.27.1': + resolution: {integrity: sha512-8Z4TGic6xW70FKThA5HYEKKyBpOOsucTOD1DjU3fZxDg+K3zBJcXMFnt/4yQiZnf5+MiOMSXQ9PaEK/Ilh1DeA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-async-generator-functions@7.28.0': + resolution: {integrity: sha512-BEOdvX4+M765icNPZeidyADIvQ1m1gmunXufXxvRESy/jNNyfovIqUyE7MVgGBjWktCoJlzvFA1To2O4ymIO3Q==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-async-to-generator@7.27.1': + resolution: {integrity: sha512-NREkZsZVJS4xmTr8qzE5y8AfIPqsdQfRuUiLRTEzb7Qii8iFWCyDKaUV2c0rCuh4ljDZ98ALHP/PetiBV2nddA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-block-scoped-functions@7.27.1': + resolution: {integrity: sha512-cnqkuOtZLapWYZUYM5rVIdv1nXYuFVIltZ6ZJ7nIj585QsjKM5dhL2Fu/lICXZ1OyIAFc7Qy+bvDAtTXqGrlhg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-block-scoping@7.28.4': + resolution: {integrity: sha512-1yxmvN0MJHOhPVmAsmoW5liWwoILobu/d/ShymZmj867bAdxGbehIrew1DuLpw2Ukv+qDSSPQdYW1dLNE7t11A==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-class-properties@7.27.1': + resolution: {integrity: sha512-D0VcalChDMtuRvJIu3U/fwWjf8ZMykz5iZsg77Nuj821vCKI3zCyRLwRdWbsuJ/uRwZhZ002QtCqIkwC/ZkvbA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-class-static-block@7.28.3': + resolution: {integrity: sha512-LtPXlBbRoc4Njl/oh1CeD/3jC+atytbnf/UqLoqTDcEYGUPj022+rvfkbDYieUrSj3CaV4yHDByPE+T2HwfsJg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.12.0 + + '@babel/plugin-transform-classes@7.28.4': + resolution: {integrity: sha512-cFOlhIYPBv/iBoc+KS3M6et2XPtbT2HiCRfBXWtfpc9OAyostldxIf9YAYB6ypURBBbx+Qv6nyrLzASfJe+hBA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-computed-properties@7.27.1': + resolution: {integrity: sha512-lj9PGWvMTVksbWiDT2tW68zGS/cyo4AkZ/QTp0sQT0mjPopCmrSkzxeXkznjqBxzDI6TclZhOJbBmbBLjuOZUw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-destructuring@7.28.0': + resolution: {integrity: sha512-v1nrSMBiKcodhsyJ4Gf+Z0U/yawmJDBOTpEB3mcQY52r9RIyPneGyAS/yM6seP/8I+mWI3elOMtT5dB8GJVs+A==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-dotall-regex@7.27.1': + resolution: {integrity: sha512-gEbkDVGRvjj7+T1ivxrfgygpT7GUd4vmODtYpbs0gZATdkX8/iSnOtZSxiZnsgm1YjTgjI6VKBGSJJevkrclzw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-duplicate-keys@7.27.1': + resolution: {integrity: sha512-MTyJk98sHvSs+cvZ4nOauwTTG1JeonDjSGvGGUNHreGQns+Mpt6WX/dVzWBHgg+dYZhkC4X+zTDfkTU+Vy9y7Q==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-duplicate-named-capturing-groups-regex@7.27.1': + resolution: {integrity: sha512-hkGcueTEzuhB30B3eJCbCYeCaaEQOmQR0AdvzpD4LoN0GXMWzzGSuRrxR2xTnCrvNbVwK9N6/jQ92GSLfiZWoQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + + '@babel/plugin-transform-dynamic-import@7.27.1': + resolution: {integrity: sha512-MHzkWQcEmjzzVW9j2q8LGjwGWpG2mjwaaB0BNQwst3FIjqsg8Ct/mIZlvSPJvfi9y2AC8mi/ktxbFVL9pZ1I4A==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-explicit-resource-management@7.28.0': + resolution: {integrity: sha512-K8nhUcn3f6iB+P3gwCv/no7OdzOZQcKchW6N389V6PD8NUWKZHzndOd9sPDVbMoBsbmjMqlB4L9fm+fEFNVlwQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-exponentiation-operator@7.27.1': + resolution: {integrity: sha512-uspvXnhHvGKf2r4VVtBpeFnuDWsJLQ6MF6lGJLC89jBR1uoVeqM416AZtTuhTezOfgHicpJQmoD5YUakO/YmXQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-export-namespace-from@7.27.1': + resolution: {integrity: sha512-tQvHWSZ3/jH2xuq/vZDy0jNn+ZdXJeM8gHvX4lnJmsc3+50yPlWdZXIc5ay+umX+2/tJIqHqiEqcJvxlmIvRvQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-flow-strip-types@7.27.1': + resolution: {integrity: sha512-G5eDKsu50udECw7DL2AcsysXiQyB7Nfg521t2OAJ4tbfTJ27doHLeF/vlI1NZGlLdbb/v+ibvtL1YBQqYOwJGg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-for-of@7.27.1': + resolution: {integrity: sha512-BfbWFFEJFQzLCQ5N8VocnCtA8J1CLkNTe2Ms2wocj75dd6VpiqS5Z5quTYcUoo4Yq+DN0rtikODccuv7RU81sw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-function-name@7.27.1': + resolution: {integrity: sha512-1bQeydJF9Nr1eBCMMbC+hdwmRlsv5XYOMu03YSWFwNs0HsAmtSxxF1fyuYPqemVldVyFmlCU7w8UE14LupUSZQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-json-strings@7.27.1': + resolution: {integrity: sha512-6WVLVJiTjqcQauBhn1LkICsR2H+zm62I3h9faTDKt1qP4jn2o72tSvqMwtGFKGTpojce0gJs+76eZ2uCHRZh0Q==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-literals@7.27.1': + resolution: {integrity: sha512-0HCFSepIpLTkLcsi86GG3mTUzxV5jpmbv97hTETW3yzrAij8aqlD36toB1D0daVFJM8NK6GvKO0gslVQmm+zZA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-logical-assignment-operators@7.27.1': + resolution: {integrity: sha512-SJvDs5dXxiae4FbSL1aBJlG4wvl594N6YEVVn9e3JGulwioy6z3oPjx/sQBO3Y4NwUu5HNix6KJ3wBZoewcdbw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-member-expression-literals@7.27.1': + resolution: {integrity: sha512-hqoBX4dcZ1I33jCSWcXrP+1Ku7kdqXf1oeah7ooKOIiAdKQ+uqftgCFNOSzA5AMS2XIHEYeGFg4cKRCdpxzVOQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-modules-amd@7.27.1': + resolution: {integrity: sha512-iCsytMg/N9/oFq6n+gFTvUYDZQOMK5kEdeYxmxt91fcJGycfxVP9CnrxoliM0oumFERba2i8ZtwRUCMhvP1LnA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-modules-commonjs@7.27.1': + resolution: {integrity: sha512-OJguuwlTYlN0gBZFRPqwOGNWssZjfIUdS7HMYtN8c1KmwpwHFBwTeFZrg9XZa+DFTitWOW5iTAG7tyCUPsCCyw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-modules-systemjs@7.27.1': + resolution: {integrity: sha512-w5N1XzsRbc0PQStASMksmUeqECuzKuTJer7kFagK8AXgpCMkeDMO5S+aaFb7A51ZYDF7XI34qsTX+fkHiIm5yA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-modules-umd@7.27.1': + resolution: {integrity: sha512-iQBE/xC5BV1OxJbp6WG7jq9IWiD+xxlZhLrdwpPkTX3ydmXdvoCpyfJN7acaIBZaOqTfr76pgzqBJflNbeRK+w==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-named-capturing-groups-regex@7.27.1': + resolution: {integrity: sha512-SstR5JYy8ddZvD6MhV0tM/j16Qds4mIpJTOd1Yu9J9pJjH93bxHECF7pgtc28XvkzTD6Pxcm/0Z73Hvk7kb3Ng==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + + '@babel/plugin-transform-new-target@7.27.1': + resolution: {integrity: sha512-f6PiYeqXQ05lYq3TIfIDu/MtliKUbNwkGApPUvyo6+tc7uaR4cPjPe7DFPr15Uyycg2lZU6btZ575CuQoYh7MQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-nullish-coalescing-operator@7.27.1': + resolution: {integrity: sha512-aGZh6xMo6q9vq1JGcw58lZ1Z0+i0xB2x0XaauNIUXd6O1xXc3RwoWEBlsTQrY4KQ9Jf0s5rgD6SiNkaUdJegTA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-numeric-separator@7.27.1': + resolution: {integrity: sha512-fdPKAcujuvEChxDBJ5c+0BTaS6revLV7CJL08e4m3de8qJfNIuCc2nc7XJYOjBoTMJeqSmwXJ0ypE14RCjLwaw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-object-rest-spread@7.28.4': + resolution: {integrity: sha512-373KA2HQzKhQCYiRVIRr+3MjpCObqzDlyrM6u4I201wL8Mp2wHf7uB8GhDwis03k2ti8Zr65Zyyqs1xOxUF/Ew==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-object-super@7.27.1': + resolution: {integrity: sha512-SFy8S9plRPbIcxlJ8A6mT/CxFdJx/c04JEctz4jf8YZaVS2px34j7NXRrlGlHkN/M2gnpL37ZpGRGVFLd3l8Ng==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-optional-catch-binding@7.27.1': + resolution: {integrity: sha512-txEAEKzYrHEX4xSZN4kJ+OfKXFVSWKB2ZxM9dpcE3wT7smwkNmXo5ORRlVzMVdJbD+Q8ILTgSD7959uj+3Dm3Q==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-optional-chaining@7.27.1': + resolution: {integrity: sha512-BQmKPPIuc8EkZgNKsv0X4bPmOoayeu4F1YCwx2/CfmDSXDbp7GnzlUH+/ul5VGfRg1AoFPsrIThlEBj2xb4CAg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-parameters@7.27.7': + resolution: {integrity: sha512-qBkYTYCb76RRxUM6CcZA5KRu8K4SM8ajzVeUgVdMVO9NN9uI/GaVmBg/WKJJGnNokV9SY8FxNOVWGXzqzUidBg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-private-methods@7.27.1': + resolution: {integrity: sha512-10FVt+X55AjRAYI9BrdISN9/AQWHqldOeZDUoLyif1Kn05a56xVBXb8ZouL8pZ9jem8QpXaOt8TS7RHUIS+GPA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-private-property-in-object@7.27.1': + resolution: {integrity: sha512-5J+IhqTi1XPa0DXF83jYOaARrX+41gOewWbkPyjMNRDqgOCqdffGh8L3f/Ek5utaEBZExjSAzcyjmV9SSAWObQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-property-literals@7.27.1': + resolution: {integrity: sha512-oThy3BCuCha8kDZ8ZkgOg2exvPYUlprMukKQXI1r1pJ47NCvxfkEy8vK+r/hT9nF0Aa4H1WUPZZjHTFtAhGfmQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-react-constant-elements@7.27.1': + resolution: {integrity: sha512-edoidOjl/ZxvYo4lSBOQGDSyToYVkTAwyVoa2tkuYTSmjrB1+uAedoL5iROVLXkxH+vRgA7uP4tMg2pUJpZ3Ug==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-react-display-name@7.28.0': + resolution: {integrity: sha512-D6Eujc2zMxKjfa4Zxl4GHMsmhKKZ9VpcqIchJLvwTxad9zWIYulwYItBovpDOoNLISpcZSXoDJ5gaGbQUDqViA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-react-jsx-development@7.27.1': + resolution: {integrity: sha512-ykDdF5yI4f1WrAolLqeF3hmYU12j9ntLQl/AOG1HAS21jxyg1Q0/J/tpREuYLfatGdGmXp/3yS0ZA76kOlVq9Q==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-react-jsx-self@7.27.1': + resolution: {integrity: sha512-6UzkCs+ejGdZ5mFFC/OCUrv028ab2fp1znZmCZjAOBKiBK2jXD1O+BPSfX8X2qjJ75fZBMSnQn3Rq2mrBJK2mw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-react-jsx-source@7.27.1': + resolution: {integrity: sha512-zbwoTsBruTeKB9hSq73ha66iFeJHuaFkUbwvqElnygoNbj/jHRsSeokowZFN3CZ64IvEqcmmkVe89OPXc7ldAw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-react-jsx@7.27.1': + resolution: {integrity: sha512-2KH4LWGSrJIkVf5tSiBFYuXDAoWRq2MMwgivCf+93dd0GQi8RXLjKA/0EvRnVV5G0hrHczsquXuD01L8s6dmBw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-react-pure-annotations@7.27.1': + resolution: {integrity: sha512-JfuinvDOsD9FVMTHpzA/pBLisxpv1aSf+OIV8lgH3MuWrks19R27e6a6DipIg4aX1Zm9Wpb04p8wljfKrVSnPA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-regenerator@7.28.4': + resolution: {integrity: sha512-+ZEdQlBoRg9m2NnzvEeLgtvBMO4tkFBw5SQIUgLICgTrumLoU7lr+Oghi6km2PFj+dbUt2u1oby2w3BDO9YQnA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-regexp-modifiers@7.27.1': + resolution: {integrity: sha512-TtEciroaiODtXvLZv4rmfMhkCv8jx3wgKpL68PuiPh2M4fvz5jhsA7697N1gMvkvr/JTF13DrFYyEbY9U7cVPA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + + '@babel/plugin-transform-reserved-words@7.27.1': + resolution: {integrity: sha512-V2ABPHIJX4kC7HegLkYoDpfg9PVmuWy/i6vUM5eGK22bx4YVFD3M5F0QQnWQoDs6AGsUWTVOopBiMFQgHaSkVw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-runtime@7.28.3': + resolution: {integrity: sha512-Y6ab1kGqZ0u42Zv/4a7l0l72n9DKP/MKoKWaUSBylrhNZO2prYuqFOLbn5aW5SIFXwSH93yfjbgllL8lxuGKLg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-shorthand-properties@7.27.1': + resolution: {integrity: sha512-N/wH1vcn4oYawbJ13Y/FxcQrWk63jhfNa7jef0ih7PHSIHX2LB7GWE1rkPrOnka9kwMxb6hMl19p7lidA+EHmQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-spread@7.27.1': + resolution: {integrity: sha512-kpb3HUqaILBJcRFVhFUs6Trdd4mkrzcGXss+6/mxUd273PfbWqSDHRzMT2234gIg2QYfAjvXLSquP1xECSg09Q==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-sticky-regex@7.27.1': + resolution: {integrity: sha512-lhInBO5bi/Kowe2/aLdBAawijx+q1pQzicSgnkB6dUPc1+RC8QmJHKf2OjvU+NZWitguJHEaEmbV6VWEouT58g==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-template-literals@7.27.1': + resolution: {integrity: sha512-fBJKiV7F2DxZUkg5EtHKXQdbsbURW3DZKQUWphDum0uRP6eHGGa/He9mc0mypL680pb+e/lDIthRohlv8NCHkg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-typeof-symbol@7.27.1': + resolution: {integrity: sha512-RiSILC+nRJM7FY5srIyc4/fGIwUhyDuuBSdWn4y6yT6gm652DpCHZjIipgn6B7MQ1ITOUnAKWixEUjQRIBIcLw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-typescript@7.28.0': + resolution: {integrity: sha512-4AEiDEBPIZvLQaWlc9liCavE0xRM0dNca41WtBeM3jgFptfUOSG9z0uteLhq6+3rq+WB6jIvUwKDTpXEHPJ2Vg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-unicode-escapes@7.27.1': + resolution: {integrity: sha512-Ysg4v6AmF26k9vpfFuTZg8HRfVWzsh1kVfowA23y9j/Gu6dOuahdUVhkLqpObp3JIv27MLSii6noRnuKN8H0Mg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-unicode-property-regex@7.27.1': + resolution: {integrity: sha512-uW20S39PnaTImxp39O5qFlHLS9LJEmANjMG7SxIhap8rCHqu0Ik+tLEPX5DKmHn6CsWQ7j3lix2tFOa5YtL12Q==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-unicode-regex@7.27.1': + resolution: {integrity: sha512-xvINq24TRojDuyt6JGtHmkVkrfVV3FPT16uytxImLeBZqW3/H52yN+kM1MGuyPkIQxrzKwPHs5U/MP3qKyzkGw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-unicode-sets-regex@7.27.1': + resolution: {integrity: sha512-EtkOujbc4cgvb0mlpQefi4NTPBzhSIevblFevACNLUspmrALgmEBdL/XfnyyITfd8fKBZrZys92zOWcik7j9Tw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + + '@babel/preset-env@7.28.3': + resolution: {integrity: sha512-ROiDcM+GbYVPYBOeCR6uBXKkQpBExLl8k9HO1ygXEyds39j+vCCsjmj7S8GOniZQlEs81QlkdJZe76IpLSiqpg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/preset-modules@0.1.6-no-external-plugins': + resolution: {integrity: sha512-HrcgcIESLm9aIR842yhJ5RWan/gebQUJ6E/E5+rf0y9o6oj7w0Br+sWuL6kEQ/o/AdfvR1Je9jG18/gnpwjEyA==} + peerDependencies: + '@babel/core': ^7.0.0-0 || ^8.0.0-0 <8.0.0 + + '@babel/preset-react@7.27.1': + resolution: {integrity: sha512-oJHWh2gLhU9dW9HHr42q0cI0/iHHXTLGe39qvpAZZzagHy0MzYLCnCVV0symeRvzmjHyVU7mw2K06E6u/JwbhA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/preset-typescript@7.18.6': + resolution: {integrity: sha512-s9ik86kXBAnD760aybBucdpnLsAt0jK1xqJn2juOn9lkOvSHV60os5hxoVJsPzMQxvnUJFAlkont2DvvaYEBtQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/runtime@7.28.4': + resolution: {integrity: sha512-Q/N6JNWvIvPnLDvjlE1OUBLPQHH6l3CltCEsHIujp45zQUSSh8K+gHnaEX45yAT1nyngnINhvWtzN+Nb9D8RAQ==} + engines: {node: '>=6.9.0'} + + '@babel/template@7.27.2': + resolution: {integrity: sha512-LPDZ85aEJyYSd18/DkjNh4/y1ntkE5KwUHWTiqgRxruuZL2F1yuHligVHLvcHY2vMHXttKFpJn6LwfI7cw7ODw==} + engines: {node: '>=6.9.0'} + + '@babel/traverse@7.28.4': + resolution: {integrity: sha512-YEzuboP2qvQavAcjgQNVgsvHIDv6ZpwXvcvjmyySP2DIMuByS/6ioU5G9pYrWHM6T2YDfc7xga9iNzYOs12CFQ==} + engines: {node: '>=6.9.0'} + + '@babel/types@7.28.4': + resolution: {integrity: sha512-bkFqkLhh3pMBUQQkpVgWDWq/lqzc2678eUyDlTBhRqhCHFguYYGM0Efga7tYk4TogG/3x0EEl66/OQ+WGbWB/Q==} + engines: {node: '>=6.9.0'} + + '@bcoe/v8-coverage@0.2.3': + resolution: {integrity: sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==} + + '@codemirror/autocomplete@6.19.0': + resolution: {integrity: sha512-61Hfv3cF07XvUxNeC3E7jhG8XNi1Yom1G0lRC936oLnlF+jrbrv8rc/J98XlYzcsAoTVupfsf5fLej1aI8kyIg==} + + '@codemirror/commands@6.9.0': + resolution: {integrity: sha512-454TVgjhO6cMufsyyGN70rGIfJxJEjcqjBG2x2Y03Y/+Fm99d3O/Kv1QDYWuG6hvxsgmjXmBuATikIIYvERX+w==} + + '@codemirror/lang-css@6.3.1': + resolution: {integrity: sha512-kr5fwBGiGtmz6l0LSJIbno9QrifNMUusivHbnA1H6Dmqy4HZFte3UAICix1VuKo0lMPKQr2rqB+0BkKi/S3Ejg==} + + '@codemirror/lang-html@6.4.11': + resolution: {integrity: sha512-9NsXp7Nwp891pQchI7gPdTwBuSuT3K65NGTHWHNJ55HjYcHLllr0rbIZNdOzas9ztc1EUVBlHou85FFZS4BNnw==} + + '@codemirror/lang-javascript@6.2.4': + resolution: {integrity: sha512-0WVmhp1QOqZ4Rt6GlVGwKJN3KW7Xh4H2q8ZZNGZaP6lRdxXJzmjm4FqvmOojVj6khWJHIb9sp7U/72W7xQgqAA==} + + '@codemirror/lang-json@6.0.2': + resolution: {integrity: sha512-x2OtO+AvwEHrEwR0FyyPtfDUiloG3rnVTSZV1W8UteaLL8/MajQd8DpvUb2YVzC+/T18aSDv0H9mu+xw0EStoQ==} + + '@codemirror/lang-markdown@6.5.0': + resolution: {integrity: sha512-0K40bZ35jpHya6FriukbgaleaqzBLZfOh7HuzqbMxBXkbYMJDxfF39c23xOgxFezR+3G+tR2/Mup+Xk865OMvw==} + + '@codemirror/language@6.11.3': + resolution: {integrity: sha512-9HBM2XnwDj7fnu0551HkGdrUrrqmYq/WC5iv6nbY2WdicXdGbhR/gfbZOH73Aqj4351alY1+aoG9rCNfiwS1RA==} + + '@codemirror/lint@6.9.0': + resolution: {integrity: sha512-wZxW+9XDytH3SKvS8cQzMyQCaaazH8XL1EMHleHe00wVzsv7NBQKVW2yzEHrRhmM7ZOhVdItPbvlRBvMp9ej7A==} + + '@codemirror/search@6.5.11': + resolution: {integrity: sha512-KmWepDE6jUdL6n8cAAqIpRmLPBZ5ZKnicE8oGU/s3QrAVID+0VhLFrzUucVKHG5035/BSykhExDL/Xm7dHthiA==} + + '@codemirror/state@6.5.2': + resolution: {integrity: sha512-FVqsPqtPWKVVL3dPSxy8wEF/ymIEuVzF1PK3VbUgrxXpJUSHQWWZz4JMToquRxnkw+36LTamCZG2iua2Ptq0fA==} + + '@codemirror/theme-one-dark@6.1.3': + resolution: {integrity: sha512-NzBdIvEJmx6fjeremiGp3t/okrLPYT0d9orIc7AFun8oZcRk58aejkqhv6spnz4MLAevrKNPMQYXEWMg4s+sKA==} + + '@codemirror/view@6.38.5': + resolution: {integrity: sha512-SFVsNAgsAoou+BjRewMqN+m9jaztB9wCWN9RSRgePqUbq8UVlvJfku5zB2KVhLPgH/h0RLk38tvd4tGeAhygnw==} + + '@colors/colors@1.5.0': + resolution: {integrity: sha512-ooWCrlZP11i8GImSjTHYHLkvFDP48nS4+204nGb1RiX/WXYHmJA2III9/e2DWVabCESdW7hBAEzHRqUn9OUVvQ==} + engines: {node: '>=0.1.90'} + + '@colors/colors@1.6.0': + resolution: {integrity: sha512-Ir+AOibqzrIsL6ajt3Rz3LskB7OiMVHqltZmspbW/TJuTVuyOMirVqAkjfY6JISiLHgyNqicAC8AyHHGzNd/dA==} + engines: {node: '>=0.1.90'} + + '@cspotcode/source-map-support@0.8.1': + resolution: {integrity: sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==} + engines: {node: '>=12'} + + '@csstools/normalize.css@12.1.1': + resolution: {integrity: sha512-YAYeJ+Xqh7fUou1d1j9XHl44BmsuThiTr4iNrgCQ3J27IbhXsxXDGZ1cXv8Qvs99d4rBbLiSKy3+WZiet32PcQ==} + + '@csstools/postcss-cascade-layers@1.1.1': + resolution: {integrity: sha512-+KdYrpKC5TgomQr2DlZF4lDEpHcoxnj5IGddYYfBWJAKfj1JtuHUIqMa+E1pJJ+z3kvDViWMqyqPlG4Ja7amQA==} + engines: {node: ^12 || ^14 || >=16} + peerDependencies: + postcss: ^8.2 + + '@csstools/postcss-color-function@1.1.1': + resolution: {integrity: sha512-Bc0f62WmHdtRDjf5f3e2STwRAl89N2CLb+9iAwzrv4L2hncrbDwnQD9PCq0gtAt7pOI2leIV08HIBUd4jxD8cw==} + engines: {node: ^12 || ^14 || >=16} + peerDependencies: + postcss: ^8.2 + + '@csstools/postcss-font-format-keywords@1.0.1': + resolution: {integrity: sha512-ZgrlzuUAjXIOc2JueK0X5sZDjCtgimVp/O5CEqTcs5ShWBa6smhWYbS0x5cVc/+rycTDbjjzoP0KTDnUneZGOg==} + engines: {node: ^12 || ^14 || >=16} + peerDependencies: + postcss: ^8.2 + + '@csstools/postcss-hwb-function@1.0.2': + resolution: {integrity: sha512-YHdEru4o3Rsbjmu6vHy4UKOXZD+Rn2zmkAmLRfPet6+Jz4Ojw8cbWxe1n42VaXQhD3CQUXXTooIy8OkVbUcL+w==} + engines: {node: ^12 || ^14 || >=16} + peerDependencies: + postcss: ^8.2 + + '@csstools/postcss-ic-unit@1.0.1': + resolution: {integrity: sha512-Ot1rcwRAaRHNKC9tAqoqNZhjdYBzKk1POgWfhN4uCOE47ebGcLRqXjKkApVDpjifL6u2/55ekkpnFcp+s/OZUw==} + engines: {node: ^12 || ^14 || >=16} + peerDependencies: + postcss: ^8.2 + + '@csstools/postcss-is-pseudo-class@2.0.7': + resolution: {integrity: sha512-7JPeVVZHd+jxYdULl87lvjgvWldYu+Bc62s9vD/ED6/QTGjy0jy0US/f6BG53sVMTBJ1lzKZFpYmofBN9eaRiA==} + engines: {node: ^12 || ^14 || >=16} + peerDependencies: + postcss: ^8.2 + + '@csstools/postcss-nested-calc@1.0.0': + resolution: {integrity: sha512-JCsQsw1wjYwv1bJmgjKSoZNvf7R6+wuHDAbi5f/7MbFhl2d/+v+TvBTU4BJH3G1X1H87dHl0mh6TfYogbT/dJQ==} + engines: {node: ^12 || ^14 || >=16} + peerDependencies: + postcss: ^8.2 + + '@csstools/postcss-normalize-display-values@1.0.1': + resolution: {integrity: sha512-jcOanIbv55OFKQ3sYeFD/T0Ti7AMXc9nM1hZWu8m/2722gOTxFg7xYu4RDLJLeZmPUVQlGzo4jhzvTUq3x4ZUw==} + engines: {node: ^12 || ^14 || >=16} + peerDependencies: + postcss: ^8.2 + + '@csstools/postcss-oklab-function@1.1.1': + resolution: {integrity: sha512-nJpJgsdA3dA9y5pgyb/UfEzE7W5Ka7u0CX0/HIMVBNWzWemdcTH3XwANECU6anWv/ao4vVNLTMxhiPNZsTK6iA==} + engines: {node: ^12 || ^14 || >=16} + peerDependencies: + postcss: ^8.2 + + '@csstools/postcss-progressive-custom-properties@1.3.0': + resolution: {integrity: sha512-ASA9W1aIy5ygskZYuWams4BzafD12ULvSypmaLJT2jvQ8G0M3I8PRQhC0h7mG0Z3LI05+agZjqSR9+K9yaQQjA==} + engines: {node: ^12 || ^14 || >=16} + peerDependencies: + postcss: ^8.3 + + '@csstools/postcss-stepped-value-functions@1.0.1': + resolution: {integrity: sha512-dz0LNoo3ijpTOQqEJLY8nyaapl6umbmDcgj4AD0lgVQ572b2eqA1iGZYTTWhrcrHztWDDRAX2DGYyw2VBjvCvQ==} + engines: {node: ^12 || ^14 || >=16} + peerDependencies: + postcss: ^8.2 + + '@csstools/postcss-text-decoration-shorthand@1.0.0': + resolution: {integrity: sha512-c1XwKJ2eMIWrzQenN0XbcfzckOLLJiczqy+YvfGmzoVXd7pT9FfObiSEfzs84bpE/VqfpEuAZ9tCRbZkZxxbdw==} + engines: {node: ^12 || ^14 || >=16} + peerDependencies: + postcss: ^8.2 + + '@csstools/postcss-trigonometric-functions@1.0.2': + resolution: {integrity: sha512-woKaLO///4bb+zZC2s80l+7cm07M7268MsyG3M0ActXXEFi6SuhvriQYcb58iiKGbjwwIU7n45iRLEHypB47Og==} + engines: {node: ^14 || >=16} + peerDependencies: + postcss: ^8.2 + + '@csstools/postcss-unset-value@1.0.2': + resolution: {integrity: sha512-c8J4roPBILnelAsdLr4XOAR/GsTm0GJi4XpcfvoWk3U6KiTCqiFYc63KhRMQQX35jYMp4Ao8Ij9+IZRgMfJp1g==} + engines: {node: ^12 || ^14 || >=16} + peerDependencies: + postcss: ^8.2 + + '@csstools/selector-specificity@2.2.0': + resolution: {integrity: sha512-+OJ9konv95ClSTOJCmMZqpd5+YGsB2S+x6w3E1oaM8UuR5j8nTNHYSz8c9BEPGDOCMQYIEEGlVPj/VY64iTbGw==} + engines: {node: ^14 || ^16 || >=18} + peerDependencies: + postcss-selector-parser: ^6.0.10 + + '@cypress/request@3.0.9': + resolution: {integrity: sha512-I3l7FdGRXluAS44/0NguwWlO83J18p0vlr2FYHrJkWdNYhgVoiYo61IXPqaOsL+vNxU1ZqMACzItGK3/KKDsdw==} + engines: {node: '>= 6'} + + '@cypress/xvfb@1.2.4': + resolution: {integrity: sha512-skbBzPggOVYCbnGgV+0dmBdW/s77ZkAOXIC1knS8NagwDjBrNC1LuXtQJeiN6l+m7lzmHtaoUw/ctJKdqkG57Q==} + + '@dabh/diagnostics@2.0.8': + resolution: {integrity: sha512-R4MSXTVnuMzGD7bzHdW2ZhhdPC/igELENcq5IjEverBvq5hn1SXCWcsi6eSsdWP0/Ur+SItRRjAktmdoX/8R/Q==} + + '@emotion/babel-plugin@11.13.5': + resolution: {integrity: sha512-pxHCpT2ex+0q+HH91/zsdHkw/lXd468DIN2zvfvLtPKLLMo6gQj7oLObq8PhkrxOZb/gGCq03S3Z7PDhS8pduQ==} + + '@emotion/cache@11.14.0': + resolution: {integrity: sha512-L/B1lc/TViYk4DcpGxtAVbx0ZyiKM5ktoIyafGkH6zg/tj+mA+NE//aPYKG0k8kCHSHVJrpLpcAlOBEXQ3SavA==} + + '@emotion/hash@0.9.2': + resolution: {integrity: sha512-MyqliTZGuOm3+5ZRSaaBGP3USLw6+EGykkwZns2EPC5g8jJ4z9OrdZY9apkl3+UP9+sdz76YYkwCKP5gh8iY3g==} + + '@emotion/is-prop-valid@0.8.8': + resolution: {integrity: sha512-u5WtneEAr5IDG2Wv65yhunPSMLIpuKsbuOktRojfrEiEvRyC85LgPMZI63cr7NUqT8ZIGdSVg8ZKGxIug4lXcA==} + + '@emotion/is-prop-valid@1.4.0': + resolution: {integrity: sha512-QgD4fyscGcbbKwJmqNvUMSE02OsHUa+lAWKdEUIJKgqe5IwRSKd7+KhibEWdaKwgjLj0DRSHA9biAIqGBk05lw==} + + '@emotion/memoize@0.7.4': + resolution: {integrity: sha512-Ja/Vfqe3HpuzRsG1oBtWTHk2PGZ7GR+2Vz5iYGelAw8dx32K0y7PjVuxK6z1nMpZOqAFsRUPCkK1YjJ56qJlgw==} + + '@emotion/memoize@0.9.0': + resolution: {integrity: sha512-30FAj7/EoJ5mwVPOWhAyCX+FPfMDrVecJAM+Iw9NRoSl4BBAQeqj4cApHHUXOVvIPgLVDsCFoz/hGD+5QQD1GQ==} + + '@emotion/react@11.14.0': + resolution: {integrity: sha512-O000MLDBDdk/EohJPFUqvnp4qnHeYkVP5B0xEG0D/L7cOKP9kefu2DXn8dj74cQfsEzUqh+sr1RzFqiL1o+PpA==} + peerDependencies: + '@types/react': '*' + react: '>=16.8.0' + peerDependenciesMeta: + '@types/react': + optional: true + + '@emotion/serialize@1.3.3': + resolution: {integrity: sha512-EISGqt7sSNWHGI76hC7x1CksiXPahbxEOrC5RjmFRJTqLyEK9/9hZvBbiYn70dw4wuwMKiEMCUlR6ZXTSWQqxA==} + + '@emotion/sheet@1.4.0': + resolution: {integrity: sha512-fTBW9/8r2w3dXWYM4HCB1Rdp8NLibOw2+XELH5m5+AkWiL/KqYX6dc0kKYlaYyKjrQ6ds33MCdMPEwgs2z1rqg==} + + '@emotion/styled@11.14.1': + resolution: {integrity: sha512-qEEJt42DuToa3gurlH4Qqc1kVpNq8wO8cJtDzU46TjlzWjDlsVyevtYCRijVq3SrHsROS+gVQ8Fnea108GnKzw==} + peerDependencies: + '@emotion/react': ^11.0.0-rc.0 + '@types/react': '*' + react: '>=16.8.0' + peerDependenciesMeta: + '@types/react': + optional: true + + '@emotion/unitless@0.10.0': + resolution: {integrity: sha512-dFoMUuQA20zvtVTuxZww6OHoJYgrzfKM1t52mVySDJnMSEa08ruEvdYQbhvyu6soU+NeLVd3yKfTfT0NeV6qGg==} + + '@emotion/use-insertion-effect-with-fallbacks@1.2.0': + resolution: {integrity: sha512-yJMtVdH59sxi/aVJBpk9FQq+OR8ll5GT8oWd57UpeaKEVGab41JWaCFA7FRLoMLloOZF/c/wsPoe+bfGmRKgDg==} + peerDependencies: + react: '>=16.8.0' + + '@emotion/utils@1.4.2': + resolution: {integrity: sha512-3vLclRofFziIa3J2wDh9jjbkUz9qk5Vi3IZ/FSTKViB0k+ef0fPV7dYrUIugbgupYDx7v9ud/SjrtEP8Y4xLoA==} + + '@emotion/weak-memoize@0.4.0': + resolution: {integrity: sha512-snKqtPW01tN0ui7yu9rGv69aJXr/a/Ywvl11sUjNtEcRc+ng/mQriFL0wLXMef74iHa/EkftbDzU9F8iFbH+zg==} + + '@esbuild/aix-ppc64@0.27.1': + resolution: {integrity: sha512-HHB50pdsBX6k47S4u5g/CaLjqS3qwaOVE5ILsq64jyzgMhLuCuZ8rGzM9yhsAjfjkbgUPMzZEPa7DAp7yz6vuA==} + engines: {node: '>=18'} + cpu: [ppc64] + os: [aix] + + '@esbuild/android-arm64@0.27.1': + resolution: {integrity: sha512-45fuKmAJpxnQWixOGCrS+ro4Uvb4Re9+UTieUY2f8AEc+t7d4AaZ6eUJ3Hva7dtrxAAWHtlEFsXFMAgNnGU9uQ==} + engines: {node: '>=18'} + cpu: [arm64] + os: [android] + + '@esbuild/android-arm@0.27.1': + resolution: {integrity: sha512-kFqa6/UcaTbGm/NncN9kzVOODjhZW8e+FRdSeypWe6j33gzclHtwlANs26JrupOntlcWmB0u8+8HZo8s7thHvg==} + engines: {node: '>=18'} + cpu: [arm] + os: [android] + + '@esbuild/android-x64@0.27.1': + resolution: {integrity: sha512-LBEpOz0BsgMEeHgenf5aqmn/lLNTFXVfoWMUox8CtWWYK9X4jmQzWjoGoNb8lmAYml/tQ/Ysvm8q7szu7BoxRQ==} + engines: {node: '>=18'} + cpu: [x64] + os: [android] + + '@esbuild/darwin-arm64@0.27.1': + resolution: {integrity: sha512-veg7fL8eMSCVKL7IW4pxb54QERtedFDfY/ASrumK/SbFsXnRazxY4YykN/THYqFnFwJ0aVjiUrVG2PwcdAEqQQ==} + engines: {node: '>=18'} + cpu: [arm64] + os: [darwin] + + '@esbuild/darwin-x64@0.27.1': + resolution: {integrity: sha512-+3ELd+nTzhfWb07Vol7EZ+5PTbJ/u74nC6iv4/lwIU99Ip5uuY6QoIf0Hn4m2HoV0qcnRivN3KSqc+FyCHjoVQ==} + engines: {node: '>=18'} + cpu: [x64] + os: [darwin] + + '@esbuild/freebsd-arm64@0.27.1': + resolution: {integrity: sha512-/8Rfgns4XD9XOSXlzUDepG8PX+AVWHliYlUkFI3K3GB6tqbdjYqdhcb4BKRd7C0BhZSoaCxhv8kTcBrcZWP+xg==} + engines: {node: '>=18'} + cpu: [arm64] + os: [freebsd] + + '@esbuild/freebsd-x64@0.27.1': + resolution: {integrity: sha512-GITpD8dK9C+r+5yRT/UKVT36h/DQLOHdwGVwwoHidlnA168oD3uxA878XloXebK4Ul3gDBBIvEdL7go9gCUFzQ==} + engines: {node: '>=18'} + cpu: [x64] + os: [freebsd] + + '@esbuild/linux-arm64@0.27.1': + resolution: {integrity: sha512-W9//kCrh/6in9rWIBdKaMtuTTzNj6jSeG/haWBADqLLa9P8O5YSRDzgD5y9QBok4AYlzS6ARHifAb75V6G670Q==} + engines: {node: '>=18'} + cpu: [arm64] + os: [linux] + + '@esbuild/linux-arm@0.27.1': + resolution: {integrity: sha512-ieMID0JRZY/ZeCrsFQ3Y3NlHNCqIhTprJfDgSB3/lv5jJZ8FX3hqPyXWhe+gvS5ARMBJ242PM+VNz/ctNj//eA==} + engines: {node: '>=18'} + cpu: [arm] + os: [linux] + + '@esbuild/linux-ia32@0.27.1': + resolution: {integrity: sha512-VIUV4z8GD8rtSVMfAj1aXFahsi/+tcoXXNYmXgzISL+KB381vbSTNdeZHHHIYqFyXcoEhu9n5cT+05tRv13rlw==} + engines: {node: '>=18'} + cpu: [ia32] + os: [linux] + + '@esbuild/linux-loong64@0.27.1': + resolution: {integrity: sha512-l4rfiiJRN7sTNI//ff65zJ9z8U+k6zcCg0LALU5iEWzY+a1mVZ8iWC1k5EsNKThZ7XCQ6YWtsZ8EWYm7r1UEsg==} + engines: {node: '>=18'} + cpu: [loong64] + os: [linux] + + '@esbuild/linux-mips64el@0.27.1': + resolution: {integrity: sha512-U0bEuAOLvO/DWFdygTHWY8C067FXz+UbzKgxYhXC0fDieFa0kDIra1FAhsAARRJbvEyso8aAqvPdNxzWuStBnA==} + engines: {node: '>=18'} + cpu: [mips64el] + os: [linux] + + '@esbuild/linux-ppc64@0.27.1': + resolution: {integrity: sha512-NzdQ/Xwu6vPSf/GkdmRNsOfIeSGnh7muundsWItmBsVpMoNPVpM61qNzAVY3pZ1glzzAxLR40UyYM23eaDDbYQ==} + engines: {node: '>=18'} + cpu: [ppc64] + os: [linux] + + '@esbuild/linux-riscv64@0.27.1': + resolution: {integrity: sha512-7zlw8p3IApcsN7mFw0O1Z1PyEk6PlKMu18roImfl3iQHTnr/yAfYv6s4hXPidbDoI2Q0pW+5xeoM4eTCC0UdrQ==} + engines: {node: '>=18'} + cpu: [riscv64] + os: [linux] + + '@esbuild/linux-s390x@0.27.1': + resolution: {integrity: sha512-cGj5wli+G+nkVQdZo3+7FDKC25Uh4ZVwOAK6A06Hsvgr8WqBBuOy/1s+PUEd/6Je+vjfm6stX0kmib5b/O2Ykw==} + engines: {node: '>=18'} + cpu: [s390x] + os: [linux] + + '@esbuild/linux-x64@0.27.1': + resolution: {integrity: sha512-z3H/HYI9MM0HTv3hQZ81f+AKb+yEoCRlUby1F80vbQ5XdzEMyY/9iNlAmhqiBKw4MJXwfgsh7ERGEOhrM1niMA==} + engines: {node: '>=18'} + cpu: [x64] + os: [linux] + + '@esbuild/netbsd-arm64@0.27.1': + resolution: {integrity: sha512-wzC24DxAvk8Em01YmVXyjl96Mr+ecTPyOuADAvjGg+fyBpGmxmcr2E5ttf7Im8D0sXZihpxzO1isus8MdjMCXQ==} + engines: {node: '>=18'} + cpu: [arm64] + os: [netbsd] + + '@esbuild/netbsd-x64@0.27.1': + resolution: {integrity: sha512-1YQ8ybGi2yIXswu6eNzJsrYIGFpnlzEWRl6iR5gMgmsrR0FcNoV1m9k9sc3PuP5rUBLshOZylc9nqSgymI+TYg==} + engines: {node: '>=18'} + cpu: [x64] + os: [netbsd] + + '@esbuild/openbsd-arm64@0.27.1': + resolution: {integrity: sha512-5Z+DzLCrq5wmU7RDaMDe2DVXMRm2tTDvX2KU14JJVBN2CT/qov7XVix85QoJqHltpvAOZUAc3ndU56HSMWrv8g==} + engines: {node: '>=18'} + cpu: [arm64] + os: [openbsd] + + '@esbuild/openbsd-x64@0.27.1': + resolution: {integrity: sha512-Q73ENzIdPF5jap4wqLtsfh8YbYSZ8Q0wnxplOlZUOyZy7B4ZKW8DXGWgTCZmF8VWD7Tciwv5F4NsRf6vYlZtqg==} + engines: {node: '>=18'} + cpu: [x64] + os: [openbsd] + + '@esbuild/openharmony-arm64@0.27.1': + resolution: {integrity: sha512-ajbHrGM/XiK+sXM0JzEbJAen+0E+JMQZ2l4RR4VFwvV9JEERx+oxtgkpoKv1SevhjavK2z2ReHk32pjzktWbGg==} + engines: {node: '>=18'} + cpu: [arm64] + os: [openharmony] + + '@esbuild/sunos-x64@0.27.1': + resolution: {integrity: sha512-IPUW+y4VIjuDVn+OMzHc5FV4GubIwPnsz6ubkvN8cuhEqH81NovB53IUlrlBkPMEPxvNnf79MGBoz8rZ2iW8HA==} + engines: {node: '>=18'} + cpu: [x64] + os: [sunos] + + '@esbuild/win32-arm64@0.27.1': + resolution: {integrity: sha512-RIVRWiljWA6CdVu8zkWcRmGP7iRRIIwvhDKem8UMBjPql2TXM5PkDVvvrzMtj1V+WFPB4K7zkIGM7VzRtFkjdg==} + engines: {node: '>=18'} + cpu: [arm64] + os: [win32] + + '@esbuild/win32-ia32@0.27.1': + resolution: {integrity: sha512-2BR5M8CPbptC1AK5JbJT1fWrHLvejwZidKx3UMSF0ecHMa+smhi16drIrCEggkgviBwLYd5nwrFLSl5Kho96RQ==} + engines: {node: '>=18'} + cpu: [ia32] + os: [win32] + + '@esbuild/win32-x64@0.27.1': + resolution: {integrity: sha512-d5X6RMYv6taIymSk8JBP+nxv8DQAMY6A51GPgusqLdK9wBz5wWIXy1KjTck6HnjE9hqJzJRdk+1p/t5soSbCtw==} + engines: {node: '>=18'} + cpu: [x64] + os: [win32] + + '@eslint-community/eslint-utils@4.9.0': + resolution: {integrity: sha512-ayVFHdtZ+hsq1t2Dy24wCmGXGe4q9Gu3smhLYALJrr473ZH27MsnSL+LKUlimp4BWJqMDMLmPpx/Q9R3OAlL4g==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + peerDependencies: + eslint: ^6.0.0 || ^7.0.0 || >=8.0.0 + + '@eslint-community/regexpp@4.12.1': + resolution: {integrity: sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ==} + engines: {node: ^12.0.0 || ^14.0.0 || >=16.0.0} + + '@eslint/eslintrc@2.1.4': + resolution: {integrity: sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + + '@eslint/js@8.57.1': + resolution: {integrity: sha512-d9zaMRSTIKDLhctzH12MtXvJKSSUhaHcjV+2Z+GK+EEY7XKpP5yR4x+N3TAcHTcu963nIr+TMcCb4DBCYX1z6Q==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + + '@floating-ui/core@1.7.3': + resolution: {integrity: sha512-sGnvb5dmrJaKEZ+LDIpguvdX3bDlEllmv4/ClQ9awcmCZrlx5jQyyMWFM5kBI+EyNOCDDiKk8il0zeuX3Zlg/w==} + + '@floating-ui/dom@1.7.4': + resolution: {integrity: sha512-OOchDgh4F2CchOX94cRVqhvy7b3AFb+/rQXyswmzmGakRfkMgoWVjfnLWkRirfLEfuD4ysVW16eXzwt3jHIzKA==} + + '@floating-ui/react-dom@2.1.6': + resolution: {integrity: sha512-4JX6rEatQEvlmgU80wZyq9RT96HZJa88q8hp0pBd+LrczeDI4o6uA2M+uvxngVHo4Ihr8uibXxH6+70zhAFrVw==} + peerDependencies: + react: '>=16.8.0' + react-dom: '>=16.8.0' + + '@floating-ui/utils@0.2.10': + resolution: {integrity: sha512-aGTxbpbg8/b5JfU1HXSrbH3wXZuLPJcNEcZQFMxLs3oSzgtVu6nFPkbbGGUvBcUjKV2YyB9Wxxabo+HEH9tcRQ==} + + '@gar/promisify@1.1.3': + resolution: {integrity: sha512-k2Ty1JcVojjJFwrg/ThKi2ujJ7XNLYaFGNB/bWT9wGR+oSMJHMa5w+CUq6p/pVrKeNNgA7pCqEcjSnHVoqJQFw==} + + '@grpc/grpc-js@1.14.0': + resolution: {integrity: sha512-N8Jx6PaYzcTRNzirReJCtADVoq4z7+1KQ4E70jTg/koQiMoUSN1kbNjPOqpPbhMFhfU1/l7ixspPl8dNY+FoUg==} + engines: {node: '>=12.10.0'} + + '@grpc/proto-loader@0.8.0': + resolution: {integrity: sha512-rc1hOQtjIWGxcxpb9aHAfLpIctjEnsDehj0DAiVfBlmT84uvR0uUtN2hEi/ecvWVjXUGf5qPF4qEgiLOx1YIMQ==} + engines: {node: '>=6'} + hasBin: true + + '@hapi/address@5.1.1': + resolution: {integrity: sha512-A+po2d/dVoY7cYajycYI43ZbYMXukuopIsqCjh5QzsBCipDtdofHntljDlpccMjIfTy6UOkg+5KPriwYch2bXA==} + engines: {node: '>=14.0.0'} + + '@hapi/formula@3.0.2': + resolution: {integrity: sha512-hY5YPNXzw1He7s0iqkRQi+uMGh383CGdyyIGYtB+W5N3KHPXoqychklvHhKCC9M3Xtv0OCs/IHw+r4dcHtBYWw==} + + '@hapi/hoek@11.0.7': + resolution: {integrity: sha512-HV5undWkKzcB4RZUusqOpcgxOaq6VOAH7zhhIr2g3G8NF/MlFO75SjOr2NfuSx0Mh40+1FqCkagKLJRykUWoFQ==} + + '@hapi/pinpoint@2.0.1': + resolution: {integrity: sha512-EKQmr16tM8s16vTT3cA5L0kZZcTMU5DUOZTuvpnY738m+jyP3JIUj+Mm1xc1rsLkGBQ/gVnfKYPwOmPg1tUR4Q==} + + '@hapi/tlds@1.1.3': + resolution: {integrity: sha512-QIvUMB5VZ8HMLZF9A2oWr3AFM430QC8oGd0L35y2jHpuW6bIIca6x/xL7zUf4J7L9WJ3qjz+iJII8ncaeMbpSg==} + engines: {node: '>=14.0.0'} + + '@hapi/topo@6.0.2': + resolution: {integrity: sha512-KR3rD5inZbGMrHmgPxsJ9dbi6zEK+C3ZwUwTa+eMwWLz7oijWUTWD2pMSNNYJAU6Qq+65NkxXjqHr/7LM2Xkqg==} + + '@humanwhocodes/config-array@0.13.0': + resolution: {integrity: sha512-DZLEEqFWQFiyK6h5YIeynKx7JlvCYWL0cImfSRXZ9l4Sg2efkFGTuFf6vzXjK1cq6IYkU+Eg/JizXw+TD2vRNw==} + engines: {node: '>=10.10.0'} + deprecated: Use @eslint/config-array instead + + '@humanwhocodes/module-importer@1.0.1': + resolution: {integrity: sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==} + engines: {node: '>=12.22'} + + '@humanwhocodes/object-schema@2.0.3': + resolution: {integrity: sha512-93zYdMES/c1D69yZiKDBj0V24vqNzB/koF26KPaagAfd3P/4gUlh3Dys5ogAK+Exi9QyzlD8x/08Zt7wIKcDcA==} + deprecated: Use @eslint/object-schema instead + + '@icons/material@0.2.4': + resolution: {integrity: sha512-QPcGmICAPbGLGb6F/yNf/KzKqvFx8z5qx3D1yFqVAjoFmXK35EgyW+cJ57Te3CNsmzblwtzakLGFqHPqrfb4Tw==} + peerDependencies: + react: '*' + + '@ioredis/commands@1.4.0': + resolution: {integrity: sha512-aFT2yemJJo+TZCmieA7qnYGQooOS7QfNmYrzGtsYd3g9j5iDP8AimYYAesf79ohjbLG12XxC4nG5DyEnC88AsQ==} + + '@isaacs/cliui@8.0.2': + resolution: {integrity: sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==} + engines: {node: '>=12'} + + '@istanbuljs/load-nyc-config@1.1.0': + resolution: {integrity: sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ==} + engines: {node: '>=8'} + + '@istanbuljs/schema@0.1.3': + resolution: {integrity: sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==} + engines: {node: '>=8'} + + '@jest/console@27.5.1': + resolution: {integrity: sha512-kZ/tNpS3NXn0mlXXXPNuDZnb4c0oZ20r4K5eemM2k30ZC3G0T02nXUvyhf5YdbXWHPEJLc9qGLxEZ216MdL+Zg==} + engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0} + + '@jest/console@28.1.3': + resolution: {integrity: sha512-QPAkP5EwKdK/bxIr6C1I4Vs0rm2nHiANzj/Z5X2JQkrZo6IqvC4ldZ9K95tF0HdidhA8Bo6egxSzUFPYKcEXLw==} + engines: {node: ^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0} + + '@jest/core@27.5.1': + resolution: {integrity: sha512-AK6/UTrvQD0Cd24NSqmIA6rKsu0tKIxfiCducZvqxYdmMisOYAsdItspT+fQDQYARPf8XgjAFZi0ogW2agH5nQ==} + engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0} + peerDependencies: + node-notifier: ^8.0.1 || ^9.0.0 || ^10.0.0 + peerDependenciesMeta: + node-notifier: + optional: true + + '@jest/diff-sequences@30.0.1': + resolution: {integrity: sha512-n5H8QLDJ47QqbCNn5SuFjCRDrOLEZ0h8vAHCK5RL9Ls7Xa8AQLa/YxAc9UjFqoEDM48muwtBGjtMY5cr0PLDCw==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} + + '@jest/environment@27.5.1': + resolution: {integrity: sha512-/WQjhPJe3/ghaol/4Bq480JKXV/Rfw8nQdN7f41fM8VDHLcxKXou6QyXAh3EFr9/bVG3x74z1NWDkP87EiY8gA==} + engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0} + + '@jest/expect-utils@30.2.0': + resolution: {integrity: sha512-1JnRfhqpD8HGpOmQp180Fo9Zt69zNtC+9lR+kT7NVL05tNXIi+QC8Csz7lfidMoVLPD3FnOtcmp0CEFnxExGEA==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} + + '@jest/fake-timers@27.5.1': + resolution: {integrity: sha512-/aPowoolwa07k7/oM3aASneNeBGCmGQsc3ugN4u6s4C/+s5M64MFo/+djTdiwcbQlRfFElGuDXWzaWj6QgKObQ==} + engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0} + + '@jest/get-type@30.1.0': + resolution: {integrity: sha512-eMbZE2hUnx1WV0pmURZY9XoXPkUYjpc55mb0CrhtdWLtzMQPFvu/rZkTLZFTsdaVQa+Tr4eWAteqcUzoawq/uA==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} + + '@jest/globals@27.5.1': + resolution: {integrity: sha512-ZEJNB41OBQQgGzgyInAv0UUfDDj3upmHydjieSxFvTRuZElrx7tXg/uVQ5hYVEwiXs3+aMsAeEc9X7xiSKCm4Q==} + engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0} + + '@jest/pattern@30.0.1': + resolution: {integrity: sha512-gWp7NfQW27LaBQz3TITS8L7ZCQ0TLvtmI//4OwlQRx4rnWxcPNIYjxZpDcN4+UlGxgm3jS5QPz8IPTCkb59wZA==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} + + '@jest/reporters@27.5.1': + resolution: {integrity: sha512-cPXh9hWIlVJMQkVk84aIvXuBB4uQQmFqZiacloFuGiP3ah1sbCxCosidXFDfqG8+6fO1oR2dTJTlsOy4VFmUfw==} + engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0} + peerDependencies: + node-notifier: ^8.0.1 || ^9.0.0 || ^10.0.0 + peerDependenciesMeta: + node-notifier: + optional: true + + '@jest/schemas@28.1.3': + resolution: {integrity: sha512-/l/VWsdt/aBXgjshLWOFyFt3IVdYypu5y2Wn2rOO1un6nkqIn8SLXzgIMYXFyYsRWDyF5EthmKJMIdJvk08grg==} + engines: {node: ^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0} + + '@jest/schemas@30.0.5': + resolution: {integrity: sha512-DmdYgtezMkh3cpU8/1uyXakv3tJRcmcXxBOcO0tbaozPwpmh4YMsnWrQm9ZmZMfa5ocbxzbFk6O4bDPEc/iAnA==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} + + '@jest/source-map@27.5.1': + resolution: {integrity: sha512-y9NIHUYF3PJRlHk98NdC/N1gl88BL08aQQgu4k4ZopQkCw9t9cV8mtl3TV8b/YCB8XaVTFrmUTAJvjsntDireg==} + engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0} + + '@jest/test-result@27.5.1': + resolution: {integrity: sha512-EW35l2RYFUcUQxFJz5Cv5MTOxlJIQs4I7gxzi2zVU7PJhOwfYq1MdC5nhSmYjX1gmMmLPvB3sIaC+BkcHRBfag==} + engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0} + + '@jest/test-result@28.1.3': + resolution: {integrity: sha512-kZAkxnSE+FqE8YjW8gNuoVkkC9I7S1qmenl8sGcDOLropASP+BkcGKwhXoyqQuGOGeYY0y/ixjrd/iERpEXHNg==} + engines: {node: ^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0} + + '@jest/test-sequencer@27.5.1': + resolution: {integrity: sha512-LCheJF7WB2+9JuCS7VB/EmGIdQuhtqjRNI9A43idHv3E4KltCTsPsLxvdaubFHSYwY/fNjMWjl6vNRhDiN7vpQ==} + engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0} + + '@jest/transform@27.5.1': + resolution: {integrity: sha512-ipON6WtYgl/1329g5AIJVbUuEh0wZVbdpGwC99Jw4LwuoBNS95MVphU6zOeD9pDkon+LLbFL7lOQRapbB8SCHw==} + engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0} + + '@jest/types@27.5.1': + resolution: {integrity: sha512-Cx46iJ9QpwQTjIdq5VJu2QTMMs3QlEjI0x1QbBP5W1+nMzyc2XmimiRR/CbX9TO0cPTeUlxWMOu8mslYsJ8DEw==} + engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0} + + '@jest/types@28.1.3': + resolution: {integrity: sha512-RyjiyMUZrKz/c+zlMFO1pm70DcIlST8AeWTkoUdZevew44wcNZQHsEVOiCVtgVnlFFD82FPaXycys58cf2muVQ==} + engines: {node: ^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0} + + '@jest/types@30.2.0': + resolution: {integrity: sha512-H9xg1/sfVvyfU7o3zMfBEjQ1gcsdeTMgqHoYdN79tuLqfTtuu7WckRA1R5whDwOzxaZAeMKTYWqP+WCAi0CHsg==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} + + '@jridgewell/gen-mapping@0.3.13': + resolution: {integrity: sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==} + + '@jridgewell/remapping@2.3.5': + resolution: {integrity: sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==} + + '@jridgewell/resolve-uri@3.1.2': + resolution: {integrity: sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==} + engines: {node: '>=6.0.0'} + + '@jridgewell/source-map@0.3.11': + resolution: {integrity: sha512-ZMp1V8ZFcPG5dIWnQLr3NSI1MiCU7UETdS/A0G8V/XWHvJv3ZsFqutJn1Y5RPmAPX6F3BiE397OqveU/9NCuIA==} + + '@jridgewell/sourcemap-codec@1.5.5': + resolution: {integrity: sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==} + + '@jridgewell/trace-mapping@0.3.31': + resolution: {integrity: sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==} + + '@jridgewell/trace-mapping@0.3.9': + resolution: {integrity: sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==} + + '@js-sdsl/ordered-map@4.4.2': + resolution: {integrity: sha512-iUKgm52T8HOE/makSxjqoWhe95ZJA1/G1sYsGev2JDKUSS14KAgg1LHb+Ba+IPow0xflbnSkOsZcO08C7w1gYw==} + + '@jsonjoy.com/base64@1.1.2': + resolution: {integrity: sha512-q6XAnWQDIMA3+FTiOYajoYqySkO+JSat0ytXGSuRdq9uXE7o92gzuQwQM14xaCRlBLGq3v5miDGC4vkVTn54xA==} + engines: {node: '>=10.0'} + peerDependencies: + tslib: '2' + + '@jsonjoy.com/base64@17.67.0': + resolution: {integrity: sha512-5SEsJGsm15aP8TQGkDfJvz9axgPwAEm98S5DxOuYe8e1EbfajcDmgeXXzccEjh+mLnjqEKrkBdjHWS5vFNwDdw==} + engines: {node: '>=10.0'} + peerDependencies: + tslib: '2' + + '@jsonjoy.com/buffers@1.2.1': + resolution: {integrity: sha512-12cdlDwX4RUM3QxmUbVJWqZ/mrK6dFQH4Zxq6+r1YXKXYBNgZXndx2qbCJwh3+WWkCSn67IjnlG3XYTvmvYtgA==} + engines: {node: '>=10.0'} + peerDependencies: + tslib: '2' + + '@jsonjoy.com/buffers@17.67.0': + resolution: {integrity: sha512-tfExRpYxBvi32vPs9ZHaTjSP4fHAfzSmcahOfNxtvGHcyJel+aibkPlGeBB+7AoC6hL7lXIE++8okecBxx7lcw==} + engines: {node: '>=10.0'} + peerDependencies: + tslib: '2' + + '@jsonjoy.com/codegen@1.0.0': + resolution: {integrity: sha512-E8Oy+08cmCf0EK/NMxpaJZmOxPqM+6iSe2S4nlSBrPZOORoDJILxtbSUEDKQyTamm/BVAhIGllOBNU79/dwf0g==} + engines: {node: '>=10.0'} + peerDependencies: + tslib: '2' + + '@jsonjoy.com/codegen@17.67.0': + resolution: {integrity: sha512-idnkUplROpdBOV0HMcwhsCUS5TRUi9poagdGs70A6S4ux9+/aPuKbh8+UYRTLYQHtXvAdNfQWXDqZEx5k4Dj2Q==} + engines: {node: '>=10.0'} + peerDependencies: + tslib: '2' + + '@jsonjoy.com/fs-core@4.57.1': + resolution: {integrity: sha512-YrEi/ZPmgc+GfdO0esBF04qv8boK9Dg9WpRQw/+vM8Qt3nnVIJWIa8HwZ/LXVZ0DB11XUROM8El/7yYTJX+WtA==} + engines: {node: '>=10.0'} + peerDependencies: + tslib: '2' + + '@jsonjoy.com/fs-fsa@4.57.1': + resolution: {integrity: sha512-ooEPvSW/HQDivPDPZMibHGKZf/QS4WRir1czGZmXmp3MsQqLECZEpN0JobrD8iV9BzsuwdIv+PxtWX9WpPLsIA==} + engines: {node: '>=10.0'} + peerDependencies: + tslib: '2' + + '@jsonjoy.com/fs-node-builtins@4.57.1': + resolution: {integrity: sha512-XHkFKQ5GSH3uxm8c3ZYXVrexGdscpWKIcMWKFQpMpMJc8gA3AwOMBJXJlgpdJqmrhPyQXxaY9nbkNeYpacC0Og==} + engines: {node: '>=10.0'} + peerDependencies: + tslib: '2' + + '@jsonjoy.com/fs-node-to-fsa@4.57.1': + resolution: {integrity: sha512-pqGHyWWzNck4jRfaGV39hkqpY5QjRUQ/nRbNT7FYbBa0xf4bDG+TE1Gt2KWZrSkrkZZDE3qZUjYMbjwSliX6pg==} + engines: {node: '>=10.0'} + peerDependencies: + tslib: '2' + + '@jsonjoy.com/fs-node-utils@4.57.1': + resolution: {integrity: sha512-vp+7ZzIB8v43G+GLXTS4oDUSQmhAsRz532QmmWBbdYA20s465JvwhkSFvX9cVTqRRAQg+vZ7zWDaIEh0lFe2gw==} + engines: {node: '>=10.0'} + peerDependencies: + tslib: '2' + + '@jsonjoy.com/fs-node@4.57.1': + resolution: {integrity: sha512-3YaKhP8gXEKN+2O49GLNfNb5l2gbnCFHyAaybbA2JkkbQP3dpdef7WcUaHAulg/c5Dg4VncHsA3NWAUSZMR5KQ==} + engines: {node: '>=10.0'} + peerDependencies: + tslib: '2' + + '@jsonjoy.com/fs-print@4.57.1': + resolution: {integrity: sha512-Ynct7ZJmfk6qoXDOKfpovNA36ITUx8rChLmRQtW08J73VOiuNsU8PB6d/Xs7fxJC2ohWR3a5AqyjmLojfrw5yw==} + engines: {node: '>=10.0'} + peerDependencies: + tslib: '2' + + '@jsonjoy.com/fs-snapshot@4.57.1': + resolution: {integrity: sha512-/oG8xBNFMbDXTq9J7vepSA1kerS5vpgd3p5QZSPd+nX59uwodGJftI51gDYyHRpP57P3WCQf7LHtBYPqwUg2Bg==} + engines: {node: '>=10.0'} + peerDependencies: + tslib: '2' + + '@jsonjoy.com/json-pack@1.21.0': + resolution: {integrity: sha512-+AKG+R2cfZMShzrF2uQw34v3zbeDYUqnQ+jg7ORic3BGtfw9p/+N6RJbq/kkV8JmYZaINknaEQ2m0/f693ZPpg==} + engines: {node: '>=10.0'} + peerDependencies: + tslib: '2' + + '@jsonjoy.com/json-pack@17.67.0': + resolution: {integrity: sha512-t0ejURcGaZsn1ClbJ/3kFqSOjlryd92eQY465IYrezsXmPcfHPE/av4twRSxf6WE+TkZgLY+71vCZbiIiFKA/w==} + engines: {node: '>=10.0'} + peerDependencies: + tslib: '2' + + '@jsonjoy.com/json-pointer@1.0.2': + resolution: {integrity: sha512-Fsn6wM2zlDzY1U+v4Nc8bo3bVqgfNTGcn6dMgs6FjrEnt4ZCe60o6ByKRjOGlI2gow0aE/Q41QOigdTqkyK5fg==} + engines: {node: '>=10.0'} + peerDependencies: + tslib: '2' + + '@jsonjoy.com/json-pointer@17.67.0': + resolution: {integrity: sha512-+iqOFInH+QZGmSuaybBUNdh7yvNrXvqR+h3wjXm0N/3JK1EyyFAeGJvqnmQL61d1ARLlk/wJdFKSL+LHJ1eaUA==} + engines: {node: '>=10.0'} + peerDependencies: + tslib: '2' + + '@jsonjoy.com/util@1.9.0': + resolution: {integrity: sha512-pLuQo+VPRnN8hfPqUTLTHk126wuYdXVxE6aDmjSeV4NCAgyxWbiOIeNJVtID3h1Vzpoi9m4jXezf73I6LgabgQ==} + engines: {node: '>=10.0'} + peerDependencies: + tslib: '2' + + '@jsonjoy.com/util@17.67.0': + resolution: {integrity: sha512-6+8xBaz1rLSohlGh68D1pdw3AwDi9xydm8QNlAFkvnavCJYSze+pxoW2VKP8p308jtlMRLs5NTHfPlZLd4w7ew==} + engines: {node: '>=10.0'} + peerDependencies: + tslib: '2' + + '@ladle/react-context@1.0.1': + resolution: {integrity: sha512-xVQ8siyOEQG6e4Knibes1uA3PTyXnqiMmfSmd5pIbkzeDty8NCBtYHhTXSlfmcDNEsw/G8OzNWo4VbyQAVDl2A==} + peerDependencies: + react: '>=16.14.0' + react-dom: '>=16.14.0' + + '@ladle/react@2.5.1': + resolution: {integrity: sha512-xTSs5dUIK+zQzHNo6i3SDuA9lu0k8nUJ7/RNeNJ7oTkX05FfBSxCUeIKeUAjaVNm/axvylVhdGDm+yLBIxq8EA==} + engines: {node: '>=16.0.0'} + hasBin: true + peerDependencies: + react: '>=16.14.0' + react-dom: '>=16.14.0' + + '@leichtgewicht/ip-codec@2.0.5': + resolution: {integrity: sha512-Vo+PSpZG2/fmgmiNzYK9qWRh8h/CHrwD0mo1h1DzL4yzHNSfWYujGTYsWGreD000gcgmZ7K4Ys6Tx9TxtsKdDw==} + + '@lezer/common@1.2.3': + resolution: {integrity: sha512-w7ojc8ejBqr2REPsWxJjrMFsA/ysDCFICn8zEOR9mrqzOu2amhITYuLD8ag6XZf0CFXDrhKqw7+tW8cX66NaDA==} + + '@lezer/common@1.5.2': + resolution: {integrity: sha512-sxQE460fPZyU3sdc8lafxiPwJHBzZRy/udNFynGQky1SePYBdhkBl1kOagA9uT3pxR8K09bOrmTUqA9wb/PjSQ==} + + '@lezer/css@1.3.3': + resolution: {integrity: sha512-RzBo8r+/6QJeow7aPHIpGVIH59xTcJXp399820gZoMo9noQDRVpJLheIBUicYwKcsbOYoBRoLZlf2720dG/4Tg==} + + '@lezer/highlight@1.2.1': + resolution: {integrity: sha512-Z5duk4RN/3zuVO7Jq0pGLJ3qynpxUVsh7IbUbGj88+uV2ApSAn6kWg2au3iJb+0Zi7kKtqffIESgNcRXWZWmSA==} + + '@lezer/html@1.3.13': + resolution: {integrity: sha512-oI7n6NJml729m7pjm9lvLvmXbdoMoi2f+1pwSDJkl9d68zGr7a9Btz8NdHTGQZtW2DA25ybeuv/SyDb9D5tseg==} + + '@lezer/javascript@1.5.4': + resolution: {integrity: sha512-vvYx3MhWqeZtGPwDStM2dwgljd5smolYD2lR2UyFcHfxbBQebqx8yjmFmxtJ/E6nN6u1D9srOiVWm3Rb4tmcUA==} + + '@lezer/json@1.0.3': + resolution: {integrity: sha512-BP9KzdF9Y35PDpv04r0VeSTKDeox5vVr3efE7eBbx3r4s3oNLfunchejZhjArmeieBH+nVOpgIiBJpEAv8ilqQ==} + + '@lezer/lr@1.4.2': + resolution: {integrity: sha512-pu0K1jCIdnQ12aWNaAVU5bzi7Bd1w54J3ECgANPmYLtQKP0HBj2cE/5coBD66MT10xbtIuUr7tg0Shbsvk0mDA==} + + '@lezer/markdown@1.6.3': + resolution: {integrity: sha512-jpGm5Ps+XErS+xA4urw7ogEGkeZOahVQF21Z6oECF0sj+2liwZopd2+I8uH5I/vZsRuuze3OxBREIANLf6KKUw==} + + '@mapbox/node-pre-gyp@1.0.11': + resolution: {integrity: sha512-Yhlar6v9WQgUp/He7BdgzOz8lqMQ8sU+jkCq7Wx8Myc5YFJLbEe7lgui/V7G1qB1DJykHSGwreceSaD60Y0PUQ==} + hasBin: true + + '@marijn/find-cluster-break@1.0.2': + resolution: {integrity: sha512-l0h88YhZFyKdXIFNfSWpyjStDjGHwZ/U7iobcK1cQQD8sejsONdQtTVU+1wVN1PBw40PiiHB1vA5S7VTfQiP9g==} + + '@microsoft/fetch-event-source@2.0.1': + resolution: {integrity: sha512-W6CLUJ2eBMw3Rec70qrsEW0jOm/3twwJv21mrmj2yORiaVmVYGS4sSS5yUwvQc1ZlDLYGPnClVWmUUMagKNsfA==} + + '@mongodb-js/saslprep@1.3.1': + resolution: {integrity: sha512-6nZrq5kfAz0POWyhljnbWQQJQ5uT8oE2ddX303q1uY0tWsivWKgBDXBBvuFPwOqRRalXJuVO9EjOdVtuhLX0zg==} + + '@mui/base@5.0.0-beta.27': + resolution: {integrity: sha512-duL37qxihT1N0pW/gyXVezP7SttLkF+cLAs/y6g6ubEFmVadjbnZ45SeF12/vAiKzqwf5M0uFH1cczIPXFZygA==} + engines: {node: '>=12.0.0'} + deprecated: This package has been replaced by @base-ui/react + peerDependencies: + '@types/react': ^17.0.0 || ^18.0.0 + react: ^17.0.0 || ^18.0.0 + react-dom: ^17.0.0 || ^18.0.0 + peerDependenciesMeta: + '@types/react': + optional: true + + '@mui/base@5.0.0-beta.40': + resolution: {integrity: sha512-I/lGHztkCzvwlXpjD2+SNmvNQvB4227xBXhISPjEaJUXGImOQ9f3D2Yj/T3KasSI/h0MLWy74X0J6clhPmsRbQ==} + engines: {node: '>=12.0.0'} + deprecated: This package has been replaced by @base-ui/react + peerDependencies: + '@types/react': ^17.0.0 || ^18.0.0 + react: ^17.0.0 || ^18.0.0 + react-dom: ^17.0.0 || ^18.0.0 + peerDependenciesMeta: + '@types/react': + optional: true + + '@mui/core-downloads-tracker@5.18.0': + resolution: {integrity: sha512-jbhwoQ1AY200PSSOrNXmrFCaSDSJWP7qk6urkTmIirvRXDROkqe+QwcLlUiw/PrREwsIF/vm3/dAXvjlMHF0RA==} + + '@mui/icons-material@5.18.0': + resolution: {integrity: sha512-1s0vEZj5XFXDMmz3Arl/R7IncFqJ+WQ95LDp1roHWGDE2oCO3IS4/hmiOv1/8SD9r6B7tv9GLiqVZYHo+6PkTg==} + engines: {node: '>=12.0.0'} + peerDependencies: + '@mui/material': ^5.0.0 + '@types/react': ^17.0.0 || ^18.0.0 || ^19.0.0 + react: ^17.0.0 || ^18.0.0 || ^19.0.0 + peerDependenciesMeta: + '@types/react': + optional: true + + '@mui/lab@5.0.0-alpha.156': + resolution: {integrity: sha512-OUAckFeqlAG6aIBG1Ud1fDCBqnU1wltWZYHsA7YCGzRBykNzQC/W/dYddp+RJLu0BgYpMiXwPXq2Hg0ERVtaog==} + engines: {node: '>=12.0.0'} + peerDependencies: + '@emotion/react': ^11.5.0 + '@emotion/styled': ^11.3.0 + '@mui/material': '>=5.10.11' + '@types/react': ^17.0.0 || ^18.0.0 + react: ^17.0.0 || ^18.0.0 + react-dom: ^17.0.0 || ^18.0.0 + peerDependenciesMeta: + '@emotion/react': + optional: true + '@emotion/styled': + optional: true + '@types/react': + optional: true + + '@mui/material@5.15.0': + resolution: {integrity: sha512-60CDI/hQNwJv9a3vEZtFG7zz0USdQhVwpBd3fZqrzhuXSdiMdYMaZcCXeX/KMuNq0ZxQEAZd74Pv+gOb408QVA==} + engines: {node: '>=12.0.0'} + peerDependencies: + '@emotion/react': ^11.5.0 + '@emotion/styled': ^11.3.0 + '@types/react': ^17.0.0 || ^18.0.0 + react: ^17.0.0 || ^18.0.0 + react-dom: ^17.0.0 || ^18.0.0 + peerDependenciesMeta: + '@emotion/react': + optional: true + '@emotion/styled': + optional: true + '@types/react': + optional: true + + '@mui/private-theming@5.17.1': + resolution: {integrity: sha512-XMxU0NTYcKqdsG8LRmSoxERPXwMbp16sIXPcLVgLGII/bVNagX0xaheWAwFv8+zDK7tI3ajllkuD3GZZE++ICQ==} + engines: {node: '>=12.0.0'} + peerDependencies: + '@types/react': ^17.0.0 || ^18.0.0 || ^19.0.0 + react: ^17.0.0 || ^18.0.0 || ^19.0.0 + peerDependenciesMeta: + '@types/react': + optional: true + + '@mui/styled-engine@5.18.0': + resolution: {integrity: sha512-BN/vKV/O6uaQh2z5rXV+MBlVrEkwoS/TK75rFQ2mjxA7+NBo8qtTAOA4UaM0XeJfn7kh2wZ+xQw2HAx0u+TiBg==} + engines: {node: '>=12.0.0'} + peerDependencies: + '@emotion/react': ^11.4.1 + '@emotion/styled': ^11.3.0 + react: ^17.0.0 || ^18.0.0 || ^19.0.0 + peerDependenciesMeta: + '@emotion/react': + optional: true + '@emotion/styled': + optional: true + + '@mui/system@5.18.0': + resolution: {integrity: sha512-ojZGVcRWqWhu557cdO3pWHloIGJdzVtxs3rk0F9L+x55LsUjcMUVkEhiF7E4TMxZoF9MmIHGGs0ZX3FDLAf0Xw==} + engines: {node: '>=12.0.0'} + peerDependencies: + '@emotion/react': ^11.5.0 + '@emotion/styled': ^11.3.0 + '@types/react': ^17.0.0 || ^18.0.0 || ^19.0.0 + react: ^17.0.0 || ^18.0.0 || ^19.0.0 + peerDependenciesMeta: + '@emotion/react': + optional: true + '@emotion/styled': + optional: true + '@types/react': + optional: true + + '@mui/types@7.2.24': + resolution: {integrity: sha512-3c8tRt/CbWZ+pEg7QpSwbdxOk36EfmhbKf6AGZsD1EcLDLTSZoxxJ86FVtcjxvjuhdyBiWKSTGZFaXCnidO2kw==} + peerDependencies: + '@types/react': ^17.0.0 || ^18.0.0 || ^19.0.0 + peerDependenciesMeta: + '@types/react': + optional: true + + '@mui/types@7.4.7': + resolution: {integrity: sha512-8vVje9rdEr1rY8oIkYgP+Su5Kwl6ik7O3jQ0wl78JGSmiZhRHV+vkjooGdKD8pbtZbutXFVTWQYshu2b3sG9zw==} + peerDependencies: + '@types/react': ^17.0.0 || ^18.0.0 || ^19.0.0 + peerDependenciesMeta: + '@types/react': + optional: true + + '@mui/utils@5.17.1': + resolution: {integrity: sha512-jEZ8FTqInt2WzxDV8bhImWBqeQRD99c/id/fq83H0ER9tFl+sfZlaAoCdznGvbSQQ9ividMxqSV2c7cC1vBcQg==} + engines: {node: '>=12.0.0'} + peerDependencies: + '@types/react': ^17.0.0 || ^18.0.0 || ^19.0.0 + react: ^17.0.0 || ^18.0.0 || ^19.0.0 + peerDependenciesMeta: + '@types/react': + optional: true + + '@mui/x-data-grid@6.8.0': + resolution: {integrity: sha512-L4CJb2zJDkyBXItfY1QwXt57ap1vIigPGiNrmuJZ8APS1jHafO1dftBWSfJyDJXsnQ5UsDBXsX1nagX51AebpQ==} + engines: {node: '>=14.0.0'} + peerDependencies: + '@mui/material': ^5.4.1 + '@mui/system': ^5.4.1 + react: ^17.0.0 || ^18.0.0 + react-dom: ^17.0.0 || ^18.0.0 + + '@mui/x-tree-view@6.17.0': + resolution: {integrity: sha512-09dc2D+Rjg2z8KOaxbUXyPi0aw7fm2jurEtV8Xw48xJ00joLWd5QJm1/v4CarEvaiyhTQzHImNqdgeJW8ZQB6g==} + engines: {node: '>=14.0.0'} + peerDependencies: + '@emotion/react': ^11.9.0 + '@emotion/styled': ^11.8.1 + '@mui/material': ^5.8.6 + '@mui/system': ^5.8.0 + react: ^17.0.0 || ^18.0.0 + react-dom: ^17.0.0 || ^18.0.0 + + '@nicolo-ribaudo/eslint-scope-5-internals@5.1.1-v1': + resolution: {integrity: sha512-54/JRvkLIzzDWshCWfuhadfrfZVPiElY8Fcgmg1HroEly/EDSszzhBAsarCux+D/kOslTRquNzuyGSmUSTTHGg==} + + '@nodelib/fs.scandir@2.1.5': + resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==} + engines: {node: '>= 8'} + + '@nodelib/fs.stat@2.0.5': + resolution: {integrity: sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==} + engines: {node: '>= 8'} + + '@nodelib/fs.walk@1.2.8': + resolution: {integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==} + engines: {node: '>= 8'} + + '@npmcli/fs@1.1.1': + resolution: {integrity: sha512-8KG5RD0GVP4ydEzRn/I4BNDuxDtqVbOdm8675T49OIG/NGhaK0pjPX7ZcDlvKYbA+ulvVK3ztfcF4uBdOxuJbQ==} + + '@npmcli/move-file@1.1.2': + resolution: {integrity: sha512-1SUf/Cg2GzGDyaf15aR9St9TWlb+XvbZXWpDx8YKs7MLzMH/BCeopv+y9vzrzgkfykCGuWOlSu3mZhj2+FQcrg==} + engines: {node: '>=10'} + deprecated: This functionality has been moved to @npmcli/fs + + '@opentelemetry/api-logs@0.214.0': + resolution: {integrity: sha512-40lSJeqYO8Uz2Yj7u94/SJWE/wONa7rmMKjI1ZcIjgf3MHNHv1OZUCrCETGuaRF62d5pQD1wKIW+L4lmSMTzZA==} + engines: {node: '>=8.0.0'} + + '@opentelemetry/api-logs@0.57.2': + resolution: {integrity: sha512-uIX52NnTM0iBh84MShlpouI7UKqkZ7MrUszTmaypHBu4r7NofznSnQRfJ+uUeDtQDj6w8eFGg5KBLDAwAPz1+A==} + engines: {node: '>=14'} + + '@opentelemetry/api@1.9.0': + resolution: {integrity: sha512-3giAOQvZiH5F9bMlMiv8+GSPMeqg0dbaeo58/0SlA9sxSqZhnUtxzX9/2FzyhS9sWQf5S0GJE0AKBrFqjpeYcg==} + engines: {node: '>=8.0.0'} + + '@opentelemetry/context-async-hooks@1.30.1': + resolution: {integrity: sha512-s5vvxXPVdjqS3kTLKMeBMvop9hbWkwzBpu+mUO2M7sZtlkyDJGwFe33wRKnbaYDo8ExRVBIIdwIGrqpxHuKttA==} + engines: {node: '>=14'} + peerDependencies: + '@opentelemetry/api': '>=1.0.0 <1.10.0' + + '@opentelemetry/context-async-hooks@2.6.1': + resolution: {integrity: sha512-XHzhwRNkBpeP8Fs/qjGrAf9r9PRv67wkJQ/7ZPaBQQ68DYlTBBx5MF9LvPx7mhuXcDessKK2b+DcxqwpgkcivQ==} + engines: {node: ^18.19.0 || >=20.6.0} + peerDependencies: + '@opentelemetry/api': '>=1.0.0 <1.10.0' + + '@opentelemetry/core@1.30.1': + resolution: {integrity: sha512-OOCM2C/QIURhJMuKaekP3TRBxBKxG/TWWA0TL2J6nXUtDnuCtccy49LUJF8xPFXMX+0LMcxFpCo8M9cGY1W6rQ==} + engines: {node: '>=14'} + peerDependencies: + '@opentelemetry/api': '>=1.0.0 <1.10.0' + + '@opentelemetry/core@2.6.1': + resolution: {integrity: sha512-8xHSGWpJP9wBxgBpnqGL0R3PbdWQndL1Qp50qrg71+B28zK5OQmUgcDKLJgzyAAV38t4tOyLMGDD60LneR5W8g==} + engines: {node: ^18.19.0 || >=20.6.0} + peerDependencies: + '@opentelemetry/api': '>=1.0.0 <1.10.0' + + '@opentelemetry/exporter-trace-otlp-grpc@0.214.0': + resolution: {integrity: sha512-FWRZ7AWoTryYhthralHkfXUuyO3l7cRsnr49WcDio1orl2a7KxT8aDZdwQtV1adzoUvZ9Gfo+IstElghCS4zfw==} + engines: {node: ^18.19.0 || >=20.6.0} + peerDependencies: + '@opentelemetry/api': ^1.3.0 + + '@opentelemetry/exporter-trace-otlp-grpc@0.57.2': + resolution: {integrity: sha512-gHU1vA3JnHbNxEXg5iysqCWxN9j83d7/epTYBZflqQnTyCC4N7yZXn/dMM+bEmyhQPGjhCkNZLx4vZuChH1PYw==} + engines: {node: '>=14'} + peerDependencies: + '@opentelemetry/api': ^1.3.0 + + '@opentelemetry/exporter-trace-otlp-proto@0.214.0': + resolution: {integrity: sha512-ON0spYWb2yAdQ9b+ItNyK0c6qdtcs+0eVR4YFJkhJL7agfT8sHFg0e5EesauSRiTHPZHiDobI92k77q0lwAmqg==} + engines: {node: ^18.19.0 || >=20.6.0} + peerDependencies: + '@opentelemetry/api': ^1.3.0 + + '@opentelemetry/exporter-trace-otlp-proto@0.57.2': + resolution: {integrity: sha512-awDdNRMIwDvUtoRYxRhja5QYH6+McBLtoz1q9BeEsskhZcrGmH/V1fWpGx8n+Rc+542e8pJA6y+aullbIzQmlw==} + engines: {node: '>=14'} + peerDependencies: + '@opentelemetry/api': ^1.3.0 + + '@opentelemetry/otlp-exporter-base@0.214.0': + resolution: {integrity: sha512-u1Gdv0/E9wP+apqWf7Wv2npXmgJtxsW2XL0TEv9FZloTZRuMBKmu8cYVXwS4Hm3q/f/3FuCnPTgiwYvIqRSpRg==} + engines: {node: ^18.19.0 || >=20.6.0} + peerDependencies: + '@opentelemetry/api': ^1.3.0 + + '@opentelemetry/otlp-exporter-base@0.57.2': + resolution: {integrity: sha512-XdxEzL23Urhidyebg5E6jZoaiW5ygP/mRjxLHixogbqwDy2Faduzb5N0o/Oi+XTIJu+iyxXdVORjXax+Qgfxag==} + engines: {node: '>=14'} + peerDependencies: + '@opentelemetry/api': ^1.3.0 + + '@opentelemetry/otlp-grpc-exporter-base@0.214.0': + resolution: {integrity: sha512-IDP6zcyA24RhNZ289MP6eToIZcinlmirHjX8v3zKCQ2ZhPpt5cGwkN91tCth337lqHIgWcTy90uKRiX/SzALDw==} + engines: {node: ^18.19.0 || >=20.6.0} + peerDependencies: + '@opentelemetry/api': ^1.3.0 + + '@opentelemetry/otlp-grpc-exporter-base@0.57.2': + resolution: {integrity: sha512-USn173KTWy0saqqRB5yU9xUZ2xdgb1Rdu5IosJnm9aV4hMTuFFRTUsQxbgc24QxpCHeoKzzCSnS/JzdV0oM2iQ==} + engines: {node: '>=14'} + peerDependencies: + '@opentelemetry/api': ^1.3.0 + + '@opentelemetry/otlp-transformer@0.214.0': + resolution: {integrity: sha512-DSaYcuBRh6uozfsWN3R8HsN0yDhCuWP7tOFdkUOVaWD1KVJg8m4qiLUsg/tNhTLS9HUYUcwNpwL2eroLtsZZ/w==} + engines: {node: ^18.19.0 || >=20.6.0} + peerDependencies: + '@opentelemetry/api': ^1.3.0 + + '@opentelemetry/otlp-transformer@0.57.2': + resolution: {integrity: sha512-48IIRj49gbQVK52jYsw70+Jv+JbahT8BqT2Th7C4H7RCM9d0gZ5sgNPoMpWldmfjvIsSgiGJtjfk9MeZvjhoig==} + engines: {node: '>=14'} + peerDependencies: + '@opentelemetry/api': ^1.3.0 + + '@opentelemetry/propagator-b3@1.30.1': + resolution: {integrity: sha512-oATwWWDIJzybAZ4pO76ATN5N6FFbOA1otibAVlS8v90B4S1wClnhRUk7K+2CHAwN1JKYuj4jh/lpCEG5BAqFuQ==} + engines: {node: '>=14'} + peerDependencies: + '@opentelemetry/api': '>=1.0.0 <1.10.0' + + '@opentelemetry/propagator-jaeger@1.30.1': + resolution: {integrity: sha512-Pj/BfnYEKIOImirH76M4hDaBSx6HyZ2CXUqk+Kj02m6BB80c/yo4BdWkn/1gDFfU+YPY+bPR2U0DKBfdxCKwmg==} + engines: {node: '>=14'} + peerDependencies: + '@opentelemetry/api': '>=1.0.0 <1.10.0' + + '@opentelemetry/resources@1.30.1': + resolution: {integrity: sha512-5UxZqiAgLYGFjS4s9qm5mBVo433u+dSPUFWVWXmLAD4wB65oMCoXaJP1KJa9DIYYMeHu3z4BZcStG3LC593cWA==} + engines: {node: '>=14'} + peerDependencies: + '@opentelemetry/api': '>=1.0.0 <1.10.0' + + '@opentelemetry/resources@2.6.1': + resolution: {integrity: sha512-lID/vxSuKWXM55XhAKNoYXu9Cutoq5hFdkbTdI/zDKQktXzcWBVhNsOkiZFTMU9UtEWuGRNe0HUgmsFldIdxVA==} + engines: {node: ^18.19.0 || >=20.6.0} + peerDependencies: + '@opentelemetry/api': '>=1.3.0 <1.10.0' + + '@opentelemetry/sdk-logs@0.214.0': + resolution: {integrity: sha512-zf6acnScjhsaBUU22zXZ/sLWim1dfhUAbGXdMmHmNG3LfBnQ3DKsOCITb2IZwoUsNNMTogqFKBnlIPPftUgGwA==} + engines: {node: ^18.19.0 || >=20.6.0} + peerDependencies: + '@opentelemetry/api': '>=1.4.0 <1.10.0' + + '@opentelemetry/sdk-logs@0.57.2': + resolution: {integrity: sha512-TXFHJ5c+BKggWbdEQ/inpgIzEmS2BGQowLE9UhsMd7YYlUfBQJ4uax0VF/B5NYigdM/75OoJGhAV3upEhK+3gg==} + engines: {node: '>=14'} + peerDependencies: + '@opentelemetry/api': '>=1.4.0 <1.10.0' + + '@opentelemetry/sdk-metrics@1.30.1': + resolution: {integrity: sha512-q9zcZ0Okl8jRgmy7eNW3Ku1XSgg3sDLa5evHZpCwjspw7E8Is4K/haRPDJrBcX3YSn/Y7gUvFnByNYEKQNbNog==} + engines: {node: '>=14'} + peerDependencies: + '@opentelemetry/api': '>=1.3.0 <1.10.0' + + '@opentelemetry/sdk-metrics@2.6.1': + resolution: {integrity: sha512-9t9hJHX15meBy2NmTJxL+NJfXmnausR2xUDvE19XQce0Qi/GBtDGamU8nS1RMbdgDmhgpm3VaOu2+fiS/SfTpQ==} + engines: {node: ^18.19.0 || >=20.6.0} + peerDependencies: + '@opentelemetry/api': '>=1.9.0 <1.10.0' + + '@opentelemetry/sdk-trace-base@1.30.1': + resolution: {integrity: sha512-jVPgBbH1gCy2Lb7X0AVQ8XAfgg0pJ4nvl8/IiQA6nxOsPvS+0zMJaFSs2ltXe0J6C8dqjcnpyqINDJmU30+uOg==} + engines: {node: '>=14'} + peerDependencies: + '@opentelemetry/api': '>=1.0.0 <1.10.0' + + '@opentelemetry/sdk-trace-base@2.6.1': + resolution: {integrity: sha512-r86ut4T1e8vNwB35CqCcKd45yzqH6/6Wzvpk2/cZB8PsPLlZFTvrh8yfOS3CYZYcUmAx4hHTZJ8AO8Dj8nrdhw==} + engines: {node: ^18.19.0 || >=20.6.0} + peerDependencies: + '@opentelemetry/api': '>=1.3.0 <1.10.0' + + '@opentelemetry/sdk-trace-node@1.30.1': + resolution: {integrity: sha512-cBjYOINt1JxXdpw1e5MlHmFRc5fgj4GW/86vsKFxJCJ8AL4PdVtYH41gWwl4qd4uQjqEL1oJVrXkSy5cnduAnQ==} + engines: {node: '>=14'} + peerDependencies: + '@opentelemetry/api': '>=1.0.0 <1.10.0' + + '@opentelemetry/sdk-trace-node@2.6.1': + resolution: {integrity: sha512-Hh2i4FwHWRFhnO2Q/p6svMxy8MPsNCG0uuzUY3glqm0rwM0nQvbTO1dXSp9OqQoTKXcQzaz9q1f65fsurmOhNw==} + engines: {node: ^18.19.0 || >=20.6.0} + peerDependencies: + '@opentelemetry/api': '>=1.0.0 <1.10.0' + + '@opentelemetry/semantic-conventions@1.28.0': + resolution: {integrity: sha512-lp4qAiMTD4sNWW4DbKLBkfiMZ4jbAboJIGOQr5DvciMRI494OapieI9qiODpOt0XBr1LjIDy1xAGAnVs5supTA==} + engines: {node: '>=14'} + + '@opentelemetry/semantic-conventions@1.37.0': + resolution: {integrity: sha512-JD6DerIKdJGmRp4jQyX5FlrQjA4tjOw1cvfsPAZXfOOEErMUHjPcPSICS+6WnM0nB0efSFARh0KAZss+bvExOA==} + engines: {node: '>=14'} + + '@parcel/watcher-android-arm64@2.5.1': + resolution: {integrity: sha512-KF8+j9nNbUN8vzOFDpRMsaKBHZ/mcjEjMToVMJOhTozkDonQFFrRcfdLWn6yWKCmJKmdVxSgHiYvTCef4/qcBA==} + engines: {node: '>= 10.0.0'} + cpu: [arm64] + os: [android] + + '@parcel/watcher-darwin-arm64@2.5.1': + resolution: {integrity: sha512-eAzPv5osDmZyBhou8PoF4i6RQXAfeKL9tjb3QzYuccXFMQU0ruIc/POh30ePnaOyD1UXdlKguHBmsTs53tVoPw==} + engines: {node: '>= 10.0.0'} + cpu: [arm64] + os: [darwin] + + '@parcel/watcher-darwin-x64@2.5.1': + resolution: {integrity: sha512-1ZXDthrnNmwv10A0/3AJNZ9JGlzrF82i3gNQcWOzd7nJ8aj+ILyW1MTxVk35Db0u91oD5Nlk9MBiujMlwmeXZg==} + engines: {node: '>= 10.0.0'} + cpu: [x64] + os: [darwin] + + '@parcel/watcher-freebsd-x64@2.5.1': + resolution: {integrity: sha512-SI4eljM7Flp9yPuKi8W0ird8TI/JK6CSxju3NojVI6BjHsTyK7zxA9urjVjEKJ5MBYC+bLmMcbAWlZ+rFkLpJQ==} + engines: {node: '>= 10.0.0'} + cpu: [x64] + os: [freebsd] + + '@parcel/watcher-linux-arm-glibc@2.5.1': + resolution: {integrity: sha512-RCdZlEyTs8geyBkkcnPWvtXLY44BCeZKmGYRtSgtwwnHR4dxfHRG3gR99XdMEdQ7KeiDdasJwwvNSF5jKtDwdA==} + engines: {node: '>= 10.0.0'} + cpu: [arm] + os: [linux] + + '@parcel/watcher-linux-arm-musl@2.5.1': + resolution: {integrity: sha512-6E+m/Mm1t1yhB8X412stiKFG3XykmgdIOqhjWj+VL8oHkKABfu/gjFj8DvLrYVHSBNC+/u5PeNrujiSQ1zwd1Q==} + engines: {node: '>= 10.0.0'} + cpu: [arm] + os: [linux] + + '@parcel/watcher-linux-arm64-glibc@2.5.1': + resolution: {integrity: sha512-LrGp+f02yU3BN9A+DGuY3v3bmnFUggAITBGriZHUREfNEzZh/GO06FF5u2kx8x+GBEUYfyTGamol4j3m9ANe8w==} + engines: {node: '>= 10.0.0'} + cpu: [arm64] + os: [linux] + + '@parcel/watcher-linux-arm64-musl@2.5.1': + resolution: {integrity: sha512-cFOjABi92pMYRXS7AcQv9/M1YuKRw8SZniCDw0ssQb/noPkRzA+HBDkwmyOJYp5wXcsTrhxO0zq1U11cK9jsFg==} + engines: {node: '>= 10.0.0'} + cpu: [arm64] + os: [linux] + + '@parcel/watcher-linux-x64-glibc@2.5.1': + resolution: {integrity: sha512-GcESn8NZySmfwlTsIur+49yDqSny2IhPeZfXunQi48DMugKeZ7uy1FX83pO0X22sHntJ4Ub+9k34XQCX+oHt2A==} + engines: {node: '>= 10.0.0'} + cpu: [x64] + os: [linux] + + '@parcel/watcher-linux-x64-musl@2.5.1': + resolution: {integrity: sha512-n0E2EQbatQ3bXhcH2D1XIAANAcTZkQICBPVaxMeaCVBtOpBZpWJuf7LwyWPSBDITb7In8mqQgJ7gH8CILCURXg==} + engines: {node: '>= 10.0.0'} + cpu: [x64] + os: [linux] + + '@parcel/watcher-win32-arm64@2.5.1': + resolution: {integrity: sha512-RFzklRvmc3PkjKjry3hLF9wD7ppR4AKcWNzH7kXR7GUe0Igb3Nz8fyPwtZCSquGrhU5HhUNDr/mKBqj7tqA2Vw==} + engines: {node: '>= 10.0.0'} + cpu: [arm64] + os: [win32] + + '@parcel/watcher-win32-ia32@2.5.1': + resolution: {integrity: sha512-c2KkcVN+NJmuA7CGlaGD1qJh1cLfDnQsHjE89E60vUEMlqduHGCdCLJCID5geFVM0dOtA3ZiIO8BoEQmzQVfpQ==} + engines: {node: '>= 10.0.0'} + cpu: [ia32] + os: [win32] + + '@parcel/watcher-win32-x64@2.5.1': + resolution: {integrity: sha512-9lHBdJITeNR++EvSQVUcaZoWupyHfXe1jZvGZ06O/5MflPcuPLtEphScIBL+AiCWBO46tDSHzWyD0uDmmZqsgA==} + engines: {node: '>= 10.0.0'} + cpu: [x64] + os: [win32] + + '@parcel/watcher@2.5.1': + resolution: {integrity: sha512-dfUnCxiN9H4ap84DvD2ubjw+3vUNpstxa0TneY/Paat8a3R4uQZDLSvWjmznAY/DoahqTHl9V46HF/Zs3F29pg==} + engines: {node: '>= 10.0.0'} + + '@pkgjs/parseargs@0.11.0': + resolution: {integrity: sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==} + engines: {node: '>=14'} + + '@pmmmwh/react-refresh-webpack-plugin@0.5.17': + resolution: {integrity: sha512-tXDyE1/jzFsHXjhRZQ3hMl0IVhYe5qula43LDWIhVfjp9G/nT5OQY5AORVOrkEGAUltBJOfOWeETbmhm6kHhuQ==} + engines: {node: '>= 10.13'} + peerDependencies: + '@types/webpack': 4.x || 5.x + react-refresh: '>=0.10.0 <1.0.0' + sockjs-client: ^1.4.0 + type-fest: '>=0.17.0 <5.0.0' + webpack: '>=4.43.0 <6.0.0' + webpack-dev-server: 3.x || 4.x || 5.x + webpack-hot-middleware: 2.x + webpack-plugin-serve: 0.x || 1.x + peerDependenciesMeta: + '@types/webpack': + optional: true + sockjs-client: + optional: true + type-fest: + optional: true + webpack-dev-server: + optional: true + webpack-hot-middleware: + optional: true + webpack-plugin-serve: + optional: true + + '@popperjs/core@2.11.8': + resolution: {integrity: sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A==} + + '@protobufjs/aspromise@1.1.2': + resolution: {integrity: sha512-j+gKExEuLmKwvz3OgROXtrJ2UG2x8Ch2YZUxahh+s1F2HZ+wAceUNLkvy6zKCPVRkU++ZWQrdxsUeQXmcg4uoQ==} + + '@protobufjs/base64@1.1.2': + resolution: {integrity: sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg==} + + '@protobufjs/codegen@2.0.4': + resolution: {integrity: sha512-YyFaikqM5sH0ziFZCN3xDC7zeGaB/d0IUb9CATugHWbd1FRFwWwt4ld4OYMPWu5a3Xe01mGAULCdqhMlPl29Jg==} + + '@protobufjs/eventemitter@1.1.0': + resolution: {integrity: sha512-j9ednRT81vYJ9OfVuXG6ERSTdEL1xVsNgqpkxMsbIabzSo3goCjDIveeGv5d03om39ML71RdmrGNjG5SReBP/Q==} + + '@protobufjs/fetch@1.1.0': + resolution: {integrity: sha512-lljVXpqXebpsijW71PZaCYeIcE5on1w5DlQy5WH6GLbFryLUrBD4932W/E2BSpfRJWseIL4v/KPgBFxDOIdKpQ==} + + '@protobufjs/float@1.0.2': + resolution: {integrity: sha512-Ddb+kVXlXst9d+R9PfTIxh1EdNkgoRe5tOX6t01f1lYWOvJnSPDBlG241QLzcyPdoNTsblLUdujGSE4RzrTZGQ==} + + '@protobufjs/inquire@1.1.0': + resolution: {integrity: sha512-kdSefcPdruJiFMVSbn801t4vFK7KB/5gd2fYvrxhuJYg8ILrmn9SKSX2tZdV6V+ksulWqS7aXjBcRXl3wHoD9Q==} + + '@protobufjs/path@1.1.2': + resolution: {integrity: sha512-6JOcJ5Tm08dOHAbdR3GrvP+yUUfkjG5ePsHYczMFLq3ZmMkAD98cDgcT2iA1lJ9NVwFd4tH/iSSoe44YWkltEA==} + + '@protobufjs/pool@1.1.0': + resolution: {integrity: sha512-0kELaGSIDBKvcgS4zkjz1PeddatrjYcmMWOlAuAPwAeccUrPHdUqo/J6LiymHHEiJT5NrF1UVwxY14f+fy4WQw==} + + '@protobufjs/utf8@1.1.0': + resolution: {integrity: sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw==} + + '@react-keycloak/core@3.2.0': + resolution: {integrity: sha512-1yzU7gQzs+6E1v6hGqxy0Q+kpMHg9sEcke2yxZR29WoU8KNE8E50xS6UbI8N7rWsgyYw8r9W1cUPCOF48MYjzw==} + peerDependencies: + react: '>=16' + + '@react-keycloak/web@3.4.0': + resolution: {integrity: sha512-yKKSCyqBtn7dt+VckYOW1IM5NW999pPkxDZOXqJ6dfXPXstYhOQCkTZqh8l7UL14PkpsoaHDh7hSJH8whah01g==} + peerDependencies: + keycloak-js: '>=9.0.2' + react: '>=16.8' + react-dom: '>=16.8' + typescript: '>=3.8' + peerDependenciesMeta: + typescript: + optional: true + + '@reactflow/background@11.3.14': + resolution: {integrity: sha512-Gewd7blEVT5Lh6jqrvOgd4G6Qk17eGKQfsDXgyRSqM+CTwDqRldG2LsWN4sNeno6sbqVIC2fZ+rAUBFA9ZEUDA==} + peerDependencies: + react: '>=17' + react-dom: '>=17' + + '@reactflow/controls@11.2.14': + resolution: {integrity: sha512-MiJp5VldFD7FrqaBNIrQ85dxChrG6ivuZ+dcFhPQUwOK3HfYgX2RHdBua+gx+40p5Vw5It3dVNp/my4Z3jF0dw==} + peerDependencies: + react: '>=17' + react-dom: '>=17' + + '@reactflow/core@11.11.4': + resolution: {integrity: sha512-H4vODklsjAq3AMq6Np4LE12i1I4Ta9PrDHuBR9GmL8uzTt2l2jh4CiQbEMpvMDcp7xi4be0hgXj+Ysodde/i7Q==} + peerDependencies: + react: '>=17' + react-dom: '>=17' + + '@reactflow/minimap@11.7.14': + resolution: {integrity: sha512-mpwLKKrEAofgFJdkhwR5UQ1JYWlcAAL/ZU/bctBkuNTT1yqV+y0buoNVImsRehVYhJwffSWeSHaBR5/GJjlCSQ==} + peerDependencies: + react: '>=17' + react-dom: '>=17' + + '@reactflow/node-resizer@2.2.14': + resolution: {integrity: sha512-fwqnks83jUlYr6OHcdFEedumWKChTHRGw/kbCxj0oqBd+ekfs+SIp4ddyNU0pdx96JIm5iNFS0oNrmEiJbbSaA==} + peerDependencies: + react: '>=17' + react-dom: '>=17' + + '@reactflow/node-toolbar@1.3.14': + resolution: {integrity: sha512-rbynXQnH/xFNu4P9H+hVqlEUafDCkEoCy0Dg9mG22Sg+rY/0ck6KkrAQrYrTgXusd+cEJOMK0uOOFCK2/5rSGQ==} + peerDependencies: + react: '>=17' + react-dom: '>=17' + + '@redis/bloom@1.2.0': + resolution: {integrity: sha512-HG2DFjYKbpNmVXsa0keLHp/3leGJz1mjh09f2RLGGLQZzSHpkmZWuwJbAvo3QcRY8p80m5+ZdXZdYOSBLlp7Cg==} + peerDependencies: + '@redis/client': ^1.0.0 + + '@redis/client@1.6.1': + resolution: {integrity: sha512-/KCsg3xSlR+nCK8/8ZYSknYxvXHwubJrU82F3Lm1Fp6789VQ0/3RJKfsmRXjqfaTA++23CvC3hqmqe/2GEt6Kw==} + engines: {node: '>=14'} + + '@redis/graph@1.1.1': + resolution: {integrity: sha512-FEMTcTHZozZciLRl6GiiIB4zGm5z5F3F6a6FZCyrfxdKOhFlGkiAqlexWMBzCi4DcRoyiOsuLfW+cjlGWyExOw==} + peerDependencies: + '@redis/client': ^1.0.0 + + '@redis/json@1.0.7': + resolution: {integrity: sha512-6UyXfjVaTBTJtKNG4/9Z8PSpKE6XgSyEb8iwaqDcy+uKrd/DGYHTWkUdnQDyzm727V7p21WUMhsqz5oy65kPcQ==} + peerDependencies: + '@redis/client': ^1.0.0 + + '@redis/search@1.2.0': + resolution: {integrity: sha512-tYoDBbtqOVigEDMAcTGsRlMycIIjwMCgD8eR2t0NANeQmgK/lvxNAvYyb6bZDD4frHRhIHkJu2TBRvB0ERkOmw==} + peerDependencies: + '@redis/client': ^1.0.0 + + '@redis/time-series@1.1.0': + resolution: {integrity: sha512-c1Q99M5ljsIuc4YdaCwfUEXsofakb9c8+Zse2qxTadu8TalLXuAESzLvFAvNVbkmSlvlzIQOLpBCmWI9wTOt+g==} + peerDependencies: + '@redis/client': ^1.0.0 + + '@reduxjs/toolkit@2.11.2': + resolution: {integrity: sha512-Kd6kAHTA6/nUpp8mySPqj3en3dm0tdMIgbttnQ1xFMVpufoj+ADi8pXLBsd4xzTRHQa7t/Jv8W5UnCuW4kuWMQ==} + peerDependencies: + react: ^16.9.0 || ^17.0.0 || ^18 || ^19 + react-redux: ^7.2.1 || ^8.1.3 || ^9.0.0 + peerDependenciesMeta: + react: + optional: true + react-redux: + optional: true + + '@rolldown/pluginutils@1.0.0-beta.27': + resolution: {integrity: sha512-+d0F4MKMCbeVUJwG96uQ4SgAznZNSq93I3V+9NHA4OpvqG8mRCpGdKmK8l/dl02h2CCDHwW2FqilnTyDcAnqjA==} + + '@rollup/plugin-babel@5.3.1': + resolution: {integrity: sha512-WFfdLWU/xVWKeRQnKmIAQULUI7Il0gZnBIH/ZFO069wYIfPu+8zrfp/KMW0atmELoRDq8FbiP3VCss9MhCut7Q==} + engines: {node: '>= 10.0.0'} + peerDependencies: + '@babel/core': ^7.0.0 + '@types/babel__core': ^7.1.9 + rollup: 4.45.0 + peerDependenciesMeta: + '@types/babel__core': + optional: true + + '@rollup/plugin-node-resolve@11.2.1': + resolution: {integrity: sha512-yc2n43jcqVyGE2sqV5/YCmocy9ArjVAP/BeXyTtADTBBX6V0e5UMqwO8CdQ0kzjb6zu5P1qMzsScCMRvE9OlVg==} + engines: {node: '>= 10.0.0'} + peerDependencies: + rollup: 4.45.0 + + '@rollup/plugin-replace@2.4.2': + resolution: {integrity: sha512-IGcu+cydlUMZ5En85jxHH4qj2hta/11BHq95iHEyb2sbgiN0eCdzvUcHw5gt9pBL5lTi4JDYJ1acCoMGpTvEZg==} + peerDependencies: + rollup: 4.45.0 + + '@rollup/pluginutils@3.1.0': + resolution: {integrity: sha512-GksZ6pr6TpIjHm8h9lSQ8pi8BE9VeubNT0OMJ3B5uZJ8pz73NPiqOtCog/x2/QzM1ENChPKxMDhiQuRHsqc+lg==} + engines: {node: '>= 8.0.0'} + peerDependencies: + rollup: 4.45.0 + + '@rollup/rollup-android-arm-eabi@4.45.0': + resolution: {integrity: sha512-2o/FgACbji4tW1dzXOqAV15Eu7DdgbKsF2QKcxfG4xbh5iwU7yr5RRP5/U+0asQliSYv5M4o7BevlGIoSL0LXg==} + cpu: [arm] + os: [android] + + '@rollup/rollup-android-arm64@4.45.0': + resolution: {integrity: sha512-PSZ0SvMOjEAxwZeTx32eI/j5xSYtDCRxGu5k9zvzoY77xUNssZM+WV6HYBLROpY5CkXsbQjvz40fBb7WPwDqtQ==} + cpu: [arm64] + os: [android] + + '@rollup/rollup-darwin-arm64@4.45.0': + resolution: {integrity: sha512-BA4yPIPssPB2aRAWzmqzQ3y2/KotkLyZukVB7j3psK/U3nVJdceo6qr9pLM2xN6iRP/wKfxEbOb1yrlZH6sYZg==} + cpu: [arm64] + os: [darwin] + + '@rollup/rollup-darwin-x64@4.45.0': + resolution: {integrity: sha512-Pr2o0lvTwsiG4HCr43Zy9xXrHspyMvsvEw4FwKYqhli4FuLE5FjcZzuQ4cfPe0iUFCvSQG6lACI0xj74FDZKRA==} + cpu: [x64] + os: [darwin] + + '@rollup/rollup-freebsd-arm64@4.45.0': + resolution: {integrity: sha512-lYE8LkE5h4a/+6VnnLiL14zWMPnx6wNbDG23GcYFpRW1V9hYWHAw9lBZ6ZUIrOaoK7NliF1sdwYGiVmziUF4vA==} + cpu: [arm64] + os: [freebsd] + + '@rollup/rollup-freebsd-x64@4.45.0': + resolution: {integrity: sha512-PVQWZK9sbzpvqC9Q0GlehNNSVHR+4m7+wET+7FgSnKG3ci5nAMgGmr9mGBXzAuE5SvguCKJ6mHL6vq1JaJ/gvw==} + cpu: [x64] + os: [freebsd] + + '@rollup/rollup-linux-arm-gnueabihf@4.45.0': + resolution: {integrity: sha512-hLrmRl53prCcD+YXTfNvXd776HTxNh8wPAMllusQ+amcQmtgo3V5i/nkhPN6FakW+QVLoUUr2AsbtIRPFU3xIA==} + cpu: [arm] + os: [linux] + + '@rollup/rollup-linux-arm-musleabihf@4.45.0': + resolution: {integrity: sha512-XBKGSYcrkdiRRjl+8XvrUR3AosXU0NvF7VuqMsm7s5nRy+nt58ZMB19Jdp1RdqewLcaYnpk8zeVs/4MlLZEJxw==} + cpu: [arm] + os: [linux] + + '@rollup/rollup-linux-arm64-gnu@4.45.0': + resolution: {integrity: sha512-fRvZZPUiBz7NztBE/2QnCS5AtqLVhXmUOPj9IHlfGEXkapgImf4W9+FSkL8cWqoAjozyUzqFmSc4zh2ooaeF6g==} + cpu: [arm64] + os: [linux] + + '@rollup/rollup-linux-arm64-musl@4.45.0': + resolution: {integrity: sha512-Btv2WRZOcUGi8XU80XwIvzTg4U6+l6D0V6sZTrZx214nrwxw5nAi8hysaXj/mctyClWgesyuxbeLylCBNauimg==} + cpu: [arm64] + os: [linux] + + '@rollup/rollup-linux-loongarch64-gnu@4.45.0': + resolution: {integrity: sha512-Li0emNnwtUZdLwHjQPBxn4VWztcrw/h7mgLyHiEI5Z0MhpeFGlzaiBHpSNVOMB/xucjXTTcO+dhv469Djr16KA==} + cpu: [loong64] + os: [linux] + + '@rollup/rollup-linux-powerpc64le-gnu@4.45.0': + resolution: {integrity: sha512-sB8+pfkYx2kvpDCfd63d5ScYT0Fz1LO6jIb2zLZvmK9ob2D8DeVqrmBDE0iDK8KlBVmsTNzrjr3G1xV4eUZhSw==} + cpu: [ppc64] + os: [linux] + + '@rollup/rollup-linux-riscv64-gnu@4.45.0': + resolution: {integrity: sha512-5GQ6PFhh7E6jQm70p1aW05G2cap5zMOvO0se5JMecHeAdj5ZhWEHbJ4hiKpfi1nnnEdTauDXxPgXae/mqjow9w==} + cpu: [riscv64] + os: [linux] + + '@rollup/rollup-linux-riscv64-musl@4.45.0': + resolution: {integrity: sha512-N/euLsBd1rekWcuduakTo/dJw6U6sBP3eUq+RXM9RNfPuWTvG2w/WObDkIvJ2KChy6oxZmOSC08Ak2OJA0UiAA==} + cpu: [riscv64] + os: [linux] + + '@rollup/rollup-linux-s390x-gnu@4.45.0': + resolution: {integrity: sha512-2l9sA7d7QdikL0xQwNMO3xURBUNEWyHVHfAsHsUdq+E/pgLTUcCE+gih5PCdmyHmfTDeXUWVhqL0WZzg0nua3g==} + cpu: [s390x] + os: [linux] + + '@rollup/rollup-linux-x64-gnu@4.45.0': + resolution: {integrity: sha512-XZdD3fEEQcwG2KrJDdEQu7NrHonPxxaV0/w2HpvINBdcqebz1aL+0vM2WFJq4DeiAVT6F5SUQas65HY5JDqoPw==} + cpu: [x64] + os: [linux] + + '@rollup/rollup-linux-x64-musl@4.45.0': + resolution: {integrity: sha512-7ayfgvtmmWgKWBkCGg5+xTQ0r5V1owVm67zTrsEY1008L5ro7mCyGYORomARt/OquB9KY7LpxVBZes+oSniAAQ==} + cpu: [x64] + os: [linux] + + '@rollup/rollup-win32-arm64-msvc@4.45.0': + resolution: {integrity: sha512-B+IJgcBnE2bm93jEW5kHisqvPITs4ddLOROAcOc/diBgrEiQJJ6Qcjby75rFSmH5eMGrqJryUgJDhrfj942apQ==} + cpu: [arm64] + os: [win32] + + '@rollup/rollup-win32-ia32-msvc@4.45.0': + resolution: {integrity: sha512-+CXwwG66g0/FpWOnP/v1HnrGVSOygK/osUbu3wPRy8ECXjoYKjRAyfxYpDQOfghC5qPJYLPH0oN4MCOjwgdMug==} + cpu: [ia32] + os: [win32] + + '@rollup/rollup-win32-x64-msvc@4.45.0': + resolution: {integrity: sha512-SRf1cytG7wqcHVLrBc9VtPK4pU5wxiB/lNIkNmW2ApKXIg+RpqwHfsaEK+e7eH4A1BpI6BX/aBWXxZCIrJg3uA==} + cpu: [x64] + os: [win32] + + '@rtsao/scc@1.1.0': + resolution: {integrity: sha512-zt6OdqaDoOnJ1ZYsCYGt9YmWzDXl4vQdKTyJev62gFhRGKdx7mcT54V9KIjg+d2wi9EXsPvAPKe7i7WjfVWB8g==} + + '@rushstack/eslint-patch@1.13.0': + resolution: {integrity: sha512-2ih5qGw5SZJ+2fLZxP6Lr6Na2NTIgPRL/7Kmyuw0uIyBQnuhQ8fi8fzUTd38eIQmqp+GYLC00cI6WgtqHxBwmw==} + + '@sinclair/typebox@0.24.51': + resolution: {integrity: sha512-1P1OROm/rdubP5aFDSZQILU0vrLCJ4fvHt6EoqHEM+2D/G5MK3bIaymUKLit8Js9gbns5UyJnkP/TZROLw4tUA==} + + '@sinclair/typebox@0.34.41': + resolution: {integrity: sha512-6gS8pZzSXdyRHTIqoqSVknxolr1kzfy4/CeDnrzsVz8TTIWUbOBr6gnzOmTYJ3eXQNh4IYHIGi5aIL7sOZ2G/g==} + + '@sinonjs/commons@1.8.6': + resolution: {integrity: sha512-Ky+XkAkqPZSm3NLBeUng77EBQl3cmeJhITaGHdYH8kjVB+aun3S4XBRti2zt17mtt0mIUDiNxYeoJm6drVvBJQ==} + + '@sinonjs/fake-timers@8.1.0': + resolution: {integrity: sha512-OAPJUAtgeINhh/TAlUID4QTs53Njm7xzddaVlEs/SXwgtiD1tW22zAB/W1wdqfrpmikgaWQ9Fw6Ws+hsiRm5Vg==} + + '@smithy/config-resolver@4.4.14': + resolution: {integrity: sha512-N55f8mPEccpzKetUagdvmAy8oohf0J5cuj9jLI1TaSceRlq0pJsIZepY3kmAXAhyxqXPV6hDerDQhqQPKWgAoQ==} + engines: {node: '>=18.0.0'} + + '@smithy/core@3.23.14': + resolution: {integrity: sha512-vJ0IhpZxZAkFYOegMKSrxw7ujhhT2pass/1UEcZ4kfl5srTAqtPU5I7MdYQoreVas3204ykCiNhY1o7Xlz6Yyg==} + engines: {node: '>=18.0.0'} + + '@smithy/credential-provider-imds@4.2.13': + resolution: {integrity: sha512-wboCPijzf6RJKLOvnjDAiBxGSmSnGXj35o5ZAWKDaHa/cvQ5U3ZJ13D4tMCE8JG4dxVAZFy/P0x/V9CwwdfULQ==} + engines: {node: '>=18.0.0'} + + '@smithy/fetch-http-handler@5.3.16': + resolution: {integrity: sha512-nYDRUIvNd4mFmuXraRWt6w5UsZTNqtj4hXJA/iiOD4tuseIdLP9Lq38teH/SZTcIFCa2f+27o7hYpIsWktJKEQ==} + engines: {node: '>=18.0.0'} + + '@smithy/hash-node@4.2.13': + resolution: {integrity: sha512-4/oy9h0jjmY80a2gOIo75iLl8TOPhmtx4E2Hz+PfMjvx/vLtGY4TMU/35WRyH2JHPfT5CVB38u4JRow7gnmzJA==} + engines: {node: '>=18.0.0'} + + '@smithy/invalid-dependency@4.2.13': + resolution: {integrity: sha512-jvC0RB/8BLj2SMIkY0Npl425IdnxZJxInpZJbu563zIRnVjpDMXevU3VMCRSabaLB0kf/eFIOusdGstrLJ8IDg==} + engines: {node: '>=18.0.0'} + + '@smithy/is-array-buffer@2.2.0': + resolution: {integrity: sha512-GGP3O9QFD24uGeAXYUjwSTXARoqpZykHadOmA8G5vfJPK0/DC67qa//0qvqrJzL1xc8WQWX7/yc7fwudjPHPhA==} + engines: {node: '>=14.0.0'} + + '@smithy/is-array-buffer@4.2.2': + resolution: {integrity: sha512-n6rQ4N8Jj4YTQO3YFrlgZuwKodf4zUFs7EJIWH86pSCWBaAtAGBFfCM7Wx6D2bBJ2xqFNxGBSrUWswT3M0VJow==} + engines: {node: '>=18.0.0'} + + '@smithy/middleware-content-length@4.2.13': + resolution: {integrity: sha512-IPMLm/LE4AZwu6qiE8Rr8vJsWhs9AtOdySRXrOM7xnvclp77Tyh7hMs/FRrMf26kgIe67vFJXXOSmVxS7oKeig==} + engines: {node: '>=18.0.0'} + + '@smithy/middleware-endpoint@4.4.29': + resolution: {integrity: sha512-R9Q/58U+qBiSARGWbAbFLczECg/RmysRksX6Q8BaQEpt75I7LI6WGDZnjuC9GXSGKljEbA7N118LhGaMbfrTXw==} + engines: {node: '>=18.0.0'} + + '@smithy/middleware-retry@4.5.1': + resolution: {integrity: sha512-/zY+Gp7Qj2D2hVm3irkCyONER7E9MiX3cUUm/k2ZmhkzZkrPgwVS4aJ5NriZUEN/M0D1hhjrgjUmX04HhRwdWA==} + engines: {node: '>=18.0.0'} + + '@smithy/middleware-serde@4.2.17': + resolution: {integrity: sha512-0T2mcaM6v9W1xku86Dk0bEW7aEseG6KenFkPK98XNw0ZhOqOiD1MrMsdnQw9QsL3/Oa85T53iSMlm0SZdSuIEQ==} + engines: {node: '>=18.0.0'} + + '@smithy/middleware-stack@4.2.13': + resolution: {integrity: sha512-g72jN/sGDLyTanrCLH9fhg3oysO3f7tQa6eWWsMyn2BiYNCgjF24n4/I9wff/5XidFvjj9ilipAoQrurTUrLvw==} + engines: {node: '>=18.0.0'} + + '@smithy/node-config-provider@4.3.13': + resolution: {integrity: sha512-iGxQ04DsKXLckbgnX4ipElrOTk+IHgTyu0q0WssZfYhDm9CQWHmu6cOeI5wmWRxpXbBDhIIfXMWz5tPEtcVqbw==} + engines: {node: '>=18.0.0'} + + '@smithy/node-http-handler@4.5.2': + resolution: {integrity: sha512-/oD7u8M0oj2ZTFw7GkuuHWpIxtWdLlnyNkbrWcyVYhd5RJNDuczdkb0wfnQICyNFrVPlr8YHOhamjNy3zidhmA==} + engines: {node: '>=18.0.0'} + + '@smithy/property-provider@4.2.13': + resolution: {integrity: sha512-bGzUCthxRmezuxkbu9wD33wWg9KX3hJpCXpQ93vVkPrHn9ZW6KNNdY5xAUWNuRCwQ+VyboFuWirG1lZhhkcyRQ==} + engines: {node: '>=18.0.0'} + + '@smithy/protocol-http@5.3.13': + resolution: {integrity: sha512-+HsmuJUF4u8POo6s8/a2Yb/AQ5t/YgLovCuHF9oxbocqv+SZ6gd8lC2duBFiCA/vFHoHQhoq7QjqJqZC6xOxxg==} + engines: {node: '>=18.0.0'} + + '@smithy/querystring-builder@4.2.13': + resolution: {integrity: sha512-tG4aOYFCZdPMjbgfhnIQ322H//ojujldp1SrHPHpBSb3NqgUp3dwiUGRJzie87hS1DYwWGqDuPaowoDF+rYCbQ==} + engines: {node: '>=18.0.0'} + + '@smithy/querystring-parser@4.2.13': + resolution: {integrity: sha512-hqW3Q4P+CDzUyQ87GrboGMeD7XYNMOF+CuTwu936UQRB/zeYn3jys8C3w+wMkDfY7CyyyVwZQ5cNFoG0x1pYmA==} + engines: {node: '>=18.0.0'} + + '@smithy/service-error-classification@4.2.13': + resolution: {integrity: sha512-a0s8XZMfOC/qpqq7RCPvJlk93rWFrElH6O++8WJKz0FqnA4Y7fkNi/0mnGgSH1C4x6MFsuBA8VKu4zxFrMe5Vw==} + engines: {node: '>=18.0.0'} + + '@smithy/shared-ini-file-loader@4.4.8': + resolution: {integrity: sha512-VZCZx2bZasxdqxVgEAhREvDSlkatTPnkdWy1+Kiy8w7kYPBosW0V5IeDwzDUMvWBt56zpK658rx1cOBFOYaPaw==} + engines: {node: '>=18.0.0'} + + '@smithy/signature-v4@5.3.13': + resolution: {integrity: sha512-YpYSyM0vMDwKbHD/JA7bVOF6kToVRpa+FM5ateEVRpsTNu564g1muBlkTubXhSKKYXInhpADF46FPyrZcTLpXg==} + engines: {node: '>=18.0.0'} + + '@smithy/smithy-client@4.12.9': + resolution: {integrity: sha512-ovaLEcTU5olSeHcRXcxV6viaKtpkHZumn6Ps0yn7dRf2rRSfy794vpjOtrWDO0d1auDSvAqxO+lyhERSXQ03EQ==} + engines: {node: '>=18.0.0'} + + '@smithy/types@4.14.0': + resolution: {integrity: sha512-OWgntFLW88kx2qvf/c/67Vno1yuXm/f9M7QFAtVkkO29IJXGBIg0ycEaBTH0kvCtwmvZxRujrgP5a86RvsXJAQ==} + engines: {node: '>=18.0.0'} + + '@smithy/url-parser@4.2.13': + resolution: {integrity: sha512-2G03yoboIRZlZze2+PT4GZEjgwQsJjUgn6iTsvxA02bVceHR6vp4Cuk7TUnPFWKF+ffNUk3kj4COwkENS2K3vw==} + engines: {node: '>=18.0.0'} + + '@smithy/util-base64@4.3.2': + resolution: {integrity: sha512-XRH6b0H/5A3SgblmMa5ErXQ2XKhfbQB+Fm/oyLZ2O2kCUrwgg55bU0RekmzAhuwOjA9qdN5VU2BprOvGGUkOOQ==} + engines: {node: '>=18.0.0'} + + '@smithy/util-body-length-browser@4.2.2': + resolution: {integrity: sha512-JKCrLNOup3OOgmzeaKQwi4ZCTWlYR5H4Gm1r2uTMVBXoemo1UEghk5vtMi1xSu2ymgKVGW631e2fp9/R610ZjQ==} + engines: {node: '>=18.0.0'} + + '@smithy/util-body-length-node@4.2.3': + resolution: {integrity: sha512-ZkJGvqBzMHVHE7r/hcuCxlTY8pQr1kMtdsVPs7ex4mMU+EAbcXppfo5NmyxMYi2XU49eqaz56j2gsk4dHHPG/g==} + engines: {node: '>=18.0.0'} + + '@smithy/util-buffer-from@2.2.0': + resolution: {integrity: sha512-IJdWBbTcMQ6DA0gdNhh/BwrLkDR+ADW5Kr1aZmd4k3DIF6ezMV4R2NIAmT08wQJ3yUK82thHWmC/TnK/wpMMIA==} + engines: {node: '>=14.0.0'} + + '@smithy/util-buffer-from@4.2.2': + resolution: {integrity: sha512-FDXD7cvUoFWwN6vtQfEta540Y/YBe5JneK3SoZg9bThSoOAC/eGeYEua6RkBgKjGa/sz6Y+DuBZj3+YEY21y4Q==} + engines: {node: '>=18.0.0'} + + '@smithy/util-config-provider@4.2.2': + resolution: {integrity: sha512-dWU03V3XUprJwaUIFVv4iOnS1FC9HnMHDfUrlNDSh4315v0cWyaIErP8KiqGVbf5z+JupoVpNM7ZB3jFiTejvQ==} + engines: {node: '>=18.0.0'} + + '@smithy/util-defaults-mode-browser@4.3.45': + resolution: {integrity: sha512-ag9sWc6/nWZAuK3Wm9KlFJUnRkXLrXn33RFjIAmCTFThqLHY+7wCst10BGq56FxslsDrjhSie46c8OULS+BiIw==} + engines: {node: '>=18.0.0'} + + '@smithy/util-defaults-mode-node@4.2.49': + resolution: {integrity: sha512-jlN6vHwE8gY5AfiFBavtD3QtCX2f7lM3BKkz7nFKSNfFR5nXLXLg6sqXTJEEyDwtxbztIDBQCfjsGVXlIru2lQ==} + engines: {node: '>=18.0.0'} + + '@smithy/util-endpoints@3.3.4': + resolution: {integrity: sha512-BKoR/ubPp9KNKFxPpg1J28N1+bgu8NGAtJblBP7yHy8yQPBWhIAv9+l92SlQLpolGm71CVO+btB60gTgzT0wog==} + engines: {node: '>=18.0.0'} + + '@smithy/util-hex-encoding@4.2.2': + resolution: {integrity: sha512-Qcz3W5vuHK4sLQdyT93k/rfrUwdJ8/HZ+nMUOyGdpeGA1Wxt65zYwi3oEl9kOM+RswvYq90fzkNDahPS8K0OIg==} + engines: {node: '>=18.0.0'} + + '@smithy/util-middleware@4.2.13': + resolution: {integrity: sha512-GTooyrlmRTqvUen4eK7/K1p6kryF7bnDfq6XsAbIsf2mo51B/utaH+XThY6dKgNCWzMAaH/+OLmqaBuLhLWRow==} + engines: {node: '>=18.0.0'} + + '@smithy/util-retry@4.3.1': + resolution: {integrity: sha512-FwmicpgWOkP5kZUjN3y+3JIom8NLGqSAJBeoIgK0rIToI817TEBHCrd0A2qGeKQlgDeP+Jzn4i0H/NLAXGy9uQ==} + engines: {node: '>=18.0.0'} + + '@smithy/util-stream@4.5.22': + resolution: {integrity: sha512-3H8iq/0BfQjUs2/4fbHZ9aG9yNzcuZs24LPkcX1Q7Z+qpqaGM8+qbGmE8zo9m2nCRgamyvS98cHdcWvR6YUsew==} + engines: {node: '>=18.0.0'} + + '@smithy/util-uri-escape@4.2.2': + resolution: {integrity: sha512-2kAStBlvq+lTXHyAZYfJRb/DfS3rsinLiwb+69SstC9Vb0s9vNWkRwpnj918Pfi85mzi42sOqdV72OLxWAISnw==} + engines: {node: '>=18.0.0'} + + '@smithy/util-utf8@2.3.0': + resolution: {integrity: sha512-R8Rdn8Hy72KKcebgLiv8jQcQkXoLMOGGv5uI1/k0l+snqkOzQ1R0ChUBCxWMlBsFMekWjq0wRudIweFs7sKT5A==} + engines: {node: '>=14.0.0'} + + '@smithy/util-utf8@4.2.2': + resolution: {integrity: sha512-75MeYpjdWRe8M5E3AW0O4Cx3UadweS+cwdXjwYGBW5h/gxxnbeZ877sLPX/ZJA9GVTlL/qG0dXP29JWFCD1Ayw==} + engines: {node: '>=18.0.0'} + + '@smithy/uuid@1.1.2': + resolution: {integrity: sha512-O/IEdcCUKkubz60tFbGA7ceITTAJsty+lBjNoorP4Z6XRqaFb/OjQjZODophEcuq68nKm6/0r+6/lLQ+XVpk8g==} + engines: {node: '>=18.0.0'} + + '@so-ric/colorspace@1.1.6': + resolution: {integrity: sha512-/KiKkpHNOBgkFJwu9sh48LkHSMYGyuTcSFK/qMBdnOAlrRJzRSXAOFB5qwzaVQuDl8wAvHVMkaASQDReTahxuw==} + + '@socket.io/component-emitter@3.1.2': + resolution: {integrity: sha512-9BCxFwvbGg/RsZK9tjXd8s4UcwR0MWeFQ1XEKIQVVvAGJyINdrqKMcTRyLoK8Rse1GjzLV9cwjWV1olXRWEXVA==} + + '@sqltools/formatter@1.2.5': + resolution: {integrity: sha512-Uy0+khmZqUrUGm5dmMqVlnvufZRSK0FbYzVgp0UMstm+F5+W2/jnEEQyc9vo1ZR/E5ZI/B1WjjoTqBqwJL6Krw==} + + '@standard-schema/spec@1.0.0': + resolution: {integrity: sha512-m2bOd0f2RT9k8QJx1JN85cZYyH1RqFBdlwtkSlf4tBDYLCiiZnv1fIIwacK6cqwXavOydf0NPToMQgpKq+dVlA==} + + '@standard-schema/utils@0.3.0': + resolution: {integrity: sha512-e7Mew686owMaPJVNNLs55PUvgz371nKgwsc4vxE49zsODpJEnxgxRo2y/OKrqueavXgZNMDVj3DdHFlaSAeU8g==} + + '@surma/rollup-plugin-off-main-thread@2.2.3': + resolution: {integrity: sha512-lR8q/9W7hZpMWweNiAKU7NQerBnzQQLvi8qnTDU/fxItPhtZVMbPV3lbCwjhIlNBe9Bbr5V+KHshvWmVSG9cxQ==} + + '@svgr/babel-plugin-add-jsx-attribute@5.4.0': + resolution: {integrity: sha512-ZFf2gs/8/6B8PnSofI0inYXr2SDNTDScPXhN7k5EqD4aZ3gi6u+rbmZHVB8IM3wDyx8ntKACZbtXSm7oZGRqVg==} + engines: {node: '>=10'} + + '@svgr/babel-plugin-remove-jsx-attribute@5.4.0': + resolution: {integrity: sha512-yaS4o2PgUtwLFGTKbsiAy6D0o3ugcUhWK0Z45umJ66EPWunAz9fuFw2gJuje6wqQvQWOTJvIahUwndOXb7QCPg==} + engines: {node: '>=10'} + + '@svgr/babel-plugin-remove-jsx-empty-expression@5.0.1': + resolution: {integrity: sha512-LA72+88A11ND/yFIMzyuLRSMJ+tRKeYKeQ+mR3DcAZ5I4h5CPWN9AHyUzJbWSYp/u2u0xhmgOe0+E41+GjEueA==} + engines: {node: '>=10'} + + '@svgr/babel-plugin-replace-jsx-attribute-value@5.0.1': + resolution: {integrity: sha512-PoiE6ZD2Eiy5mK+fjHqwGOS+IXX0wq/YDtNyIgOrc6ejFnxN4b13pRpiIPbtPwHEc+NT2KCjteAcq33/F1Y9KQ==} + engines: {node: '>=10'} + + '@svgr/babel-plugin-svg-dynamic-title@5.4.0': + resolution: {integrity: sha512-zSOZH8PdZOpuG1ZVx/cLVePB2ibo3WPpqo7gFIjLV9a0QsuQAzJiwwqmuEdTaW2pegyBE17Uu15mOgOcgabQZg==} + engines: {node: '>=10'} + + '@svgr/babel-plugin-svg-em-dimensions@5.4.0': + resolution: {integrity: sha512-cPzDbDA5oT/sPXDCUYoVXEmm3VIoAWAPT6mSPTJNbQaBNUuEKVKyGH93oDY4e42PYHRW67N5alJx/eEol20abw==} + engines: {node: '>=10'} + + '@svgr/babel-plugin-transform-react-native-svg@5.4.0': + resolution: {integrity: sha512-3eYP/SaopZ41GHwXma7Rmxcv9uRslRDTY1estspeB1w1ueZWd/tPlMfEOoccYpEMZU3jD4OU7YitnXcF5hLW2Q==} + engines: {node: '>=10'} + + '@svgr/babel-plugin-transform-svg-component@5.5.0': + resolution: {integrity: sha512-q4jSH1UUvbrsOtlo/tKcgSeiCHRSBdXoIoqX1pgcKK/aU3JD27wmMKwGtpB8qRYUYoyXvfGxUVKchLuR5pB3rQ==} + engines: {node: '>=10'} + + '@svgr/babel-preset@5.5.0': + resolution: {integrity: sha512-4FiXBjvQ+z2j7yASeGPEi8VD/5rrGQk4Xrq3EdJmoZgz/tpqChpo5hgXDvmEauwtvOc52q8ghhZK4Oy7qph4ig==} + engines: {node: '>=10'} + + '@svgr/core@5.5.0': + resolution: {integrity: sha512-q52VOcsJPvV3jO1wkPtzTuKlvX7Y3xIcWRpCMtBF3MrteZJtBfQw/+u0B1BHy5ColpQc1/YVTrPEtSYIMNZlrQ==} + engines: {node: '>=10'} + + '@svgr/hast-util-to-babel-ast@5.5.0': + resolution: {integrity: sha512-cAaR/CAiZRB8GP32N+1jocovUtvlj0+e65TB50/6Lcime+EA49m/8l+P2ko+XPJ4dw3xaPS3jOL4F2X4KWxoeQ==} + engines: {node: '>=10'} + + '@svgr/plugin-jsx@5.5.0': + resolution: {integrity: sha512-V/wVh33j12hGh05IDg8GpIUXbjAPnTdPTKuP4VNLggnwaHMPNQNae2pRnyTAILWCQdz5GyMqtO488g7CKM8CBA==} + engines: {node: '>=10'} + + '@svgr/plugin-svgo@5.5.0': + resolution: {integrity: sha512-r5swKk46GuQl4RrVejVwpeeJaydoxkdwkM1mBKOgJLBUJPGaLci6ylg/IjhrRsREKDkr4kbMWdgOtbXEh0fyLQ==} + engines: {node: '>=10'} + + '@svgr/webpack@5.5.0': + resolution: {integrity: sha512-DOBOK255wfQxguUta2INKkzPj6AIS6iafZYiYmHn6W3pHlycSRRlvWKCfLDG10fXfLWqE3DJHgRUOyJYmARa7g==} + engines: {node: '>=10'} + + '@tabler/icons-react@3.41.1': + resolution: {integrity: sha512-kUgweE+DJtAlMZVIns1FTDdcbpRVnkK7ZpUOXmoxy3JAF0rSHj0TcP4VHF14+gMJGnF+psH2Zt26BLT6owetBA==} + peerDependencies: + react: '>= 16' + + '@tabler/icons@3.41.1': + resolution: {integrity: sha512-OaRnVbRmH2nHtFeg+RmMJ/7m2oBIF9XCJAUD5gQnMrpK9f05ydj8MZrAf3NZQqOXyxGN1UBL0D5IKLLEUfr74Q==} + + '@testing-library/dom@9.3.4': + resolution: {integrity: sha512-FlS4ZWlp97iiNWig0Muq8p+3rVDjRiYE+YKGbAqXOu9nwJFFOdL00kFpz42M+4huzYi86vAK1sOOfyOG45muIQ==} + engines: {node: '>=14'} + + '@testing-library/jest-dom@5.17.0': + resolution: {integrity: sha512-ynmNeT7asXyH3aSVv4vvX4Rb+0qjOhdNHnO/3vuZNqPmhDpV/+rCSGwQ7bLcmU2cJ4dvoheIO85LQj0IbJHEtg==} + engines: {node: '>=8', npm: '>=6', yarn: '>=1'} + + '@testing-library/react@14.3.1': + resolution: {integrity: sha512-H99XjUhWQw0lTgyMN05W3xQG1Nh4lq574D8keFf1dDoNTJgp66VbJozRaczoF+wsiaPJNt/TcnfpLGufGxSrZQ==} + engines: {node: '>=14'} + peerDependencies: + react: ^18.0.0 + react-dom: ^18.0.0 + + '@testing-library/user-event@12.8.3': + resolution: {integrity: sha512-IR0iWbFkgd56Bu5ZI/ej8yQwrkCv8Qydx6RzwbKz9faXazR/+5tvYKsZQgyXJiwgpcva127YO6JcWy7YlCfofQ==} + engines: {node: '>=10', npm: '>=6'} + peerDependencies: + '@testing-library/dom': '>=7.21.4' + + '@tootallnate/once@1.1.2': + resolution: {integrity: sha512-RbzJvlNzmRq5c3O09UipeuXno4tA1FE6ikOjxZK0tuxVv3412l64l5t1W5pj4+rJq9vpkm/kwiR07aZXnsKPxw==} + engines: {node: '>= 6'} + + '@tootallnate/once@2.0.0': + resolution: {integrity: sha512-XCuKFP5PS55gnMVu3dty8KPatLqUoy/ZYzDzAGCQ8JNFCkLXzmI7vNHCR+XpbZaMWQK/vQubr7PkYq8g470J/A==} + engines: {node: '>= 10'} + + '@trysound/sax@0.2.0': + resolution: {integrity: sha512-L7z9BgrNEcYyUYtF+HaEfiS5ebkh9jXqbszz7pC0hRBPaatV0XjSD3+eHrpqFemQfgwiFF0QPIarnIihIDn7OA==} + engines: {node: '>=10.13.0'} + + '@ts-stack/markdown@1.5.0': + resolution: {integrity: sha512-ntVX2Kmb2jyTdH94plJohokvDVPvp6CwXHqsa9NVZTK8cOmHDCYNW0j6thIadUVRTStJhxhfdeovLd0owqDxLw==} + + '@tsconfig/node10@1.0.11': + resolution: {integrity: sha512-DcRjDCujK/kCk/cUe8Xz8ZSpm8mS3mNNpta+jGCA6USEDfktlNvm1+IuZ9eTcDbNk41BHwpHHeW+N1lKCz4zOw==} + + '@tsconfig/node12@1.0.11': + resolution: {integrity: sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==} + + '@tsconfig/node14@1.0.3': + resolution: {integrity: sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==} + + '@tsconfig/node16@1.0.4': + resolution: {integrity: sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==} + + '@types/archiver@6.0.4': + resolution: {integrity: sha512-ULdQpARQ3sz9WH4nb98mJDYA0ft2A8C4f4fovvUcFwINa1cgGjY36JCAYuP5YypRq4mco1lJp1/7jEMS2oR0Hg==} + + '@types/aria-query@5.0.4': + resolution: {integrity: sha512-rfT93uj5s0PRL7EzccGMs3brplhcrghnDoV26NqKhCAS1hVo+WdNsPvE/yb6ilfr5hi2MEk6d5EWJTKdxg8jVw==} + + '@types/babel__core@7.20.5': + resolution: {integrity: sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==} + + '@types/babel__generator@7.27.0': + resolution: {integrity: sha512-ufFd2Xi92OAVPYsy+P4n7/U7e68fex0+Ee8gSG9KX7eo084CWiQ4sdxktvdl0bOPupXtVJPY19zk6EwWqUQ8lg==} + + '@types/babel__template@7.4.4': + resolution: {integrity: sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==} + + '@types/babel__traverse@7.28.0': + resolution: {integrity: sha512-8PvcXf70gTDZBgt9ptxJ8elBeBjcLOAcOtoO/mPJjtji1+CdGbHgm77om1GrsPxsiE+uXIpNSK64UYaIwQXd4Q==} + + '@types/body-parser@1.19.6': + resolution: {integrity: sha512-HLFeCYgz89uk22N5Qg3dvGvsv46B8GLvKKo1zKG4NybA8U2DiEO3w9lqGg29t/tfLRJpJ6iQxnVw4OnB7MoM9g==} + + '@types/bonjour@3.5.13': + resolution: {integrity: sha512-z9fJ5Im06zvUL548KvYNecEVlA7cVDkGUi6kZusb04mpyEFKCIZJvloCcmpmLaIahDpOQGHaHmG6imtPMmPXGQ==} + + '@types/connect-history-api-fallback@1.5.4': + resolution: {integrity: sha512-n6Cr2xS1h4uAulPRdlw6Jl6s1oG8KrVilPN2yUITEs+K48EzMJJ3W1xy8K5eWuFvjp3R74AOIGSmp2UfBJ8HFw==} + + '@types/connect@3.4.38': + resolution: {integrity: sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug==} + + '@types/content-disposition@0.5.8': + resolution: {integrity: sha512-QVSSvno3dE0MgO76pJhmv4Qyi/j0Yk9pBp0Y7TJ2Tlj+KCgJWY6qX7nnxCOLkZ3VYRSIk1WTxCvwUSdx6CCLdg==} + + '@types/cors@2.8.19': + resolution: {integrity: sha512-mFNylyeyqN93lfe/9CSxOGREz8cpzAhH+E93xJ4xWQf62V8sQ/24reV2nyzUWM6H6Xji+GGHpkbLe7pVoUEskg==} + + '@types/crypto-js@4.2.2': + resolution: {integrity: sha512-sDOLlVbHhXpAUAL0YHDUUwDZf3iN4Bwi4W6a0W0b+QcAezUbRtH4FVb+9J4h+XFPW7l/gQ9F8qC7P+Ec4k8QVQ==} + + '@types/d3-array@3.2.2': + resolution: {integrity: sha512-hOLWVbm7uRza0BYXpIIW5pxfrKe0W+D5lrFiAEYR+pb6w3N2SwSMaJbXdUfSEv+dT4MfHBLtn5js0LAWaO6otw==} + + '@types/d3-axis@3.0.6': + resolution: {integrity: sha512-pYeijfZuBd87T0hGn0FO1vQ/cgLk6E1ALJjfkC0oJ8cbwkZl3TpgS8bVBLZN+2jjGgg38epgxb2zmoGtSfvgMw==} + + '@types/d3-brush@3.0.6': + resolution: {integrity: sha512-nH60IZNNxEcrh6L1ZSMNA28rj27ut/2ZmI3r96Zd+1jrZD++zD3LsMIjWlvg4AYrHn/Pqz4CF3veCxGjtbqt7A==} + + '@types/d3-chord@3.0.6': + resolution: {integrity: sha512-LFYWWd8nwfwEmTZG9PfQxd17HbNPksHBiJHaKuY1XeqscXacsS2tyoo6OdRsjf+NQYeB6XrNL3a25E3gH69lcg==} + + '@types/d3-color@3.1.3': + resolution: {integrity: sha512-iO90scth9WAbmgv7ogoq57O9YpKmFBbmoEoCHDB2xMBY0+/KVrqAaCDyCE16dUspeOvIxFFRI+0sEtqDqy2b4A==} + + '@types/d3-contour@3.0.6': + resolution: {integrity: sha512-BjzLgXGnCWjUSYGfH1cpdo41/hgdWETu4YxpezoztawmqsvCeep+8QGfiY6YbDvfgHz/DkjeIkkZVJavB4a3rg==} + + '@types/d3-delaunay@6.0.4': + resolution: {integrity: sha512-ZMaSKu4THYCU6sV64Lhg6qjf1orxBthaC161plr5KuPHo3CNm8DTHiLw/5Eq2b6TsNP0W0iJrUOFscY6Q450Hw==} + + '@types/d3-dispatch@3.0.7': + resolution: {integrity: sha512-5o9OIAdKkhN1QItV2oqaE5KMIiXAvDWBDPrD85e58Qlz1c1kI/J0NcqbEG88CoTwJrYe7ntUCVfeUl2UJKbWgA==} + + '@types/d3-drag@3.0.7': + resolution: {integrity: sha512-HE3jVKlzU9AaMazNufooRJ5ZpWmLIoc90A37WU2JMmeq28w1FQqCZswHZ3xR+SuxYftzHq6WU6KJHvqxKzTxxQ==} + + '@types/d3-dsv@3.0.7': + resolution: {integrity: sha512-n6QBF9/+XASqcKK6waudgL0pf/S5XHPPI8APyMLLUHd8NqouBGLsU8MgtO7NINGtPBtk9Kko/W4ea0oAspwh9g==} + + '@types/d3-ease@3.0.2': + resolution: {integrity: sha512-NcV1JjO5oDzoK26oMzbILE6HW7uVXOHLQvHshBUW4UMdZGfiY6v5BeQwh9a9tCzv+CeefZQHJt5SRgK154RtiA==} + + '@types/d3-fetch@3.0.7': + resolution: {integrity: sha512-fTAfNmxSb9SOWNB9IoG5c8Hg6R+AzUHDRlsXsDZsNp6sxAEOP0tkP3gKkNSO/qmHPoBFTxNrjDprVHDQDvo5aA==} + + '@types/d3-force@3.0.10': + resolution: {integrity: sha512-ZYeSaCF3p73RdOKcjj+swRlZfnYpK1EbaDiYICEEp5Q6sUiqFaFQ9qgoshp5CzIyyb/yD09kD9o2zEltCexlgw==} + + '@types/d3-format@3.0.4': + resolution: {integrity: sha512-fALi2aI6shfg7vM5KiR1wNJnZ7r6UuggVqtDA+xiEdPZQwy/trcQaHnwShLuLdta2rTymCNpxYTiMZX/e09F4g==} + + '@types/d3-geo@3.1.0': + resolution: {integrity: sha512-856sckF0oP/diXtS4jNsiQw/UuK5fQG8l/a9VVLeSouf1/PPbBE1i1W852zVwKwYCBkFJJB7nCFTbk6UMEXBOQ==} + + '@types/d3-hierarchy@3.1.7': + resolution: {integrity: sha512-tJFtNoYBtRtkNysX1Xq4sxtjK8YgoWUNpIiUee0/jHGRwqvzYxkq0hGVbbOGSz+JgFxxRu4K8nb3YpG3CMARtg==} + + '@types/d3-interpolate@3.0.4': + resolution: {integrity: sha512-mgLPETlrpVV1YRJIglr4Ez47g7Yxjl1lj7YKsiMCb27VJH9W8NVM6Bb9d8kkpG/uAQS5AmbA48q2IAolKKo1MA==} + + '@types/d3-path@3.1.1': + resolution: {integrity: sha512-VMZBYyQvbGmWyWVea0EHs/BwLgxc+MKi1zLDCONksozI4YJMcTt8ZEuIR4Sb1MMTE8MMW49v0IwI5+b7RmfWlg==} + + '@types/d3-polygon@3.0.2': + resolution: {integrity: sha512-ZuWOtMaHCkN9xoeEMr1ubW2nGWsp4nIql+OPQRstu4ypeZ+zk3YKqQT0CXVe/PYqrKpZAi+J9mTs05TKwjXSRA==} + + '@types/d3-quadtree@3.0.6': + resolution: {integrity: sha512-oUzyO1/Zm6rsxKRHA1vH0NEDG58HrT5icx/azi9MF1TWdtttWl0UIUsjEQBBh+SIkrpd21ZjEv7ptxWys1ncsg==} + + '@types/d3-random@3.0.3': + resolution: {integrity: sha512-Imagg1vJ3y76Y2ea0871wpabqp613+8/r0mCLEBfdtqC7xMSfj9idOnmBYyMoULfHePJyxMAw3nWhJxzc+LFwQ==} + + '@types/d3-scale-chromatic@3.1.0': + resolution: {integrity: sha512-iWMJgwkK7yTRmWqRB5plb1kadXyQ5Sj8V/zYlFGMUBbIPKQScw+Dku9cAAMgJG+z5GYDoMjWGLVOvjghDEFnKQ==} + + '@types/d3-scale@4.0.9': + resolution: {integrity: sha512-dLmtwB8zkAeO/juAMfnV+sItKjlsw2lKdZVVy6LRr0cBmegxSABiLEpGVmSJJ8O08i4+sGR6qQtb6WtuwJdvVw==} + + '@types/d3-selection@3.0.11': + resolution: {integrity: sha512-bhAXu23DJWsrI45xafYpkQ4NtcKMwWnAC/vKrd2l+nxMFuvOT3XMYTIj2opv8vq8AO5Yh7Qac/nSeP/3zjTK0w==} + + '@types/d3-shape@3.1.7': + resolution: {integrity: sha512-VLvUQ33C+3J+8p+Daf+nYSOsjB4GXp19/S/aGo60m9h1v6XaxjiT82lKVWJCfzhtuZ3yD7i/TPeC/fuKLLOSmg==} + + '@types/d3-time-format@4.0.3': + resolution: {integrity: sha512-5xg9rC+wWL8kdDj153qZcsJ0FWiFt0J5RB6LYUNZjwSnesfblqrI/bJ1wBdJ8OQfncgbJG5+2F+qfqnqyzYxyg==} + + '@types/d3-time@3.0.4': + resolution: {integrity: sha512-yuzZug1nkAAaBlBBikKZTgzCeA+k1uy4ZFwWANOfKw5z5LRhV0gNA7gNkKm7HoK+HRN0wX3EkxGk0fpbWhmB7g==} + + '@types/d3-timer@3.0.2': + resolution: {integrity: sha512-Ps3T8E8dZDam6fUyNiMkekK3XUsaUEik+idO9/YjPtfj2qruF8tFBXS7XhtE4iIXBLxhmLjP3SXpLhVf21I9Lw==} + + '@types/d3-transition@3.0.9': + resolution: {integrity: sha512-uZS5shfxzO3rGlu0cC3bjmMFKsXv+SmZZcgp0KD22ts4uGXp5EVYGzu/0YdwZeKmddhcAccYtREJKkPfXkZuCg==} + + '@types/d3-zoom@3.0.8': + resolution: {integrity: sha512-iqMC4/YlFCSlO8+2Ii1GGGliCAY4XdeG748w5vQUbevlbDu0zSjH/+jojorQVBK/se0j6DUFNPBGSqD3YWYnDw==} + + '@types/d3@7.4.3': + resolution: {integrity: sha512-lZXZ9ckh5R8uiFVt8ogUNf+pIrK4EsWrx2Np75WvF/eTpJ0FMHNhjXk8CKEx/+gpHbNQyJWehbFaTvqmHWB3ww==} + + '@types/debug@4.1.12': + resolution: {integrity: sha512-vIChWdVG3LG1SMxEvI/AK+FWJthlrqlTu7fbrlywTkkaONwk/UAGaULXRlf8vkzFBLVm0zkMdCquhL5aOjhXPQ==} + + '@types/dompurify@3.2.0': + resolution: {integrity: sha512-Fgg31wv9QbLDA0SpTOXO3MaxySc4DKGLi8sna4/Utjo4r3ZRPdCt4UQee8BWr+Q5z21yifghREPJGYaEOEIACg==} + deprecated: This is a stub types definition. dompurify provides its own type definitions, so you do not need this installed. + + '@types/eslint-scope@3.7.7': + resolution: {integrity: sha512-MzMFlSLBqNF2gcHWO0G1vP/YQyfvrxZ0bF+u7mzUdZ1/xK4A4sru+nraZz5i3iEIk1l1uyicaDVTB4QbbEkAYg==} + + '@types/eslint@8.56.12': + resolution: {integrity: sha512-03ruubjWyOHlmljCVoxSuNDdmfZDzsrrz0P2LeJsOXr+ZwFQ+0yQIwNCwt/GYhV7Z31fgtXJTAEs+FYlEL851g==} + + '@types/eslint@9.6.1': + resolution: {integrity: sha512-FXx2pKgId/WyYo2jXw63kk7/+TY7u7AziEJxJAnSFzHlqTAS3Ync6SvgYAN/k4/PQpnnVuzoMuVnByKK2qp0ag==} + + '@types/estree@0.0.39': + resolution: {integrity: sha512-EYNwp3bU+98cpU4lAWYYL7Zz+2gryWH1qbdDTidVd6hkiR6weksdbMadyXKXNPEkQFhXM+hVO9ZygomHXp+AIw==} + + '@types/estree@1.0.8': + resolution: {integrity: sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==} + + '@types/express-serve-static-core@4.19.7': + resolution: {integrity: sha512-FvPtiIf1LfhzsaIXhv/PHan/2FeQBbtBDtfX2QfvPxdUelMDEckK08SM6nqo1MIZY3RUlfA+HV8+hFUSio78qg==} + + '@types/express-serve-static-core@5.1.0': + resolution: {integrity: sha512-jnHMsrd0Mwa9Cf4IdOzbz543y4XJepXrbia2T4b6+spXC2We3t1y6K44D3mR8XMFSXMCf3/l7rCgddfx7UNVBA==} + + '@types/express@4.17.23': + resolution: {integrity: sha512-Crp6WY9aTYP3qPi2wGDo9iUe/rceX01UMhnF1jmwDcKCFM6cx7YhGP/Mpr3y9AASpfHixIG0E6azCcL5OcDHsQ==} + + '@types/geojson@7946.0.16': + resolution: {integrity: sha512-6C8nqWur3j98U6+lXDfTUWIfgvZU+EumvpHKcYjujKH7woYyLj2sUmff0tRhrqM7BohUw7Pz3ZB1jj2gW9Fvmg==} + + '@types/graceful-fs@4.1.9': + resolution: {integrity: sha512-olP3sd1qOEe5dXTSaFvQG+02VdRXcdytWLAZsAq1PecU8uqQAhkrnbli7DagjtXKW/Bl7YJbUsa8MPcuc8LHEQ==} + + '@types/hast@2.3.10': + resolution: {integrity: sha512-McWspRw8xx8J9HurkVBfYj0xKoE25tOFlHGdx4MJ5xORQrMGZNqJhVQWaIbm6Oyla5kYOXtDiopzKRJzEOkwJw==} + + '@types/hast@3.0.4': + resolution: {integrity: sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ==} + + '@types/hoist-non-react-statics@3.3.7': + resolution: {integrity: sha512-PQTyIulDkIDro8P+IHbKCsw7U2xxBYflVzW/FgWdCAePD9xGSidgA76/GeJ6lBKoblyhf9pBY763gbrN+1dI8g==} + peerDependencies: + '@types/react': '*' + + '@types/html-minifier-terser@6.1.0': + resolution: {integrity: sha512-oh/6byDPnL1zeNXFrDXFLyZjkr1MsBG667IM792caf1L2UPOOMf65NFzjUH/ltyfwjAGfs1rsX1eftK0jC/KIg==} + + '@types/http-errors@2.0.5': + resolution: {integrity: sha512-r8Tayk8HJnX0FztbZN7oVqGccWgw98T/0neJphO91KkmOzug1KkofZURD4UaD5uH8AqcFLfdPErnBod0u71/qg==} + + '@types/http-proxy@1.17.16': + resolution: {integrity: sha512-sdWoUajOB1cd0A8cRRQ1cfyWNbmFKLAqBB89Y8x5iYyG/mkJHc0YUH8pdWBy2omi9qtCpiIgGjuwO0dQST2l5w==} + + '@types/istanbul-lib-coverage@2.0.6': + resolution: {integrity: sha512-2QF/t/auWm0lsy8XtKVPG19v3sSOQlJe/YHZgfjb/KBBHOGSV+J2q/S671rcq9uTBrLAXmZpqJiaQbMT+zNU1w==} + + '@types/istanbul-lib-report@3.0.3': + resolution: {integrity: sha512-NQn7AHQnk/RSLOxrBbGyJM/aVQ+pjj5HCgasFxc0K/KhoATfQ/47AyUl15I2yBUpihjmas+a+VJBOqecrFH+uA==} + + '@types/istanbul-reports@3.0.4': + resolution: {integrity: sha512-pk2B1NWalF9toCRu6gjBzR69syFjP4Od8WRAX+0mmf9lAjCRicLOWc+ZrxZHx/0XRjotgkF9t6iaMJ+aXcOdZQ==} + + '@types/jest@30.0.0': + resolution: {integrity: sha512-XTYugzhuwqWjws0CVz8QpM36+T+Dz5mTEBKhNs/esGLnCIlGdRy+Dq78NRjd7ls7r8BC8ZRMOrKlkO1hU0JOwA==} + + '@types/json-schema@7.0.15': + resolution: {integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==} + + '@types/json5@0.0.29': + resolution: {integrity: sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==} + + '@types/katex@0.16.7': + resolution: {integrity: sha512-HMwFiRujE5PjrgwHQ25+bsLJgowjGjm5Z8FVSf0N6PwgJrwxH0QxzHYDcKsTfV3wva0vzrpqMTJS2jXPr5BMEQ==} + + '@types/lodash@4.17.20': + resolution: {integrity: sha512-H3MHACvFUEiujabxhaI/ImO6gUrd8oOurg7LQtS7mbwIXA/cUqWrvBsaeJ23aZEPk1TAYkurjfMbSELfoCXlGA==} + + '@types/mathjax@0.0.37': + resolution: {integrity: sha512-y0WSZBtBNQwcYipTU/BhgeFu1EZNlFvUNCmkMXV9kBQZq7/o5z82dNVyH3yy2Xv5zzeNeQoHSL4Xm06+EQiH+g==} + + '@types/mdast@3.0.15': + resolution: {integrity: sha512-LnwD+mUEfxWMa1QpDraczIn6k0Ee3SMicuYSSzS6ZYl2gKS09EClnJYGd8Du6rfc5r/GZEk5o1mRb8TaTj03sQ==} + + '@types/mime@1.3.5': + resolution: {integrity: sha512-/pyBZWSLD2n0dcHE3hq8s8ZvcETHtEuF+3E7XVt0Ig2nvsVQXdghHVcEkIWjy9A0wKfTn97a/PSDYohKIlnP/w==} + + '@types/ms@2.1.0': + resolution: {integrity: sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA==} + + '@types/multer@1.4.13': + resolution: {integrity: sha512-bhhdtPw7JqCiEfC9Jimx5LqX9BDIPJEh2q/fQ4bqbBPtyEZYr3cvF22NwG0DmPZNYA0CAf2CnqDB4KIGGpJcaw==} + + '@types/node-forge@1.3.14': + resolution: {integrity: sha512-mhVF2BnD4BO+jtOp7z1CdzaK4mbuK0LLQYAvdOLqHTavxFNq4zA1EmYkpnFjP8HOUzedfQkRnp0E2ulSAYSzAw==} + + '@types/node@24.7.2': + resolution: {integrity: sha512-/NbVmcGTP+lj5oa4yiYxxeBjRivKQ5Ns1eSZeB99ExsEQ6rX5XYU1Zy/gGxY/ilqtD4Etx9mKyrPxZRetiahhA==} + + '@types/parse-json@4.0.2': + resolution: {integrity: sha512-dISoDXWWQwUquiKsyZ4Ng+HX2KsPL7LyHKHQwgGFEA3IaKac4Obd+h2a/a6waisAoepJlBcx9paWqjA8/HVjCw==} + + '@types/prettier@2.7.3': + resolution: {integrity: sha512-+68kP9yzs4LMp7VNh8gdzMSPZFL44MLGqiHWvttYJe+6qnuVr4Ek9wSBQoveqY/r+LwjCcU29kNVkidwim+kYA==} + + '@types/prop-types@15.7.15': + resolution: {integrity: sha512-F6bEyamV9jKGAFBEmlQnesRPGOQqS2+Uwi0Em15xenOxHaf2hv6L8YCVn3rPdPJOiJfPiCnLIRyvwVaqMY3MIw==} + + '@types/q@1.5.8': + resolution: {integrity: sha512-hroOstUScF6zhIi+5+x0dzqrHA1EJi+Irri6b1fxolMTqqHIV/Cg77EtnQcZqZCu8hR3mX2BzIxN4/GzI68Kfw==} + + '@types/qs@6.14.0': + resolution: {integrity: sha512-eOunJqu0K1923aExK6y8p6fsihYEn/BYuQ4g0CxAAgFc4b/ZLN4CrsRZ55srTdqoiLzU2B2evC+apEIxprEzkQ==} + + '@types/range-parser@1.2.7': + resolution: {integrity: sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ==} + + '@types/react-dom@18.3.7': + resolution: {integrity: sha512-MEe3UeoENYVFXzoXEWsvcpg6ZvlrFNlOQ7EOsvhI3CfAXwzPfO8Qwuxd40nepsYKqyyVQnTdEfv68q91yLcKrQ==} + peerDependencies: + '@types/react': ^18.0.0 + + '@types/react-transition-group@4.4.12': + resolution: {integrity: sha512-8TV6R3h2j7a91c+1DXdJi3Syo69zzIZbz7Lg5tORM5LEJG7X/E6a1V3drRyBRZq7/utz7A+c4OgYLiLcYGHG6w==} + peerDependencies: + '@types/react': '*' + + '@types/react@18.3.26': + resolution: {integrity: sha512-RFA/bURkcKzx/X9oumPG9Vp3D3JUgus/d0b67KB0t5S/raciymilkOa66olh78MUI92QLbEJevO7rvqU/kjwKA==} + + '@types/readdir-glob@1.1.5': + resolution: {integrity: sha512-raiuEPUYqXu+nvtY2Pe8s8FEmZ3x5yAH4VkLdihcPdalvsHltomrRC9BzuStrJ9yk06470hS0Crw0f1pXqD+Hg==} + + '@types/resolve@1.17.1': + resolution: {integrity: sha512-yy7HuzQhj0dhGpD8RLXSZWEkLsV9ibvxvi6EiJ3bkqLAO1RGo0WbkWQiwpRlSFymTJRz0d3k5LM3kkx8ArDbLw==} + + '@types/retry@0.12.0': + resolution: {integrity: sha512-wWKOClTTiizcZhXnPY4wikVAwmdYHp8q6DmC+EJUzAMsycb7HB32Kh9RN4+0gExjmPmZSAQjgURXIGATPegAvA==} + + '@types/sanitize-html@2.16.0': + resolution: {integrity: sha512-l6rX1MUXje5ztPT0cAFtUayXF06DqPhRyfVXareEN5gGCFaP/iwsxIyKODr9XDhfxPpN6vXUFNfo5kZMXCxBtw==} + + '@types/semver@7.7.1': + resolution: {integrity: sha512-FmgJfu+MOcQ370SD0ev7EI8TlCAfKYU+B4m5T3yXc1CiRN94g/SZPtsCkk506aUDtlMnFZvasDwHHUcZUEaYuA==} + + '@types/send@0.17.5': + resolution: {integrity: sha512-z6F2D3cOStZvuk2SaP6YrwkNO65iTZcwA2ZkSABegdkAh/lf+Aa/YQndZVfmEXT5vgAp6zv06VQ3ejSVjAny4w==} + + '@types/send@1.2.0': + resolution: {integrity: sha512-zBF6vZJn1IaMpg3xUF25VK3gd3l8zwE0ZLRX7dsQyQi+jp4E8mMDJNGDYnYse+bQhYwWERTxVwHpi3dMOq7RKQ==} + + '@types/serve-index@1.9.4': + resolution: {integrity: sha512-qLpGZ/c2fhSs5gnYsQxtDEq3Oy8SXPClIXkW5ghvAvsNuVSA8k+gCONcUCS/UjLEYvYps+e8uBtfgXgvhwfNug==} + + '@types/serve-static@1.15.9': + resolution: {integrity: sha512-dOTIuqpWLyl3BBXU3maNQsS4A3zuuoYRNIvYSxxhebPfXg2mzWQEPne/nlJ37yOse6uGgR386uTpdsx4D0QZWA==} + + '@types/sinonjs__fake-timers@8.1.1': + resolution: {integrity: sha512-0kSuKjAS0TrGLJ0M/+8MaFkGsQhZpB6pxOmvS3K8FYI72K//YmdfoW9X2qPsAKh1mkwxGD5zib9s1FIFed6E8g==} + + '@types/sizzle@2.3.10': + resolution: {integrity: sha512-TC0dmN0K8YcWEAEfiPi5gJP14eJe30TTGjkvek3iM/1NdHHsdCA/Td6GvNndMOo/iSnIsZ4HuuhrYPDAmbxzww==} + + '@types/sockjs@0.3.36': + resolution: {integrity: sha512-MK9V6NzAS1+Ud7JV9lJLFqW85VbC9dq3LmwZCuBe4wBDgKC0Kj/jd8Xl+nSviU+Qc3+m7umHHyHg//2KSa0a0Q==} + + '@types/stack-utils@2.0.3': + resolution: {integrity: sha512-9aEbYZ3TbYMznPdcdr3SmIrLXwC/AKZXQeCf9Pgao5CKb8CyHuEX5jzWPTkvregvhRJHcpRO6BFoGW9ycaOkYw==} + + '@types/testing-library__jest-dom@5.14.9': + resolution: {integrity: sha512-FSYhIjFlfOpGSRyVoMBMuS3ws5ehFQODymf3vlI7U1K8c7PHwWwFY7VREfmsuzHSOnoKs/9/Y983ayOs7eRzqw==} + + '@types/triple-beam@1.3.5': + resolution: {integrity: sha512-6WaYesThRMCl19iryMYP7/x2OVgCtbIVflDGFpWnb9irXI3UjYE4AzmYuiUKY1AJstGijoY+MgUszMgRxIYTYw==} + + '@types/trusted-types@2.0.7': + resolution: {integrity: sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw==} + + '@types/unist@2.0.11': + resolution: {integrity: sha512-CmBKiL6NNo/OqgmMn95Fk9Whlp2mtvIv+KNpQKN2F4SjvrEesubTRWGYSg+BnWZOnlCaSTU1sMpsBOzgbYhnsA==} + + '@types/unist@3.0.3': + resolution: {integrity: sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==} + + '@types/use-sync-external-store@0.0.3': + resolution: {integrity: sha512-EwmlvuaxPNej9+T4v5AuBPJa2x2UOJVdjCtDHgcDqitUeOtjnJKJ+apYjVcAoBEMjKW1VVFGZLUb5+qqa09XFA==} + + '@types/uuid@9.0.8': + resolution: {integrity: sha512-jg+97EGIcY9AGHJJRaaPVgetKDsrTgbRjQ5Msgjh/DQKEFl0DtyRr/VCOyD1T2R1MNeWPK/u7JoGhlDZnKBAfA==} + + '@types/webidl-conversions@7.0.3': + resolution: {integrity: sha512-CiJJvcRtIgzadHCYXw7dqEnMNRjhGZlYK05Mj9OyktqV8uVT8fD2BFOB7S1uwBE3Kj2Z+4UyPmFw/Ixgw/LAlA==} + + '@types/whatwg-url@11.0.5': + resolution: {integrity: sha512-coYR071JRaHa+xoEvvYqvnIHaVqaYrLPbsufM9BF63HkwI5Lgmy2QR8Q5K/lYDYo5AK82wOvSOS0UsLTpTG7uQ==} + + '@types/ws@8.18.1': + resolution: {integrity: sha512-ThVF6DCVhA8kUGy+aazFQ4kXQ7E1Ty7A3ypFOe0IcJV8O/M511G99AW24irKrW56Wt44yG9+ij8FaqoBGkuBXg==} + + '@types/yargs-parser@21.0.3': + resolution: {integrity: sha512-I4q9QU9MQv4oEOz4tAHJtNz1cwuLxn2F3xcc2iV5WdqLPpUnj30aUuxt1mAxYTG+oe8CZMV/+6rU4S4gRDzqtQ==} + + '@types/yargs@16.0.9': + resolution: {integrity: sha512-tHhzvkFXZQeTECenFoRljLBYPZJ7jAVxqqtEI0qTLOmuultnFp4I9yKE17vTuhf7BkhCu7I4XuemPgikDVuYqA==} + + '@types/yargs@17.0.33': + resolution: {integrity: sha512-WpxBCKWPLr4xSsHgz511rFJAM+wS28w2zEO1QDNY5zM/S8ok70NNfztH0xwhqKyaK0OHCbN98LDAZuy1ctxDkA==} + + '@types/yauzl@2.10.3': + resolution: {integrity: sha512-oJoftv0LSuaDZE3Le4DbKX+KS9G36NzOeSap90UIK0yMA/NhKJhqlSGtNDORNRaIbQfzjXDrQa0ytJ6mNRGz/Q==} + + '@typescript-eslint/eslint-plugin@5.62.0': + resolution: {integrity: sha512-TiZzBSJja/LbhNPvk6yc0JrX9XqhQ0hdh6M2svYfsHGejaKFIAGd9MQ+ERIMzLGlN/kZoYIgdxFV0PuljTKXag==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + peerDependencies: + '@typescript-eslint/parser': ^5.0.0 + eslint: ^6.0.0 || ^7.0.0 || ^8.0.0 + typescript: '*' + peerDependenciesMeta: + typescript: + optional: true + + '@typescript-eslint/experimental-utils@5.62.0': + resolution: {integrity: sha512-RTXpeB3eMkpoclG3ZHft6vG/Z30azNHuqY6wKPBHlVMZFuEvrtlEDe8gMqDb+SO+9hjC/pLekeSCryf9vMZlCw==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + peerDependencies: + eslint: ^6.0.0 || ^7.0.0 || ^8.0.0 + + '@typescript-eslint/parser@5.62.0': + resolution: {integrity: sha512-VlJEV0fOQ7BExOsHYAGrgbEiZoi8D+Bl2+f6V2RrXerRSylnp+ZBHmPvaIa8cz0Ajx7WO7Z5RqfgYg7ED1nRhA==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + peerDependencies: + eslint: ^6.0.0 || ^7.0.0 || ^8.0.0 + typescript: '*' + peerDependenciesMeta: + typescript: + optional: true + + '@typescript-eslint/scope-manager@5.62.0': + resolution: {integrity: sha512-VXuvVvZeQCQb5Zgf4HAxc04q5j+WrNAtNh9OwCsCgpKqESMTu3tF/jhZ3xG6T4NZwWl65Bg8KuS2uEvhSfLl0w==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + + '@typescript-eslint/type-utils@5.62.0': + resolution: {integrity: sha512-xsSQreu+VnfbqQpW5vnCJdq1Z3Q0U31qiWmRhr98ONQmcp/yhiPJFPq8MXiJVLiksmOKSjIldZzkebzHuCGzew==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + peerDependencies: + eslint: '*' + typescript: '*' + peerDependenciesMeta: + typescript: + optional: true + + '@typescript-eslint/types@5.62.0': + resolution: {integrity: sha512-87NVngcbVXUahrRTqIK27gD2t5Cu1yuCXxbLcFtCzZGlfyVWWh8mLHkoxzjsB6DDNnvdL+fW8MiwPEJyGJQDgQ==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + + '@typescript-eslint/types@7.18.0': + resolution: {integrity: sha512-iZqi+Ds1y4EDYUtlOOC+aUmxnE9xS/yCigkjA7XpTKV6nCBd3Hp/PRGGmdwnfkV2ThMyYldP1wRpm/id99spTQ==} + engines: {node: ^18.18.0 || >=20.0.0} + + '@typescript-eslint/typescript-estree@5.62.0': + resolution: {integrity: sha512-CmcQ6uY7b9y694lKdRB8FEel7JbU/40iSAPomu++SjLMntB+2Leay2LO6i8VnJk58MtE9/nQSFIH6jpyRWyYzA==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + peerDependencies: + typescript: '*' + peerDependenciesMeta: + typescript: + optional: true + + '@typescript-eslint/typescript-estree@7.18.0': + resolution: {integrity: sha512-aP1v/BSPnnyhMHts8cf1qQ6Q1IFwwRvAQGRvBFkWlo3/lH29OXA3Pts+c10nxRxIBrDnoMqzhgdwVe5f2D6OzA==} + engines: {node: ^18.18.0 || >=20.0.0} + peerDependencies: + typescript: '*' + peerDependenciesMeta: + typescript: + optional: true + + '@typescript-eslint/utils@5.62.0': + resolution: {integrity: sha512-n8oxjeb5aIbPFEtmQxQYOLI0i9n5ySBEY/ZEHHZqKQSFnxio1rv6dthascc9dLuwrL0RC5mPCxB7vnAVGAYWAQ==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + peerDependencies: + eslint: ^6.0.0 || ^7.0.0 || ^8.0.0 + + '@typescript-eslint/visitor-keys@5.62.0': + resolution: {integrity: sha512-07ny+LHRzQXepkGg6w0mFY41fVUNBrL2Roj/++7V1txKugfjm/Ci/qSND03r2RhlJhJYMcTn9AhhSSqQp0Ysyw==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + + '@typescript-eslint/visitor-keys@7.18.0': + resolution: {integrity: sha512-cDF0/Gf81QpY3xYyJKDV14Zwdmid5+uuENhjH2EqFaF0ni+yAyq/LzMaIJdhNJXZI7uLzwIlA+V7oWoyn6Curg==} + engines: {node: ^18.18.0 || >=20.0.0} + + '@uiw/codemirror-extensions-basic-setup@4.25.2': + resolution: {integrity: sha512-s2fbpdXrSMWEc86moll/d007ZFhu6jzwNu5cWv/2o7egymvLeZO52LWkewgbr+BUCGWGPsoJVWeaejbsb/hLcw==} + peerDependencies: + '@codemirror/autocomplete': '>=6.0.0' + '@codemirror/commands': '>=6.0.0' + '@codemirror/language': '>=6.0.0' + '@codemirror/lint': '>=6.0.0' + '@codemirror/search': '>=6.0.0' + '@codemirror/state': '>=6.0.0' + '@codemirror/view': '>=6.0.0' + + '@uiw/codemirror-theme-sublime@4.25.2': + resolution: {integrity: sha512-QPqyX6yt9YirBSMmXbk98EfxGwdOPZ/LdM8zhvwITHF2+guYHfi+YPePOeGlAO9sGz5eG2ns1SqhTxDEnLurYQ==} + + '@uiw/codemirror-theme-vscode@4.25.2': + resolution: {integrity: sha512-0vZAAtC65v64sYKtUMvgg9xPw1QlcnFTzGv8vZ5fD4SmSXFZWXTapjezwhGmvR7/UdAPFyh/4korgBnQo9ix3A==} + + '@uiw/codemirror-themes@4.25.2': + resolution: {integrity: sha512-WFYxW3OlCkMomXQBlQdGj1JZ011UNCa7xYdmgYqywVc4E8f5VgIzRwCZSBNVjpWGGDBOjc+Z6F65l7gttP16pg==} + peerDependencies: + '@codemirror/language': '>=6.0.0' + '@codemirror/state': '>=6.0.0' + '@codemirror/view': '>=6.0.0' + + '@uiw/react-codemirror@4.25.2': + resolution: {integrity: sha512-XP3R1xyE0CP6Q0iR0xf3ed+cJzJnfmbLelgJR6osVVtMStGGZP3pGQjjwDRYptmjGHfEELUyyBLdY25h0BQg7w==} + peerDependencies: + '@babel/runtime': '>=7.11.0' + '@codemirror/state': '>=6.0.0' + '@codemirror/theme-one-dark': '>=6.0.0' + '@codemirror/view': '>=6.0.0' + codemirror: '>=6.0.0' + react: '>=17.0.0' + react-dom: '>=17.0.0' + + '@ungap/structured-clone@1.3.0': + resolution: {integrity: sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g==} + + '@vitejs/plugin-react@3.1.0': + resolution: {integrity: sha512-AfgcRL8ZBhAlc3BFdigClmTUMISmmzHn7sB2h9U1odvc5U/MjWXsAaz18b/WoppUTDBzxOJwo2VdClfUcItu9g==} + engines: {node: ^14.18.0 || >=16.0.0} + peerDependencies: + vite: ^4.1.0-beta.0 + + '@vitejs/plugin-react@4.7.0': + resolution: {integrity: sha512-gUu9hwfWvvEDBBmgtAowQCojwZmJ5mcLn3aufeCsitijs3+f2NsrPtlAWIR6OPiqljl96GVCUbLe0HyqIpVaoA==} + engines: {node: ^14.18.0 || >=16.0.0} + peerDependencies: + vite: ^4.2.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 + + '@webassemblyjs/ast@1.14.1': + resolution: {integrity: sha512-nuBEDgQfm1ccRp/8bCQrx1frohyufl4JlbMMZ4P1wpeOfDhF6FQkxZJ1b/e+PLwr6X1Nhw6OLme5usuBWYBvuQ==} + + '@webassemblyjs/floating-point-hex-parser@1.13.2': + resolution: {integrity: sha512-6oXyTOzbKxGH4steLbLNOu71Oj+C8Lg34n6CqRvqfS2O71BxY6ByfMDRhBytzknj9yGUPVJ1qIKhRlAwO1AovA==} + + '@webassemblyjs/helper-api-error@1.13.2': + resolution: {integrity: sha512-U56GMYxy4ZQCbDZd6JuvvNV/WFildOjsaWD3Tzzvmw/mas3cXzRJPMjP83JqEsgSbyrmaGjBfDtV7KDXV9UzFQ==} + + '@webassemblyjs/helper-buffer@1.14.1': + resolution: {integrity: sha512-jyH7wtcHiKssDtFPRB+iQdxlDf96m0E39yb0k5uJVhFGleZFoNw1c4aeIcVUPPbXUVJ94wwnMOAqUHyzoEPVMA==} + + '@webassemblyjs/helper-numbers@1.13.2': + resolution: {integrity: sha512-FE8aCmS5Q6eQYcV3gI35O4J789wlQA+7JrqTTpJqn5emA4U2hvwJmvFRC0HODS+3Ye6WioDklgd6scJ3+PLnEA==} + + '@webassemblyjs/helper-wasm-bytecode@1.13.2': + resolution: {integrity: sha512-3QbLKy93F0EAIXLh0ogEVR6rOubA9AoZ+WRYhNbFyuB70j3dRdwH9g+qXhLAO0kiYGlg3TxDV+I4rQTr/YNXkA==} + + '@webassemblyjs/helper-wasm-section@1.14.1': + resolution: {integrity: sha512-ds5mXEqTJ6oxRoqjhWDU83OgzAYjwsCV8Lo/N+oRsNDmx/ZDpqalmrtgOMkHwxsG0iI//3BwWAErYRHtgn0dZw==} + + '@webassemblyjs/ieee754@1.13.2': + resolution: {integrity: sha512-4LtOzh58S/5lX4ITKxnAK2USuNEvpdVV9AlgGQb8rJDHaLeHciwG4zlGr0j/SNWlr7x3vO1lDEsuePvtcDNCkw==} + + '@webassemblyjs/leb128@1.13.2': + resolution: {integrity: sha512-Lde1oNoIdzVzdkNEAWZ1dZ5orIbff80YPdHx20mrHwHrVNNTjNr8E3xz9BdpcGqRQbAEa+fkrCb+fRFTl/6sQw==} + + '@webassemblyjs/utf8@1.13.2': + resolution: {integrity: sha512-3NQWGjKTASY1xV5m7Hr0iPeXD9+RDobLll3T9d2AO+g3my8xy5peVyjSag4I50mR1bBSN/Ct12lo+R9tJk0NZQ==} + + '@webassemblyjs/wasm-edit@1.14.1': + resolution: {integrity: sha512-RNJUIQH/J8iA/1NzlE4N7KtyZNHi3w7at7hDjvRNm5rcUXa00z1vRz3glZoULfJ5mpvYhLybmVcwcjGrC1pRrQ==} + + '@webassemblyjs/wasm-gen@1.14.1': + resolution: {integrity: sha512-AmomSIjP8ZbfGQhumkNvgC33AY7qtMCXnN6bL2u2Js4gVCg8fp735aEiMSBbDR7UQIj90n4wKAFUSEd0QN2Ukg==} + + '@webassemblyjs/wasm-opt@1.14.1': + resolution: {integrity: sha512-PTcKLUNvBqnY2U6E5bdOQcSM+oVP/PmrDY9NzowJjislEjwP/C4an2303MCVS2Mg9d3AJpIGdUFIQQWbPds0Sw==} + + '@webassemblyjs/wasm-parser@1.14.1': + resolution: {integrity: sha512-JLBl+KZ0R5qB7mCnud/yyX08jWFw5MsoalJ1pQ4EdFlgj9VdXKGuENGsiCIjegI1W7p91rUlcB/LB5yRJKNTcQ==} + + '@webassemblyjs/wast-printer@1.14.1': + resolution: {integrity: sha512-kPSSXE6De1XOR820C90RIo2ogvZG+c3KiHzqUoO/F34Y2shGzesfqv7o57xrxovZJH/MetF5UjroJ/R/3isoiw==} + + '@xmldom/xmldom@0.9.8': + resolution: {integrity: sha512-p96FSY54r+WJ50FIOsCOjyj/wavs8921hG5+kVMmZgKcvIKxMXHTrjNJvRgWa/zuX3B6t2lijLNFaOyuxUH+2A==} + engines: {node: '>=14.6'} + deprecated: this version has critical issues, please update to the latest version + + '@xtuc/ieee754@1.2.0': + resolution: {integrity: sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA==} + + '@xtuc/long@4.2.2': + resolution: {integrity: sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==} + + abab@2.0.6: + resolution: {integrity: sha512-j2afSsaIENvHZN2B8GOpF566vZ5WVk5opAiMTvWgaQT8DkbOqsTfvNAvHoRGU2zzP8cPoqys+xHTRDWW8L+/BA==} + deprecated: Use your platform's native atob() and btoa() methods instead + + abbrev@1.1.1: + resolution: {integrity: sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==} + + accepts@1.3.8: + resolution: {integrity: sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==} + engines: {node: '>= 0.6'} + + acorn-globals@6.0.0: + resolution: {integrity: sha512-ZQl7LOWaF5ePqqcX4hLuv/bLXYQNfNWw2c0/yX/TsPRKamzHcTGQnlCjHT3TsmkOUVEPS3crCxiPfdzE/Trlhg==} + + acorn-globals@7.0.1: + resolution: {integrity: sha512-umOSDSDrfHbTNPuNpC2NSnnA3LUrqpevPb4T9jRx4MagXNS0rs+gwiTcAvqCRmsD6utzsrzNt+ebm00SNWiC3Q==} + + acorn-import-phases@1.0.4: + resolution: {integrity: sha512-wKmbr/DDiIXzEOiWrTTUcDm24kQ2vGfZQvM2fwg2vXqR5uW6aapr7ObPtj1th32b9u90/Pf4AItvdTh42fBmVQ==} + engines: {node: '>=10.13.0'} + peerDependencies: + acorn: ^8.14.0 + + acorn-jsx@5.3.2: + resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==} + peerDependencies: + acorn: ^6.0.0 || ^7.0.0 || ^8.0.0 + + acorn-walk@7.2.0: + resolution: {integrity: sha512-OPdCF6GsMIP+Az+aWfAAOEt2/+iVDKE7oy6lJ098aoe59oAmK76qV6Gw60SbZ8jHuG2wH058GF4pLFbYamYrVA==} + engines: {node: '>=0.4.0'} + + acorn-walk@8.3.4: + resolution: {integrity: sha512-ueEepnujpqee2o5aIYnvHU6C0A42MNdsIDeqy5BydrkuC5R1ZuUFnm27EeFJGoEHJQgn3uleRvmTXaJgfXbt4g==} + engines: {node: '>=0.4.0'} + + acorn@7.4.1: + resolution: {integrity: sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==} + engines: {node: '>=0.4.0'} + hasBin: true + + acorn@8.15.0: + resolution: {integrity: sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==} + engines: {node: '>=0.4.0'} + hasBin: true + + address@1.2.2: + resolution: {integrity: sha512-4B/qKCfeE/ODUaAUpSwfzazo5x29WD4r3vXiWsB7I2mSDAihwEqKO+g8GELZUQSSAo5e1XTYh3ZVfLyxBc12nA==} + engines: {node: '>= 10.0.0'} + + adjust-sourcemap-loader@4.0.0: + resolution: {integrity: sha512-OXwN5b9pCUXNQHJpwwD2qP40byEmSgzj8B4ydSN0uMNYWiFmJ6x6KwUllMmfk8Rwu/HJDFR7U8ubsWBoN0Xp0A==} + engines: {node: '>=8.9'} + + agent-base@6.0.2: + resolution: {integrity: sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==} + engines: {node: '>= 6.0.0'} + + agent-base@7.1.4: + resolution: {integrity: sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==} + engines: {node: '>= 14'} + + agentkeepalive@4.6.0: + resolution: {integrity: sha512-kja8j7PjmncONqaTsB8fQ+wE2mSU2DJ9D4XKoJ5PFWIdRMa6SLSN1ff4mOr4jCbfRSsxR4keIiySJU0N9T5hIQ==} + engines: {node: '>= 8.0.0'} + + aggregate-error@3.1.0: + resolution: {integrity: sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==} + engines: {node: '>=8'} + + ajv-formats@2.1.1: + resolution: {integrity: sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==} + peerDependencies: + ajv: ^8.0.0 + peerDependenciesMeta: + ajv: + optional: true + + ajv-keywords@3.5.2: + resolution: {integrity: sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==} + peerDependencies: + ajv: ^6.9.1 + + ajv-keywords@5.1.0: + resolution: {integrity: sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==} + peerDependencies: + ajv: ^8.8.2 + + ajv@6.12.6: + resolution: {integrity: sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==} + + ajv@8.17.1: + resolution: {integrity: sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==} + + ansi-align@3.0.1: + resolution: {integrity: sha512-IOfwwBF5iczOjp/WeY4YxyjqAFMQoZufdQWDd19SEExbVLNXqvpzSJ/M7Za4/sCPmQ0+GRquoA7bGcINcxew6w==} + + ansi-colors@4.1.3: + resolution: {integrity: sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==} + engines: {node: '>=6'} + + ansi-escapes@4.3.2: + resolution: {integrity: sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==} + engines: {node: '>=8'} + + ansi-escapes@5.0.0: + resolution: {integrity: sha512-5GFMVX8HqE/TB+FuBJGuO5XG0WrsA6ptUqoODaT/n9mmUaZFkqnBueB4leqGBCmrUHnCnC4PCZTCd0E7QQ83bA==} + engines: {node: '>=12'} + + ansi-html-community@0.0.8: + resolution: {integrity: sha512-1APHAyr3+PCamwNw3bXCPp4HFLONZt/yIH0sZp0/469KWNTEy+qN5jQ3GVX6DMZ1UXAi34yVwtTeaG/HpBuuzw==} + engines: {'0': node >= 0.8.0} + hasBin: true + + ansi-html@0.0.9: + resolution: {integrity: sha512-ozbS3LuenHVxNRh/wdnN16QapUHzauqSomAl1jwwJRRsGwFwtj644lIhxfWu0Fy0acCij2+AEgHvjscq3dlVXg==} + engines: {'0': node >= 0.8.0} + hasBin: true + + ansi-regex@5.0.1: + resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==} + engines: {node: '>=8'} + + ansi-regex@6.2.2: + resolution: {integrity: sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==} + engines: {node: '>=12'} + + ansi-styles@3.2.1: + resolution: {integrity: sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==} + engines: {node: '>=4'} + + ansi-styles@4.3.0: + resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==} + engines: {node: '>=8'} + + ansi-styles@5.2.0: + resolution: {integrity: sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==} + engines: {node: '>=10'} + + ansi-styles@6.2.3: + resolution: {integrity: sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==} + engines: {node: '>=12'} + + ansis@3.17.0: + resolution: {integrity: sha512-0qWUglt9JEqLFr3w1I1pbrChn1grhaiAR2ocX1PP/flRmxgtwTzPFFFnfIlD6aMOLQZgSuCRlidD70lvx8yhzg==} + engines: {node: '>=14'} + + any-promise@1.3.0: + resolution: {integrity: sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==} + + anymatch@3.1.3: + resolution: {integrity: sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==} + engines: {node: '>= 8'} + + app-root-path@3.1.0: + resolution: {integrity: sha512-biN3PwB2gUtjaYy/isrU3aNWI5w+fAfvHkSvCKeQGxhmYpwKFUxudR3Yya+KqVRHBmEDYh+/lTozYCFbmzX4nA==} + engines: {node: '>= 6.0.0'} + + append-field@1.0.0: + resolution: {integrity: sha512-klpgFSWLW1ZEs8svjfb7g4qWY0YS5imI82dTg+QahUvJ8YqAY0P10Uk8tTyh9ZGuYEZEMaeJYCF5BFuX552hsw==} + + aproba@2.1.0: + resolution: {integrity: sha512-tLIEcj5GuR2RSTnxNKdkK0dJ/GrC7P38sUkiDmDuHfsHmbagTFAxDVIBltoklXEVIQ/f14IL8IMJ5pn9Hez1Ew==} + + arch@2.2.0: + resolution: {integrity: sha512-Of/R0wqp83cgHozfIYLbBMnej79U/SVGOOyuB3VVFv1NRM/PSFMK12x9KVtiYzJqmnU5WR2qp0Z5rHb7sWGnFQ==} + + archiver-utils@4.0.1: + resolution: {integrity: sha512-Q4Q99idbvzmgCTEAAhi32BkOyq8iVI5EwdO0PmBDSGIzzjYNdcFn7Q7k3OzbLy4kLUPXfJtG6fO2RjftXbobBg==} + engines: {node: '>= 12.0.0'} + + archiver@6.0.2: + resolution: {integrity: sha512-UQ/2nW7NMl1G+1UnrLypQw1VdT9XZg/ECcKPq7l+STzStrSivFIXIp34D8M5zeNGW5NoOupdYCHv6VySCPNNlw==} + engines: {node: '>= 12.0.0'} + + are-we-there-yet@2.0.0: + resolution: {integrity: sha512-Ci/qENmwHnsYo9xKIcUJN5LeDKdJ6R1Z1j9V/J5wyq8nh/mYPEpIKJbBZXtZjG04HiK7zV/p6Vs9952MrMeUIw==} + engines: {node: '>=10'} + deprecated: This package is no longer supported. + + are-we-there-yet@3.0.1: + resolution: {integrity: sha512-QZW4EDmGwlYur0Yyf/b2uGucHQMa8aFUP7eu9ddR73vvhFyt4V0Vl3QHPcTNJ8l6qYOBdxgXdnBXQrHilfRQBg==} + engines: {node: ^12.13.0 || ^14.15.0 || >=16.0.0} + deprecated: This package is no longer supported. + + arg@4.1.3: + resolution: {integrity: sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==} + + arg@5.0.2: + resolution: {integrity: sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==} + + argparse@1.0.10: + resolution: {integrity: sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==} + + argparse@2.0.1: + resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==} + + aria-query@5.1.3: + resolution: {integrity: sha512-R5iJ5lkuHybztUfuOAznmboyjWq8O6sqNqtK7CLOqdydi54VNbORp49mb14KbWgG1QD3JFO9hJdZ+y4KutfdOQ==} + + aria-query@5.3.2: + resolution: {integrity: sha512-COROpnaoap1E2F000S62r6A60uHZnmlvomhfyT2DlTcrY1OrBKn2UhH7qn5wTC9zMvD0AY7csdPSNwKP+7WiQw==} + engines: {node: '>= 0.4'} + + array-buffer-byte-length@1.0.2: + resolution: {integrity: sha512-LHE+8BuR7RYGDKvnrmcuSq3tDcKv9OFEXQt/HpbZhY7V6h0zlUXutnAD82GiFx9rdieCMjkvtcsPqBwgUl1Iiw==} + engines: {node: '>= 0.4'} + + array-flatten@1.1.1: + resolution: {integrity: sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==} + + array-includes@3.1.9: + resolution: {integrity: sha512-FmeCCAenzH0KH381SPT5FZmiA/TmpndpcaShhfgEN9eCVjnFBqq3l1xrI42y8+PPLI6hypzou4GXw00WHmPBLQ==} + engines: {node: '>= 0.4'} + + array-union@2.1.0: + resolution: {integrity: sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==} + engines: {node: '>=8'} + + array.prototype.findlast@1.2.5: + resolution: {integrity: sha512-CVvd6FHg1Z3POpBLxO6E6zr+rSKEQ9L6rZHAaY7lLfhKsWYUBBOuMs0e9o24oopj6H+geRCX0YJ+TJLBK2eHyQ==} + engines: {node: '>= 0.4'} + + array.prototype.findlastindex@1.2.6: + resolution: {integrity: sha512-F/TKATkzseUExPlfvmwQKGITM3DGTK+vkAsCZoDc5daVygbJBnjEUCbgkAvVFsgfXfX4YIqZ/27G3k3tdXrTxQ==} + engines: {node: '>= 0.4'} + + array.prototype.flat@1.3.3: + resolution: {integrity: sha512-rwG/ja1neyLqCuGZ5YYrznA62D4mZXg0i1cIskIUKSiqF3Cje9/wXAls9B9s1Wa2fomMsIv8czB8jZcPmxCXFg==} + engines: {node: '>= 0.4'} + + array.prototype.flatmap@1.3.3: + resolution: {integrity: sha512-Y7Wt51eKJSyi80hFrJCePGGNo5ktJCslFuboqJsbf57CCPcm5zztluPlc4/aD8sWsKvlwatezpV4U1efk8kpjg==} + engines: {node: '>= 0.4'} + + array.prototype.reduce@1.0.8: + resolution: {integrity: sha512-DwuEqgXFBwbmZSRqt3BpQigWNUoqw9Ml2dTWdF3B2zQlQX4OeUE0zyuzX0fX0IbTvjdkZbcBTU3idgpO78qkTw==} + engines: {node: '>= 0.4'} + + array.prototype.tosorted@1.1.4: + resolution: {integrity: sha512-p6Fx8B7b7ZhL/gmUsAy0D15WhvDccw3mnGNbZpi3pmeJdxtWsj2jEaI4Y6oo3XiHfzuSgPwKc04MYt6KgvC/wA==} + engines: {node: '>= 0.4'} + + arraybuffer.prototype.slice@1.0.4: + resolution: {integrity: sha512-BNoCY6SXXPQ7gF2opIP4GBE+Xw7U+pHMYKuzjgCN3GwiaIR09UUeKfheyIry77QtrCBlC0KK0q5/TER/tYh3PQ==} + engines: {node: '>= 0.4'} + + asap@2.0.6: + resolution: {integrity: sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA==} + + asn1@0.2.6: + resolution: {integrity: sha512-ix/FxPn0MDjeyJ7i/yoHGFt/EX6LyNbxSEhPPXODPL+KB0VPk86UYfL0lMdy+KCnv+fmvIzySwaK5COwqVbWTQ==} + + assert-plus@1.0.0: + resolution: {integrity: sha512-NfJ4UzBCcQGLDlQq7nHxH+tv3kyZ0hHQqF5BO6J7tNJeP5do1llPr8dZ8zHonfhAu0PHAdMkSo+8o0wxg9lZWw==} + engines: {node: '>=0.8'} + + ast-types-flow@0.0.8: + resolution: {integrity: sha512-OH/2E5Fg20h2aPrbe+QL8JZQFko0YZaF+j4mnQ7BGhfavO7OpSLa8a0y9sBwomHdSbkhTS8TQNayBfnW5DwbvQ==} + + astral-regex@2.0.0: + resolution: {integrity: sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==} + engines: {node: '>=8'} + + async-function@1.0.0: + resolution: {integrity: sha512-hsU18Ae8CDTR6Kgu9DYf0EbCr/a5iGL0rytQDobUcdpYOKokk8LEjVphnXkDkgpi0wYVsqrXuP0bZxJaTqdgoA==} + engines: {node: '>= 0.4'} + + async-mutex@0.4.1: + resolution: {integrity: sha512-WfoBo4E/TbCX1G95XTjbWTE3X2XLG0m1Xbv2cwOtuPdyH9CZvnaA5nCt1ucjaKEgW2A5IF71hxrRhr83Je5xjA==} + + async@3.2.6: + resolution: {integrity: sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA==} + + asynckit@0.4.0: + resolution: {integrity: sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==} + + at-least-node@1.0.0: + resolution: {integrity: sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg==} + engines: {node: '>= 4.0.0'} + + autoprefixer@10.4.21: + resolution: {integrity: sha512-O+A6LWV5LDHSJD3LjHYoNi4VLsj/Whi7k6zG12xTYaU4cQ8oxQGckXNX8cRHK5yOZ/ppVHe0ZBXGzSV9jXdVbQ==} + engines: {node: ^10 || ^12 || >=14} + hasBin: true + peerDependencies: + postcss: ^8.1.0 + + available-typed-arrays@1.0.7: + resolution: {integrity: sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==} + engines: {node: '>= 0.4'} + + aws-sign2@0.7.0: + resolution: {integrity: sha512-08kcGqnYf/YmjoRhfxyu+CLxBjUtHLXLXX/vUfx9l2LYzG3c1m61nrpyFUZI6zeS+Li/wWMMidD9KgrqtGq3mA==} + + aws-ssl-profiles@1.1.2: + resolution: {integrity: sha512-NZKeq9AfyQvEeNlN0zSYAaWrmBffJh3IELMZfRpJVWgrpEbtEpnjvzqBPf+mxoI287JohRDoa+/nsfqqiZmF6g==} + engines: {node: '>= 6.0.0'} + + aws4@1.13.2: + resolution: {integrity: sha512-lHe62zvbTB5eEABUVi/AwVh0ZKY9rMMDhmm+eeyuuUQbQ3+J+fONVQOZyj+DdrvD4BY33uYniyRJ4UJIaSKAfw==} + + axe-core@4.11.0: + resolution: {integrity: sha512-ilYanEU8vxxBexpJd8cWM4ElSQq4QctCLKih0TSfjIfCQTeyH/6zVrmIJfLPrKTKJRbiG+cfnZbQIjAlJmF1jQ==} + engines: {node: '>=4'} + + axios@1.12.0: + resolution: {integrity: sha512-oXTDccv8PcfjZmPGlWsPSwtOJCZ/b6W5jAMCNcfwJbCzDckwG0jrYJFaWH1yvivfCXjVzV/SPDEhMB3Q+DSurg==} + + axobject-query@4.1.0: + resolution: {integrity: sha512-qIj0G9wZbMGNLjLmg1PT6v2mE9AH2zlnADJD/2tC6E00hgmhUOfEB6greHPAfLRSufHqROIUTkw6E+M3lH0PTQ==} + engines: {node: '>= 0.4'} + + b4a@1.7.3: + resolution: {integrity: sha512-5Q2mfq2WfGuFp3uS//0s6baOJLMoVduPYVeNmDYxu5OUA1/cBfvr2RIS7vi62LdNj/urk1hfmj867I3qt6uZ7Q==} + peerDependencies: + react-native-b4a: '*' + peerDependenciesMeta: + react-native-b4a: + optional: true + + babel-jest@27.5.1: + resolution: {integrity: sha512-cdQ5dXjGRd0IBRATiQ4mZGlGlRE8kJpjPOixdNRdT+m3UcNqmYWN6rK6nvtXYfY3D76cb8s/O1Ss8ea24PIwcg==} + engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0} + peerDependencies: + '@babel/core': ^7.8.0 + + babel-loader@8.4.1: + resolution: {integrity: sha512-nXzRChX+Z1GoE6yWavBQg6jDslyFF3SDjl2paADuoQtQW10JqShJt62R6eJQ5m/pjJFDT8xgKIWSP85OY8eXeA==} + engines: {node: '>= 8.9'} + peerDependencies: + '@babel/core': ^7.0.0 + webpack: '>=2' + + babel-plugin-istanbul@6.1.1: + resolution: {integrity: sha512-Y1IQok9821cC9onCx5otgFfRm7Lm+I+wwxOx738M/WLPZ9Q42m4IG5W0FNX8WLL2gYMZo3JkuXIH2DOpWM+qwA==} + engines: {node: '>=8'} + + babel-plugin-jest-hoist@27.5.1: + resolution: {integrity: sha512-50wCwD5EMNW4aRpOwtqzyZHIewTYNxLA4nhB+09d8BIssfNfzBRhkBIHiaPv1Si226TQSvp8gxAJm2iY2qs2hQ==} + engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0} + + babel-plugin-macros@3.1.0: + resolution: {integrity: sha512-Cg7TFGpIr01vOQNODXOOaGz2NpCU5gl8x1qJFbb6hbZxR7XrcE2vtbAsTAbJ7/xwJtUuJEw8K8Zr/AE0LHlesg==} + engines: {node: '>=10', npm: '>=6'} + + babel-plugin-named-asset-import@0.3.8: + resolution: {integrity: sha512-WXiAc++qo7XcJ1ZnTYGtLxmBCVbddAml3CEXgWaBzNzLNoxtQ8AiGEFDMOhot9XjTCQbvP5E77Fj9Gk924f00Q==} + peerDependencies: + '@babel/core': ^7.1.0 + + babel-plugin-polyfill-corejs2@0.4.14: + resolution: {integrity: sha512-Co2Y9wX854ts6U8gAAPXfn0GmAyctHuK8n0Yhfjd6t30g7yvKjspvvOo9yG+z52PZRgFErt7Ka2pYnXCjLKEpg==} + peerDependencies: + '@babel/core': ^7.4.0 || ^8.0.0-0 <8.0.0 + + babel-plugin-polyfill-corejs3@0.13.0: + resolution: {integrity: sha512-U+GNwMdSFgzVmfhNm8GJUX88AadB3uo9KpJqS3FaqNIPKgySuvMb+bHPsOmmuWyIcuqZj/pzt1RUIUZns4y2+A==} + peerDependencies: + '@babel/core': ^7.4.0 || ^8.0.0-0 <8.0.0 + + babel-plugin-polyfill-regenerator@0.6.5: + resolution: {integrity: sha512-ISqQ2frbiNU9vIJkzg7dlPpznPZ4jOiUQ1uSmB0fEHeowtN3COYRsXr/xexn64NpU13P06jc/L5TgiJXOgrbEg==} + peerDependencies: + '@babel/core': ^7.4.0 || ^8.0.0-0 <8.0.0 + + babel-plugin-transform-react-remove-prop-types@0.4.24: + resolution: {integrity: sha512-eqj0hVcJUR57/Ug2zE1Yswsw4LhuqqHhD+8v120T1cl3kjg76QwtyBrdIk4WVwK+lAhBJVYCd/v+4nc4y+8JsA==} + + babel-preset-current-node-syntax@1.2.0: + resolution: {integrity: sha512-E/VlAEzRrsLEb2+dv8yp3bo4scof3l9nR4lrld+Iy5NyVqgVYUJnDAmunkhPMisRI32Qc4iRiz425d8vM++2fg==} + peerDependencies: + '@babel/core': ^7.0.0 || ^8.0.0-0 + + babel-preset-jest@27.5.1: + resolution: {integrity: sha512-Nptf2FzlPCWYuJg41HBqXVT8ym6bXOevuCTbhxlUpjwtysGaIWFvDEjp4y+G7fl13FgOdjs7P/DmErqH7da0Ag==} + engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0} + peerDependencies: + '@babel/core': ^7.0.0 + + babel-preset-react-app@10.1.0: + resolution: {integrity: sha512-f9B1xMdnkCIqe+2dHrJsoQFRz7reChaAHE/65SdaykPklQqhme2WaC08oD3is77x9ff98/9EazAKFDZv5rFEQg==} + + bail@2.0.2: + resolution: {integrity: sha512-0xO6mYd7JB2YesxDKplafRpsiOzPt9V02ddPCLbY1xYGPOX24NTyN50qnUxgCPcSoYMhKpAuBTjQoRZCAkUDRw==} + + balanced-match@1.0.2: + resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} + + bare-events@2.8.0: + resolution: {integrity: sha512-AOhh6Bg5QmFIXdViHbMc2tLDsBIRxdkIaIddPslJF9Z5De3APBScuqGP2uThXnIpqFrgoxMNC6km7uXNIMLHXA==} + peerDependencies: + bare-abort-controller: '*' + peerDependenciesMeta: + bare-abort-controller: + optional: true + + bare-fs@4.4.10: + resolution: {integrity: sha512-arqVF+xX/rJHwrONZaSPhlzleT2gXwVs9rsAe1p1mIVwWZI2A76/raio+KwwxfWMO8oV9Wo90EaUkS2QwVmy4w==} + engines: {bare: '>=1.16.0'} + peerDependencies: + bare-buffer: '*' + peerDependenciesMeta: + bare-buffer: + optional: true + + bare-os@3.6.2: + resolution: {integrity: sha512-T+V1+1srU2qYNBmJCXZkUY5vQ0B4FSlL3QDROnKQYOqeiQR8UbjNHlPa+TIbM4cuidiN9GaTaOZgSEgsvPbh5A==} + engines: {bare: '>=1.14.0'} + + bare-path@3.0.0: + resolution: {integrity: sha512-tyfW2cQcB5NN8Saijrhqn0Zh7AnFNsnczRcuWODH0eYAXBsJ5gVxAUuNr7tsHSC6IZ77cA0SitzT+s47kot8Mw==} + + bare-stream@2.7.0: + resolution: {integrity: sha512-oyXQNicV1y8nc2aKffH+BUHFRXmx6VrPzlnaEvMhram0nPBrKcEdcyBg5r08D0i8VxngHFAiVyn1QKXpSG0B8A==} + peerDependencies: + bare-buffer: '*' + bare-events: '*' + peerDependenciesMeta: + bare-buffer: + optional: true + bare-events: + optional: true + + bare-url@2.2.2: + resolution: {integrity: sha512-g+ueNGKkrjMazDG3elZO1pNs3HY5+mMmOet1jtKyhOaCnkLzitxf26z7hoAEkDNgdNmnc1KIlt/dw6Po6xZMpA==} + + base16@1.0.0: + resolution: {integrity: sha512-pNdYkNPiJUnEhnfXV56+sQy8+AaPcG3POZAUnwr4EeqCUZFz4u2PePbo3e5Gj4ziYPCWGUZT9RHisvJKnwFuBQ==} + + base64-js@1.5.1: + resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==} + + base64id@2.0.0: + resolution: {integrity: sha512-lGe34o6EHj9y3Kts9R4ZYs/Gr+6N7MCaMlIFA3F1R2O5/m7K06AxfSeO5530PEERE6/WyEg3lsuyw4GHlPZHog==} + engines: {node: ^4.5.0 || >= 5.9} + + baseline-browser-mapping@2.8.16: + resolution: {integrity: sha512-OMu3BGQ4E7P1ErFsIPpbJh0qvDudM/UuJeHgkAvfWe+0HFJCXh+t/l8L6fVLR55RI/UbKrVLnAXZSVwd9ysWYw==} + hasBin: true + + basic-auth@2.0.1: + resolution: {integrity: sha512-NF+epuEdnUYVlGuhaxbbq+dvJttwLnGY+YixlXlME5KpQ5W3CnXA5cVTneY3SPbPDRkcjMbifrwmFYcClgOZeg==} + engines: {node: '>= 0.8'} + + batch@0.6.1: + resolution: {integrity: sha512-x+VAiMRL6UPkx+kudNvxTl6hB2XNNCG2r+7wixVfIYwu/2HKRXimwQyaumLjMveWvT2Hkd/cAJw+QBMfJ/EKVw==} + + bcrypt-pbkdf@1.0.2: + resolution: {integrity: sha512-qeFIXtP4MSoi6NLqO12WfqARWWuCKi2Rn/9hJLEmtB5yTNr9DqFWkJRCf2qShWzPeAMRnOgCrq0sg/KLv5ES9w==} + + bfj@7.1.0: + resolution: {integrity: sha512-I6MMLkn+anzNdCUp9hMRyui1HaNEUCco50lxbvNS4+EyXg8lN3nJ48PjPWtbH8UVS9CuMoaKE9U2V3l29DaRQw==} + engines: {node: '>= 8.0.0'} + + big-integer@1.6.52: + resolution: {integrity: sha512-QxD8cf2eVqJOOz63z6JIN9BzvVs/dlySa5HGSBH5xtR8dPteIRQnBxxKqkNTiT6jbDTF6jAfrd4oMcND9RGbQg==} + engines: {node: '>=0.6'} + + big.js@5.2.2: + resolution: {integrity: sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ==} + + binary-extensions@2.3.0: + resolution: {integrity: sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==} + engines: {node: '>=8'} + + bindings@1.5.0: + resolution: {integrity: sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==} + + blob-util@2.0.2: + resolution: {integrity: sha512-T7JQa+zsXXEa6/8ZhHcQEW1UFfVM49Ts65uBkFL6fz2QmrElqmbajIDJvuA0tEhRe5eIjpV9ZF+0RfZR9voJFQ==} + + bluebird@3.7.2: + resolution: {integrity: sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==} + + body-parser@2.0.2: + resolution: {integrity: sha512-SNMk0OONlQ01uk8EPeiBvTW7W4ovpL5b1O3t1sjpPgfxOQ6BqQJ6XjxinDPR79Z6HdcD5zBBwr5ssiTlgdNztQ==} + engines: {node: '>=18'} + + bonjour-service@1.3.0: + resolution: {integrity: sha512-3YuAUiSkWykd+2Azjgyxei8OWf8thdn8AITIog2M4UICzoqfjlqr64WIjEXZllf/W6vK1goqleSR6brGomxQqA==} + + boolbase@1.0.0: + resolution: {integrity: sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==} + + bowser@2.12.1: + resolution: {integrity: sha512-z4rE2Gxh7tvshQ4hluIT7XcFrgLIQaw9X3A+kTTRdovCz5PMukm/0QC/BKSYPj3omF5Qfypn9O/c5kgpmvYUCw==} + + boxen@7.1.1: + resolution: {integrity: sha512-2hCgjEmP8YLWQ130n2FerGv7rYpfBmnmp9Uy2Le1vge6X3gZIfSmEzP5QTDElFxcvVcXlEn8Aq6MU/PZygIOog==} + engines: {node: '>=14.16'} + + bplist-parser@0.2.0: + resolution: {integrity: sha512-z0M+byMThzQmD9NILRniCUXYsYpjwnlO8N5uCFaCqIOpqRsJCrQL9NK3JsD67CN5a08nF5oIL2bD6loTdHOuKw==} + engines: {node: '>= 5.10.0'} + + brace-expansion@1.1.12: + resolution: {integrity: sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==} + + brace-expansion@2.0.2: + resolution: {integrity: sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==} + + braces@3.0.3: + resolution: {integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==} + engines: {node: '>=8'} + + browser-process-hrtime@1.0.0: + resolution: {integrity: sha512-9o5UecI3GhkpM6DrXr69PblIuWxPKk9Y0jHBRhdocZ2y7YECBFCsHm79Pr3OyR2AvjhDkabFJaDJMYRazHgsow==} + + browserslist@4.26.3: + resolution: {integrity: sha512-lAUU+02RFBuCKQPj/P6NgjlbCnLBMp4UtgTx7vNHd3XSIJF87s9a5rA3aH2yw3GS9DqZAUbOtZdCCiZeVRqt0w==} + engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} + hasBin: true + + bser@2.1.1: + resolution: {integrity: sha512-gQxTNE/GAfIIrmHLUE3oJyp5FO6HRBfhjnw4/wMmA63ZGDJnWBmgY/lyQBpnDUkGmAhbSe39tx2d/iTOAfglwQ==} + + bson@6.10.4: + resolution: {integrity: sha512-WIsKqkSC0ABoBJuT1LEX+2HEvNmNKKgnTAyd0fL8qzK4SH2i9NXg+t08YtdZp/V9IZ33cxe3iV4yM0qg8lMQng==} + engines: {node: '>=16.20.1'} + + buffer-crc32@0.2.13: + resolution: {integrity: sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ==} + + buffer-from@1.1.2: + resolution: {integrity: sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==} + + buffer@5.7.1: + resolution: {integrity: sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==} + + buffer@6.0.3: + resolution: {integrity: sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==} + + bufferutil@4.0.9: + resolution: {integrity: sha512-WDtdLmJvAuNNPzByAYpRo2rF1Mmradw6gvWsQKf63476DDXmomT9zUiGypLcG4ibIM67vhAj8jJRdbmEws2Aqw==} + engines: {node: '>=6.14.2'} + + builtin-modules@3.3.0: + resolution: {integrity: sha512-zhaCDicdLuWN5UbN5IMnFqNMhNfo919sH85y2/ea+5Yg9TsTkeZxpL+JLbp6cgYFS4sRLp3YV4S6yDuqVWHYOw==} + engines: {node: '>=6'} + + bundle-name@3.0.0: + resolution: {integrity: sha512-PKA4BeSvBpQKQ8iPOGCSiell+N8P+Tf1DlwqmYhpe2gAhKPHn8EYOxVT+ShuGmhg8lN8XiSlS80yiExKXrURlw==} + engines: {node: '>=12'} + + busboy@1.6.0: + resolution: {integrity: sha512-8SFQbg/0hQ9xy3UNTB0YEnsNBbWfhf7RtnzpL7TkBiTBRfrQ9Fxcnz7VJsleJpyp6rVLvXiuORqjlHi5q+PYuA==} + engines: {node: '>=10.16.0'} + + bytes@3.1.2: + resolution: {integrity: sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==} + engines: {node: '>= 0.8'} + + cacache@15.3.0: + resolution: {integrity: sha512-VVdYzXEn+cnbXpFgWs5hTT7OScegHVmLhJIR8Ufqk3iFD6A6j5iSX1KuBTfNEv4tdJWE2PzA6IVFtcLC7fN9wQ==} + engines: {node: '>= 10'} + + cachedir@2.4.0: + resolution: {integrity: sha512-9EtFOZR8g22CL7BWjJ9BUx1+A/djkofnyW3aOXZORNW2kxoUpx2h+uN2cOqwPmFhnpVmxg+KW2OjOSgChTEvsQ==} + engines: {node: '>=6'} + + call-bind-apply-helpers@1.0.2: + resolution: {integrity: sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==} + engines: {node: '>= 0.4'} + + call-bind@1.0.8: + resolution: {integrity: sha512-oKlSFMcMwpUg2ednkhQ454wfWiU/ul3CkJe/PEHcTKuiX6RpbehUiFMXu13HalGZxfUwCQzZG747YXBn1im9ww==} + engines: {node: '>= 0.4'} + + call-bound@1.0.4: + resolution: {integrity: sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==} + engines: {node: '>= 0.4'} + + callsites@3.1.0: + resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==} + engines: {node: '>=6'} + + camel-case@4.1.2: + resolution: {integrity: sha512-gxGWBrTT1JuMx6R+o5PTXMmUnhnVzLQ9SNutD4YqKtI6ap897t3tKECYla6gCWEkplXnlNybEkZg9GEGxKFCgw==} + + camelcase-css@2.0.1: + resolution: {integrity: sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA==} + engines: {node: '>= 6'} + + camelcase@5.3.1: + resolution: {integrity: sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==} + engines: {node: '>=6'} + + camelcase@6.3.0: + resolution: {integrity: sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==} + engines: {node: '>=10'} + + camelcase@7.0.1: + resolution: {integrity: sha512-xlx1yCK2Oc1APsPXDL2LdlNP6+uu8OCDdhOBSVT279M/S+y75O30C2VuD8T2ogdePBBl7PfPF4504tnLgX3zfw==} + engines: {node: '>=14.16'} + + camelize@1.0.1: + resolution: {integrity: sha512-dU+Tx2fsypxTgtLoE36npi3UqcjSSMNYfkqgmoEhtZrraP5VWq0K7FkWVTYa8eMPtnU/G2txVsfdCJTn9uzpuQ==} + + caniuse-api@3.0.0: + resolution: {integrity: sha512-bsTwuIg/BZZK/vreVTYYbSWoe2F+71P7K5QGEX+pT250DZbfU1MQ5prOKpPR+LL6uWKK3KMwMCAS74QB3Um1uw==} + + caniuse-lite@1.0.30001750: + resolution: {integrity: sha512-cuom0g5sdX6rw00qOoLNSFCJ9/mYIsuSOA+yzpDw8eopiFqcVwQvZHqov0vmEighRxX++cfC0Vg1G+1Iy/mSpQ==} + + canvas@2.11.2: + resolution: {integrity: sha512-ItanGBMrmRV7Py2Z+Xhs7cT+FNt5K0vPL4p9EZ/UX/Mu7hFbkxSjKF2KVtPwX7UYWp7dRKnrTvReflgrItJbdw==} + engines: {node: '>=6'} + + case-sensitive-paths-webpack-plugin@2.4.0: + resolution: {integrity: sha512-roIFONhcxog0JSSWbvVAh3OocukmSgpqOH6YpMkCvav/ySIV3JKg4Dc8vYtQjYi/UxpNE36r/9v+VqTQqgkYmw==} + engines: {node: '>=4'} + + caseless@0.12.0: + resolution: {integrity: sha512-4tYFyifaFfGacoiObjJegolkwSU4xQNGbVgUiNYVUxbQ2x2lUsFvY4hVgVzGiIe6WLOPqycWXA40l+PWsxthUw==} + + ccount@2.0.1: + resolution: {integrity: sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg==} + + chalk@2.4.2: + resolution: {integrity: sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==} + engines: {node: '>=4'} + + chalk@3.0.0: + resolution: {integrity: sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==} + engines: {node: '>=8'} + + chalk@4.1.2: + resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==} + engines: {node: '>=10'} + + chalk@5.3.0: + resolution: {integrity: sha512-dLitG79d+GV1Nb/VYcCDFivJeK1hiukt9QjRNVOsUtTy1rR1YJsmpGGTZ3qJos+uw7WmWF4wUwBd9jxjocFC2w==} + engines: {node: ^12.17.0 || ^14.13 || >=16.0.0} + + chalk@5.6.2: + resolution: {integrity: sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA==} + engines: {node: ^12.17.0 || ^14.13 || >=16.0.0} + + char-regex@1.0.2: + resolution: {integrity: sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw==} + engines: {node: '>=10'} + + char-regex@2.0.2: + resolution: {integrity: sha512-cbGOjAptfM2LVmWhwRFHEKTPkLwNddVmuqYZQt895yXwAsWsXObCG+YN4DGQ/JBtT4GP1a1lPPdio2z413LmTg==} + engines: {node: '>=12.20'} + + character-entities-legacy@1.1.4: + resolution: {integrity: sha512-3Xnr+7ZFS1uxeiUDvV02wQ+QDbc55o97tIV5zHScSPJpcLm/r0DFPcoY3tYRp+VZukxuMeKgXYmsXQHO05zQeA==} + + character-entities@1.2.4: + resolution: {integrity: sha512-iBMyeEHxfVnIakwOuDXpVkc54HijNgCyQB2w0VfGQThle6NXn50zU6V/u+LDhxHcDUPojn6Kpga3PTAD8W1bQw==} + + character-entities@2.0.2: + resolution: {integrity: sha512-shx7oQ0Awen/BRIdkjkvz54PnEEI/EjwXDSIZp86/KKdbafHh1Df/RYGBhn4hbe2+uKC9FnT5UCEdyPz3ai9hQ==} + + character-reference-invalid@1.1.4: + resolution: {integrity: sha512-mKKUkUbhPpQlCOfIuZkvSEgktjPFIsZKRRbC6KWVEMvlzblj3i3asQv5ODsrwt0N3pHAEvjP8KTQPHkp0+6jOg==} + + check-more-types@2.24.0: + resolution: {integrity: sha512-Pj779qHxV2tuapviy1bSZNEL1maXr13bPYpsvSDB68HlYcYuhlDrmGd63i0JHMCLKzc7rUSNIrpdJlhVlNwrxA==} + engines: {node: '>= 0.8.0'} + + check-types@11.2.3: + resolution: {integrity: sha512-+67P1GkJRaxQD6PKK0Et9DhwQB+vGg3PM5+aavopCpZT1lj9jeqfvpgTLAWErNj8qApkkmXlu/Ug74kmhagkXg==} + + chokidar@3.6.0: + resolution: {integrity: sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==} + engines: {node: '>= 8.10.0'} + + chokidar@4.0.3: + resolution: {integrity: sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==} + engines: {node: '>= 14.16.0'} + + chownr@2.0.0: + resolution: {integrity: sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==} + engines: {node: '>=10'} + + chrome-trace-event@1.0.4: + resolution: {integrity: sha512-rNjApaLzuwaOTjCiT8lSDdGN1APCiqkChLMJxJPWLunPAt5fy8xgU9/jNOchV84wfIxrA0lRQB7oCT8jrn/wrQ==} + engines: {node: '>=6.0'} + + ci-info@3.9.0: + resolution: {integrity: sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ==} + engines: {node: '>=8'} + + ci-info@4.3.1: + resolution: {integrity: sha512-Wdy2Igu8OcBpI2pZePZ5oWjPC38tmDVx5WKUXKwlLYkA0ozo85sLsLvkBbBn/sZaSCMFOGZJ14fvW9t5/d7kdA==} + engines: {node: '>=8'} + + cjs-module-lexer@1.4.3: + resolution: {integrity: sha512-9z8TZaGM1pfswYeXrUpzPrkx8UnWYdhJclsiYMm6x/w5+nN+8Tf/LnAgfLGQCm59qAOxU8WwHEq2vNwF6i4j+Q==} + + classcat@5.0.5: + resolution: {integrity: sha512-JhZUT7JFcQy/EzW605k/ktHtncoo9vnyW/2GspNYwFlN1C/WmjuV/xtS04e9SOkL2sTdw0VAZ2UGCcQ9lR6p6w==} + + classnames@2.5.1: + resolution: {integrity: sha512-saHYOzhIQs6wy2sVxTM6bUDsQO4F50V9RQ22qBpEdCW+I+/Wmke2HOl6lS6dTpdxVhb88/I6+Hs+438c3lfUow==} + + clean-css@5.3.3: + resolution: {integrity: sha512-D5J+kHaVb/wKSFcyyV75uCn8fiY4sV38XJoe4CUyGQ+mOU/fMVYUdH1hJC+CJQ5uY3EnW27SbJYS4X8BiLrAFg==} + engines: {node: '>= 10.0'} + + clean-stack@2.2.0: + resolution: {integrity: sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==} + engines: {node: '>=6'} + + cli-boxes@3.0.0: + resolution: {integrity: sha512-/lzGpEWL/8PfI0BmBOPRwp0c/wFNX1RdUML3jK/RcSBA9T8mZDdQpqYBKtCFTOfQbwPqWEOpjqW+Fnayc0969g==} + engines: {node: '>=10'} + + cli-cursor@3.1.0: + resolution: {integrity: sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==} + engines: {node: '>=8'} + + cli-cursor@4.0.0: + resolution: {integrity: sha512-VGtlMu3x/4DOtIUwEkRezxUZ2lBacNJCHash0N0WeZDBS+7Ux1dm3XWAgWYxLJFMMdOeXMHXorshEFhbMSGelg==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + + cli-table3@0.6.5: + resolution: {integrity: sha512-+W/5efTR7y5HRD7gACw9yQjqMVvEMLBHmboM/kPWam+H+Hmyrgjh6YncVKK122YZkXrLudzTuAukUw9FnMf7IQ==} + engines: {node: 10.* || >= 12.*} + + cli-truncate@2.1.0: + resolution: {integrity: sha512-n8fOixwDD6b/ObinzTrp1ZKFzbgvKZvuz/TvejnLn1aQfC6r52XEx85FmuC+3HI+JM7coBRXUvNqEU2PHVrHpg==} + engines: {node: '>=8'} + + cli-truncate@3.1.0: + resolution: {integrity: sha512-wfOBkjXteqSnI59oPcJkcPl/ZmwvMMOj340qUIY1SKZCv0B9Cf4D4fAucRkIKQmsIuYK3x1rrgU7MeGRruiuiA==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + + cliui@7.0.4: + resolution: {integrity: sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==} + + cliui@8.0.1: + resolution: {integrity: sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==} + engines: {node: '>=12'} + + clsx@1.2.1: + resolution: {integrity: sha512-EcR6r5a8bj6pu3ycsa/E/cKVGuTgZJZdsyUYHOksG/UHIiKfjxzRxYJpyVBwYaQeOvghal9fcc4PidlgzugAQg==} + engines: {node: '>=6'} + + clsx@2.1.1: + resolution: {integrity: sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==} + engines: {node: '>=6'} + + cluster-key-slot@1.1.2: + resolution: {integrity: sha512-RMr0FhtfXemyinomL4hrWcYJxmX6deFdCxpJzhDttxgO1+bcCnkk+9drydLVDmAMG7NE6aN/fl4F7ucU/90gAA==} + engines: {node: '>=0.10.0'} + + co@4.6.0: + resolution: {integrity: sha512-QVb0dM5HvG+uaxitm8wONl7jltx8dqhfU33DcqtOZcLSVIKSDDLDi7+0LbAKiyI8hD9u42m2YxXSkMGWThaecQ==} + engines: {iojs: '>= 1.0.0', node: '>= 0.12.0'} + + coa@2.0.2: + resolution: {integrity: sha512-q5/jG+YQnSy4nRTV4F7lPepBJZ8qBNJJDBuJdoejDyLXgmL7IEo+Le2JDZudFTFt7mrCqIRaSjws4ygRCTCAXA==} + engines: {node: '>= 4.0'} + + codemirror@6.0.2: + resolution: {integrity: sha512-VhydHotNW5w1UGK0Qj96BwSk/Zqbp9WbnyK2W/eVMv4QyF41INRGpjUhFJY7/uDNuudSc33a/PKr4iDqRduvHw==} + + collect-v8-coverage@1.0.2: + resolution: {integrity: sha512-lHl4d5/ONEbLlJvaJNtsF/Lz+WvB07u2ycqTYbdrq7UypDXailES4valYb2eWiJFxZlVmpGekfqoxQhzyFdT4Q==} + + color-convert@1.9.3: + resolution: {integrity: sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==} + + color-convert@2.0.1: + resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==} + engines: {node: '>=7.0.0'} + + color-convert@3.1.2: + resolution: {integrity: sha512-UNqkvCDXstVck3kdowtOTWROIJQwafjOfXSmddoDrXo4cewMKmusCeF22Q24zvjR8nwWib/3S/dfyzPItPEiJg==} + engines: {node: '>=14.6'} + + color-name@1.1.3: + resolution: {integrity: sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==} + + color-name@1.1.4: + resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==} + + color-name@2.0.2: + resolution: {integrity: sha512-9vEt7gE16EW7Eu7pvZnR0abW9z6ufzhXxGXZEVU9IqPdlsUiMwJeJfRtq0zePUmnbHGT9zajca7mX8zgoayo4A==} + engines: {node: '>=12.20'} + + color-string@2.1.2: + resolution: {integrity: sha512-RxmjYxbWemV9gKu4zPgiZagUxbH3RQpEIO77XoSSX0ivgABDZ+h8Zuash/EMFLTI4N9QgFPOJ6JQpPZKFxa+dA==} + engines: {node: '>=18'} + + color-support@1.1.3: + resolution: {integrity: sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg==} + hasBin: true + + color@5.0.2: + resolution: {integrity: sha512-e2hz5BzbUPcYlIRHo8ieAhYgoajrJr+hWoceg6E345TPsATMUKqDgzt8fSXZJJbxfpiPzkWyphz8yn8At7q3fA==} + engines: {node: '>=18'} + + colord@2.9.3: + resolution: {integrity: sha512-jeC1axXpnb0/2nn/Y1LPuLdgXBLH7aDcHu4KEKfqw3CUhX7ZpfBSlPKyqXE6btIgEzfWtrX3/tyBCaCvXvMkOw==} + + colorette@2.0.20: + resolution: {integrity: sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==} + + combined-stream@1.0.8: + resolution: {integrity: sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==} + engines: {node: '>= 0.8'} + + comma-separated-tokens@1.0.8: + resolution: {integrity: sha512-GHuDRO12Sypu2cV70d1dkA2EUmXHgntrzbpvOB+Qy+49ypNfGgFQIC2fhhXbnyrJRynDCAARsT7Ou0M6hirpfw==} + + comma-separated-tokens@2.0.3: + resolution: {integrity: sha512-Fu4hJdvzeylCfQPp9SGWidpzrMs7tTrlu6Vb8XGaRGck8QSNZJJp538Wrb60Lax4fPwR64ViY468OIUTbRlGZg==} + + commander@11.0.0: + resolution: {integrity: sha512-9HMlXtt/BNoYr8ooyjjNRdIilOTkVJXB+GhxMTtOKwk0R4j4lS4NpjuqmRxroBfnfTSHQIHQB7wryHhXarNjmQ==} + engines: {node: '>=16'} + + commander@13.1.0: + resolution: {integrity: sha512-/rFeCpNJQbhSZjGVwO9RFV3xPqbnERS8MmIQzCtD/zl6gpJuV/bMLuN92oG3F7d8oDEHHRrujSXNUr8fpjntKw==} + engines: {node: '>=18'} + + commander@2.20.3: + resolution: {integrity: sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==} + + commander@4.1.1: + resolution: {integrity: sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==} + engines: {node: '>= 6'} + + commander@6.2.1: + resolution: {integrity: sha512-U7VdrJFnJgo4xjrHpTzu0yrHPGImdsmD95ZlgYSEajAn2JKzDhDTPG9kBTefmObL2w/ngeZnilk+OV9CG3d7UA==} + engines: {node: '>= 6'} + + commander@7.2.0: + resolution: {integrity: sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==} + engines: {node: '>= 10'} + + commander@8.3.0: + resolution: {integrity: sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww==} + engines: {node: '>= 12'} + + commander@9.5.0: + resolution: {integrity: sha512-KRs7WVDKg86PWiuAqhDrAQnTXZKraVcCc6vFdL14qrZ/DcWwuRo7VoiYXalXO7S5GKpqYiVEwCbgFDfxNHKJBQ==} + engines: {node: ^12.20.0 || >=14} + + common-tags@1.8.2: + resolution: {integrity: sha512-gk/Z852D2Wtb//0I+kRFNKKE9dIIVirjoqPoA1wJU+XePVXZfGeBpk45+A1rKO4Q43prqWBNY/MiIeRLbPWUaA==} + engines: {node: '>=4.0.0'} + + commondir@1.0.1: + resolution: {integrity: sha512-W9pAhw0ja1Edb5GVdIF1mjZw/ASI0AlShXM83UUGe2DVr5TdAPEA1OA8m/g8zWp9x6On7gqufY+FatDbC3MDQg==} + + component-register@0.8.8: + resolution: {integrity: sha512-djhwcxjY+X9dacaYUEOkOm7tda8uOEDiMDigWysu3xv54M8o6XDlsjR1qt5Y8QLGiKg51fqXFIR2HUTmt9ys0Q==} + + compress-commons@5.0.3: + resolution: {integrity: sha512-/UIcLWvwAQyVibgpQDPtfNM3SvqN7G9elAPAV7GM0L53EbNWwWiCsWtK8Fwed/APEbptPHXs5PuW+y8Bq8lFTA==} + engines: {node: '>= 12.0.0'} + + compressible@2.0.18: + resolution: {integrity: sha512-AF3r7P5dWxL8MxyITRMlORQNaOA2IkAFaTr4k7BUumjPtRpGDTZpl0Pb1XCO6JeDCBdp126Cgs9sMxqSjgYyRg==} + engines: {node: '>= 0.6'} + + compression@1.8.1: + resolution: {integrity: sha512-9mAqGPHLakhCLeNyxPkK4xVo746zQ/czLH1Ky+vkitMnWfWZps8r0qXuwhwizagCRttsL4lfG4pIOvaWLpAP0w==} + engines: {node: '>= 0.8.0'} + + concat-map@0.0.1: + resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==} + + concat-stream@1.6.2: + resolution: {integrity: sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==} + engines: {'0': node >= 0.8} + + concurrently@7.6.0: + resolution: {integrity: sha512-BKtRgvcJGeZ4XttiDiNcFiRlxoAeZOseqUvyYRUp/Vtd+9p1ULmeoSqGsDA+2ivdeDFpqrJvGvmI+StKfKl5hw==} + engines: {node: ^12.20.0 || ^14.13.0 || >=16.0.0} + hasBin: true + + confusing-browser-globals@1.0.11: + resolution: {integrity: sha512-JsPKdmh8ZkmnHxDk55FZ1TqVLvEQTvoByJZRN9jzI0UjxK/QgAmsphz7PGtqgPieQZ/CQcHWXCR7ATDNhGe+YA==} + + connect-history-api-fallback@2.0.0: + resolution: {integrity: sha512-U73+6lQFmfiNPrYbXqr6kZ1i1wiRqXnp2nhMsINseWXO8lDau0LGEffJ8kQi4EjLZympVgRdvqjAgiZ1tgzDDA==} + engines: {node: '>=0.8'} + + console-control-strings@1.1.0: + resolution: {integrity: sha512-ty/fTekppD2fIwRvnZAVdeOiGd1c7YXEixbgJTNzqcxJWKQnjJ/V1bNEEE6hygpM3WjwHFUVK6HTjWSzV4a8sQ==} + + content-disposition@0.5.4: + resolution: {integrity: sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==} + engines: {node: '>= 0.6'} + + content-type@1.0.5: + resolution: {integrity: sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==} + engines: {node: '>= 0.6'} + + convert-source-map@1.9.0: + resolution: {integrity: sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==} + + convert-source-map@2.0.0: + resolution: {integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==} + + cookie-signature@1.0.6: + resolution: {integrity: sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==} + + cookie@0.7.1: + resolution: {integrity: sha512-6DnInpx7SJ2AK3+CTUE/ZM0vWTUboZCegxhC2xiIydHR9jNuTAASBrfEpHhiGOZw/nX51bHt6YQl8jsGo4y/0w==} + engines: {node: '>= 0.6'} + + cookie@0.7.2: + resolution: {integrity: sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==} + engines: {node: '>= 0.6'} + + core-js-compat@3.46.0: + resolution: {integrity: sha512-p9hObIIEENxSV8xIu+V68JjSeARg6UVMG5mR+JEUguG3sI6MsiS1njz2jHmyJDvA+8jX/sytkBHup6kxhM9law==} + + core-js-pure@3.46.0: + resolution: {integrity: sha512-NMCW30bHNofuhwLhYPt66OLOKTMbOhgTTatKVbaQC3KRHpTCiRIBYvtshr+NBYSnBxwAFhjW/RfJ0XbIjS16rw==} + + core-js@3.46.0: + resolution: {integrity: sha512-vDMm9B0xnqqZ8uSBpZ8sNtRtOdmfShrvT6h2TuQGLs0Is+cR0DYbj/KWP6ALVNbWPpqA/qPLoOuppJN07humpA==} + + core-util-is@1.0.2: + resolution: {integrity: sha512-3lqz5YjWTYnW6dlDa5TLaTCcShfar1e40rmcJVwCBJC6mWlFuj0eCHIElmG1g5kyuJ/GD+8Wn4FFCcz4gJPfaQ==} + + core-util-is@1.0.3: + resolution: {integrity: sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==} + + cors@2.8.5: + resolution: {integrity: sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==} + engines: {node: '>= 0.10'} + + cosmiconfig@6.0.0: + resolution: {integrity: sha512-xb3ZL6+L8b9JLLCx3ZdoZy4+2ECphCMo2PwqgP1tlfVq6M6YReyzBJtvWWtbDSpNr9hn96pkCiZqUcFEc+54Qg==} + engines: {node: '>=8'} + + cosmiconfig@7.1.0: + resolution: {integrity: sha512-AdmX6xUzdNASswsFtmwSt7Vj8po9IuqXm0UXz7QKPuEUmPB4XyjGfaAr2PSuELMwkRMVH1EpIkX5bTZGRB3eCA==} + engines: {node: '>=10'} + + crc-32@1.2.2: + resolution: {integrity: sha512-ROmzCKrTnOwybPcJApAA6WBWij23HVfGVNKqqrZpuyZOHqK2CwHSvpGuyt/UNNvaIjEd8X5IFGp4Mh+Ie1IHJQ==} + engines: {node: '>=0.8'} + hasBin: true + + crc32-stream@5.0.1: + resolution: {integrity: sha512-lO1dFui+CEUh/ztYIpgpKItKW9Bb4NWakCRJrnqAbFIYD+OZAwb2VfD5T5eXMw2FNcsDHkQcNl/Wh3iVXYwU6g==} + engines: {node: '>= 12.0.0'} + + create-require@1.1.1: + resolution: {integrity: sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==} + + crelt@1.0.6: + resolution: {integrity: sha512-VQ2MBenTq1fWZUH9DJNGti7kKv6EeAuYr3cLwxUWhIu1baTaXh4Ib5W2CqHVqib4/MqbYGJqiL3Zb8GJZr3l4g==} + + cross-env@7.0.3: + resolution: {integrity: sha512-+/HKd6EgcQCJGh2PSjZuUitQBQynKor4wrFbRg4DtAgS1aWO+gU52xpH7M9ScGgXSYmAVS9bIJ8EzuaGw0oNAw==} + engines: {node: '>=10.14', npm: '>=6', yarn: '>=1'} + hasBin: true + + cross-fetch@3.2.0: + resolution: {integrity: sha512-Q+xVJLoGOeIMXZmbUK4HYk+69cQH6LudR0Vu/pRm2YlU/hDV9CiS0gKUMaWY5f2NeUH9C1nV3bsTlCo0FsTV1Q==} + + cross-spawn-async@2.2.5: + resolution: {integrity: sha512-snteb3aVrxYYOX9e8BabYFK9WhCDhTlw1YQktfTthBogxri4/2r9U2nQc0ffY73ZAxezDc+U8gvHAeU1wy1ubQ==} + deprecated: cross-spawn no longer requires a build toolchain, use it instead + + cross-spawn@7.0.6: + resolution: {integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==} + engines: {node: '>= 8'} + + crypto-js@4.2.0: + resolution: {integrity: sha512-KALDyEYgpY+Rlob/iriUtjV6d5Eq+Y191A5g4UqLAi8CyGP9N1+FdVbkc1SxKc2r4YAYqG8JzO2KGL+AizD70Q==} + + crypto-random-string@2.0.0: + resolution: {integrity: sha512-v1plID3y9r/lPhviJ1wrXpLeyUIGAZ2SHNYTEapm7/8A9nLPoyvVp3RK/EPFqn5kEznyWgYZNsRtYYIWbuG8KA==} + engines: {node: '>=8'} + + css-blank-pseudo@3.0.3: + resolution: {integrity: sha512-VS90XWtsHGqoM0t4KpH053c4ehxZ2E6HtGI7x68YFV0pTo/QmkV/YFA+NnlvK8guxZVNWGQhVNJGC39Q8XF4OQ==} + engines: {node: ^12 || ^14 || >=16} + hasBin: true + peerDependencies: + postcss: ^8.4 + + css-color-keywords@1.0.0: + resolution: {integrity: sha512-FyyrDHZKEjXDpNJYvVsV960FiqQyXc/LlYmsxl2BcdMb2WPx0OGRVgTg55rPSyLSNMqP52R9r8geSp7apN3Ofg==} + engines: {node: '>=4'} + + css-declaration-sorter@6.4.1: + resolution: {integrity: sha512-rtdthzxKuyq6IzqX6jEcIzQF/YqccluefyCYheovBOLhFT/drQA9zj/UbRAa9J7C0o6EG6u3E6g+vKkay7/k3g==} + engines: {node: ^10 || ^12 || >=14} + peerDependencies: + postcss: ^8.0.9 + + css-has-pseudo@3.0.4: + resolution: {integrity: sha512-Vse0xpR1K9MNlp2j5w1pgWIJtm1a8qS0JwS9goFYcImjlHEmywP9VUF05aGBXzGpDJF86QXk4L0ypBmwPhGArw==} + engines: {node: ^12 || ^14 || >=16} + hasBin: true + peerDependencies: + postcss: ^8.4 + + css-loader@6.11.0: + resolution: {integrity: sha512-CTJ+AEQJjq5NzLga5pE39qdiSV56F8ywCIsqNIRF0r7BDgWsN25aazToqAFg7ZrtA/U016xudB3ffgweORxX7g==} + engines: {node: '>= 12.13.0'} + peerDependencies: + '@rspack/core': 0.x || 1.x + webpack: ^5.0.0 + peerDependenciesMeta: + '@rspack/core': + optional: true + webpack: + optional: true + + css-minimizer-webpack-plugin@3.4.1: + resolution: {integrity: sha512-1u6D71zeIfgngN2XNRJefc/hY7Ybsxd74Jm4qngIXyUEk7fss3VUzuHxLAq/R8NAba4QU9OUSaMZlbpRc7bM4Q==} + engines: {node: '>= 12.13.0'} + peerDependencies: + '@parcel/css': '*' + clean-css: '*' + csso: '*' + esbuild: '*' + webpack: ^5.0.0 + peerDependenciesMeta: + '@parcel/css': + optional: true + clean-css: + optional: true + csso: + optional: true + esbuild: + optional: true + + css-prefers-color-scheme@6.0.3: + resolution: {integrity: sha512-4BqMbZksRkJQx2zAjrokiGMd07RqOa2IxIrrN10lyBe9xhn9DEvjUK79J6jkeiv9D9hQFXKb6g1jwU62jziJZA==} + engines: {node: ^12 || ^14 || >=16} + hasBin: true + peerDependencies: + postcss: ^8.4 + + css-select-base-adapter@0.1.1: + resolution: {integrity: sha512-jQVeeRG70QI08vSTwf1jHxp74JoZsr2XSgETae8/xC8ovSnL2WF87GTLO86Sbwdt2lK4Umg4HnnwMO4YF3Ce7w==} + + css-select@2.1.0: + resolution: {integrity: sha512-Dqk7LQKpwLoH3VovzZnkzegqNSuAziQyNZUcrdDM401iY+R5NkGBXGmtO05/yaXQziALuPogeG0b7UAgjnTJTQ==} + + css-select@4.3.0: + resolution: {integrity: sha512-wPpOYtnsVontu2mODhA19JrqWxNsfdatRKd64kmpRbQgh1KtItko5sTnEpPdpSaJszTOhEMlF/RPz28qj4HqhQ==} + + css-to-react-native@3.2.0: + resolution: {integrity: sha512-e8RKaLXMOFii+02mOlqwjbD00KSEKqblnpO9e++1aXS1fPQOpS1YoqdVHBqPjHNoxeF2mimzVqawm2KCbEdtHQ==} + + css-tree@1.0.0-alpha.37: + resolution: {integrity: sha512-DMxWJg0rnz7UgxKT0Q1HU/L9BeJI0M6ksor0OgqOnF+aRCDWg/N2641HmVyU9KVIu0OVVWOb2IpC9A+BJRnejg==} + engines: {node: '>=8.0.0'} + + css-tree@1.1.3: + resolution: {integrity: sha512-tRpdppF7TRazZrjJ6v3stzv93qxRcSsFmW6cX0Zm2NVKpxE1WV1HblnghVv9TreireHkqI/VDEsfolRF1p6y7Q==} + engines: {node: '>=8.0.0'} + + css-what@3.4.2: + resolution: {integrity: sha512-ACUm3L0/jiZTqfzRM3Hi9Q8eZqd6IK37mMWPLz9PJxkLWllYeRf+EHUSHYEtFop2Eqytaq1FizFVh7XfBnXCDQ==} + engines: {node: '>= 6'} + + css-what@6.2.2: + resolution: {integrity: sha512-u/O3vwbptzhMs3L1fQE82ZSLHQQfto5gyZzwteVIEyeaY5Fc7R4dapF/BvRoSYFeqfBk4m0V1Vafq5Pjv25wvA==} + engines: {node: '>= 6'} + + css.escape@1.5.1: + resolution: {integrity: sha512-YUifsXXuknHlUsmlgyY0PKzgPOr7/FjCePfHNt0jxm83wHZi44VDMQ7/fGNkjY3/jV1MC+1CmZbaHzugyeRtpg==} + + cssdb@7.11.2: + resolution: {integrity: sha512-lhQ32TFkc1X4eTefGfYPvgovRSzIMofHkigfH8nWtyRL4XJLsRhJFreRvEgKzept7x1rjBuy3J/MurXLaFxW/A==} + + cssesc@3.0.0: + resolution: {integrity: sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==} + engines: {node: '>=4'} + hasBin: true + + cssnano-preset-default@5.2.14: + resolution: {integrity: sha512-t0SFesj/ZV2OTylqQVOrFgEh5uanxbO6ZAdeCrNsUQ6fVuXwYTxJPNAGvGTxHbD68ldIJNec7PyYZDBrfDQ+6A==} + engines: {node: ^10 || ^12 || >=14.0} + peerDependencies: + postcss: ^8.2.15 + + cssnano-utils@3.1.0: + resolution: {integrity: sha512-JQNR19/YZhz4psLX/rQ9M83e3z2Wf/HdJbryzte4a3NSuafyp9w/I4U+hx5C2S9g41qlstH7DEWnZaaj83OuEA==} + engines: {node: ^10 || ^12 || >=14.0} + peerDependencies: + postcss: ^8.2.15 + + cssnano@5.1.15: + resolution: {integrity: sha512-j+BKgDcLDQA+eDifLx0EO4XSA56b7uut3BQFH+wbSaSTuGLuiyTa/wbRYthUXX8LC9mLg+WWKe8h+qJuwTAbHw==} + engines: {node: ^10 || ^12 || >=14.0} + peerDependencies: + postcss: ^8.2.15 + + csso@4.2.0: + resolution: {integrity: sha512-wvlcdIbf6pwKEk7vHj8/Bkc0B4ylXZruLvOgs9doS5eOsOpuodOV2zJChSpkp+pRpYQLQMeF04nr3Z68Sta9jA==} + engines: {node: '>=8.0.0'} + + cssom@0.3.8: + resolution: {integrity: sha512-b0tGHbfegbhPJpxpiBPU2sCkigAqtM9O121le6bbOlgyV+NyGyCmVfJ6QW9eRjz8CpNfWEOYBIMIGRYkLwsIYg==} + + cssom@0.4.4: + resolution: {integrity: sha512-p3pvU7r1MyyqbTk+WbNJIgJjG2VmTIaB10rI93LzVPrmDJKkzKYMtxxyAvQXR/NS6otuzveI7+7BBq3SjBS2mw==} + + cssom@0.5.0: + resolution: {integrity: sha512-iKuQcq+NdHqlAcwUY0o/HL69XQrUaQdMjmStJ8JFmUaiiQErlhrmuigkg/CU4E2J0IyUKUrMAgl36TvN67MqTw==} + + cssstyle@2.3.0: + resolution: {integrity: sha512-AZL67abkUzIuvcHqk7c09cezpGNcxUxU4Ioi/05xHk4DQeTkWmGYftIE6ctU6AEt+Gn4n1lDStOtj7FKycP71A==} + engines: {node: '>=8'} + + csstype@3.1.3: + resolution: {integrity: sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==} + + csstype@3.2.3: + resolution: {integrity: sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==} + + cypress@13.17.0: + resolution: {integrity: sha512-5xWkaPurwkIljojFidhw8lFScyxhtiFHl/i/3zov+1Z5CmY4t9tjIdvSXfu82Y3w7wt0uR9KkucbhkVvJZLQSA==} + engines: {node: ^16.0.0 || ^18.0.0 || >=20.0.0} + hasBin: true + + d3-array@3.2.4: + resolution: {integrity: sha512-tdQAmyA18i4J7wprpYq8ClcxZy3SC31QMeByyCFyRt7BVHdREQZ5lpzoe5mFEYZUWe+oq8HBvk9JjpibyEV4Jg==} + engines: {node: '>=12'} + + d3-color@3.1.0: + resolution: {integrity: sha512-zg/chbXyeBtMQ1LbD/WSoW2DpC3I0mpmPdW+ynRTj/x2DAWYrIY7qeZIHidozwV24m4iavr15lNwIwLxRmOxhA==} + engines: {node: '>=12'} + + d3-dispatch@3.0.1: + resolution: {integrity: sha512-rzUyPU/S7rwUflMyLc1ETDeBj0NRuHKKAcvukozwhshr6g6c5d8zh4c2gQjY2bZ0dXeGLWc1PF174P2tVvKhfg==} + engines: {node: '>=12'} + + d3-drag@3.0.0: + resolution: {integrity: sha512-pWbUJLdETVA8lQNJecMxoXfH6x+mO2UQo8rSmZ+QqxcbyA3hfeprFgIT//HW2nlHChWeIIMwS2Fq+gEARkhTkg==} + engines: {node: '>=12'} + + d3-ease@3.0.1: + resolution: {integrity: sha512-wR/XK3D3XcLIZwpbvQwQ5fK+8Ykds1ip7A2Txe0yxncXSdq1L9skcG7blcedkOX+ZcgxGAmLX1FrRGbADwzi0w==} + engines: {node: '>=12'} + + d3-format@3.1.2: + resolution: {integrity: sha512-AJDdYOdnyRDV5b6ArilzCPPwc1ejkHcoyFarqlPqT7zRYjhavcT3uSrqcMvsgh2CgoPbK3RCwyHaVyxYcP2Arg==} + engines: {node: '>=12'} + + d3-interpolate@3.0.1: + resolution: {integrity: sha512-3bYs1rOD33uo8aqJfKP3JWPAibgw8Zm2+L9vBKEHJ2Rg+viTR7o5Mmv5mZcieN+FRYaAOWX5SJATX6k1PWz72g==} + engines: {node: '>=12'} + + d3-path@3.1.0: + resolution: {integrity: sha512-p3KP5HCf/bvjBSSKuXid6Zqijx7wIfNW+J/maPs+iwR35at5JCbLUT0LzF1cnjbCHWhqzQTIN2Jpe8pRebIEFQ==} + engines: {node: '>=12'} + + d3-scale@4.0.2: + resolution: {integrity: sha512-GZW464g1SH7ag3Y7hXjf8RoUuAFIqklOAq3MRl4OaWabTFJY9PN/E1YklhXLh+OQ3fM9yS2nOkCoS+WLZ6kvxQ==} + engines: {node: '>=12'} + + d3-selection@3.0.0: + resolution: {integrity: sha512-fmTRWbNMmsmWq6xJV8D19U/gw/bwrHfNXxrIN+HfZgnzqTHp9jOmKMhsTUjXOJnZOdZY9Q28y4yebKzqDKlxlQ==} + engines: {node: '>=12'} + + d3-shape@3.2.0: + resolution: {integrity: sha512-SaLBuwGm3MOViRq2ABk3eLoxwZELpH6zhl3FbAoJ7Vm1gofKx6El1Ib5z23NUEhF9AsGl7y+dzLe5Cw2AArGTA==} + engines: {node: '>=12'} + + d3-time-format@4.1.0: + resolution: {integrity: sha512-dJxPBlzC7NugB2PDLwo9Q8JiTR3M3e4/XANkreKSUxF8vvXKqm1Yfq4Q5dl8budlunRVlUUaDUgFt7eA8D6NLg==} + engines: {node: '>=12'} + + d3-time@3.1.0: + resolution: {integrity: sha512-VqKjzBLejbSMT4IgbmVgDjpkYrNWUYJnbCGo874u7MMKIWsILRX+OpX/gTk8MqjpT1A/c6HY2dCA77ZN0lkQ2Q==} + engines: {node: '>=12'} + + d3-timer@3.0.1: + resolution: {integrity: sha512-ndfJ/JxxMd3nw31uyKoY2naivF+r29V+Lc0svZxe1JvvIRmi8hUsrMvdOwgS1o6uBHmiz91geQ0ylPP0aj1VUA==} + engines: {node: '>=12'} + + d3-transition@3.0.1: + resolution: {integrity: sha512-ApKvfjsSR6tg06xrL434C0WydLr7JewBB3V+/39RMHsaXTOG0zmt/OAXeng5M5LBm0ojmxJrpomQVZ1aPvBL4w==} + engines: {node: '>=12'} + peerDependencies: + d3-selection: 2 - 3 + + d3-zoom@3.0.0: + resolution: {integrity: sha512-b8AmV3kfQaqWAuacbPuNbL6vahnOJflOhexLzMMNLga62+/nh0JzvJ0aO/5a5MVgUFGS7Hu1P9P03o3fJkDCyw==} + engines: {node: '>=12'} + + damerau-levenshtein@1.0.8: + resolution: {integrity: sha512-sdQSFB7+llfUcQHUQO3+B8ERRj0Oa4w9POWMI/puGtuf7gFywGmkaLCElnudfTiKZV+NvHqL0ifzdrI8Ro7ESA==} + + dashdash@1.14.1: + resolution: {integrity: sha512-jRFi8UDGo6j+odZiEpjazZaWqEal3w/basFjQHQEwVtZJGDpxbH1MeYluwCS8Xq5wmLJooDlMgvVarmWfGM44g==} + engines: {node: '>=0.10'} + + data-uri-to-buffer@4.0.1: + resolution: {integrity: sha512-0R9ikRb668HB7QDxT1vkpuUBtqc53YyAwMwGeUFKRojY/NWKvdZ+9UYtRfGmhqNbRkTSVpMbmyhXipFFv2cb/A==} + engines: {node: '>= 12'} + + data-urls@2.0.0: + resolution: {integrity: sha512-X5eWTSXO/BJmpdIKCRuKUgSCgAN0OwliVK3yPKbwIWU1Tdw5BRajxlzMidvh+gwko9AfQ9zIj52pzF91Q3YAvQ==} + engines: {node: '>=10'} + + data-urls@3.0.2: + resolution: {integrity: sha512-Jy/tj3ldjZJo63sVAvg6LHt2mHvl4V6AgRAmNDtLdm7faqtsx+aJG42rsyCo9JCoRVKwPFzKlIPx3DIibwSIaQ==} + engines: {node: '>=12'} + + data-view-buffer@1.0.2: + resolution: {integrity: sha512-EmKO5V3OLXh1rtK2wgXRansaK1/mtVdTUEiEI0W8RkvgT05kfxaH29PliLnpLP73yYO6142Q72QNa8Wx/A5CqQ==} + engines: {node: '>= 0.4'} + + data-view-byte-length@1.0.2: + resolution: {integrity: sha512-tuhGbE6CfTM9+5ANGf+oQb72Ky/0+s3xKUpHvShfiz2RxMFgFPjsXuRLBVMtvMs15awe45SRb83D6wH4ew6wlQ==} + engines: {node: '>= 0.4'} + + data-view-byte-offset@1.0.1: + resolution: {integrity: sha512-BS8PfmtDGnrgYdOonGZQdLZslWIeCGFP9tpan0hi1Co2Zr2NKADsvGYA8XxuG/4UWgJ6Cjtv+YJnB6MM69QGlQ==} + engines: {node: '>= 0.4'} + + date-fns@2.30.0: + resolution: {integrity: sha512-fnULvOpxnC5/Vg3NCiWelDsLiUc9bRwAPs/+LfTLNvetFCtCTN+yQz15C/fs4AwX1R9K5GLtLfn8QW+dWisaAw==} + engines: {node: '>=0.11'} + + dayjs@1.11.18: + resolution: {integrity: sha512-zFBQ7WFRvVRhKcWoUh+ZA1g2HVgUbsZm9sbddh8EC5iv93sui8DVVz1Npvz+r6meo9VKfa8NyLWBsQK1VvIKPA==} + + debug@2.6.9: + resolution: {integrity: sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==} + peerDependencies: + supports-color: '*' + peerDependenciesMeta: + supports-color: + optional: true + + debug@3.1.0: + resolution: {integrity: sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==} + peerDependencies: + supports-color: '*' + peerDependenciesMeta: + supports-color: + optional: true + + debug@3.2.7: + resolution: {integrity: sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==} + peerDependencies: + supports-color: '*' + peerDependenciesMeta: + supports-color: + optional: true + + debug@4.3.4: + resolution: {integrity: sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==} + engines: {node: '>=6.0'} + peerDependencies: + supports-color: '*' + peerDependenciesMeta: + supports-color: + optional: true + + debug@4.3.7: + resolution: {integrity: sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==} + engines: {node: '>=6.0'} + peerDependencies: + supports-color: '*' + peerDependenciesMeta: + supports-color: + optional: true + + debug@4.4.3: + resolution: {integrity: sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==} + engines: {node: '>=6.0'} + peerDependencies: + supports-color: '*' + peerDependenciesMeta: + supports-color: + optional: true + + decimal.js-light@2.5.1: + resolution: {integrity: sha512-qIMFpTMZmny+MMIitAB6D7iVPEorVw6YQRWkvarTkT4tBeSLLiHzcwj6q0MmYSFCiVpiqPJTJEYIrpcPzVEIvg==} + + decimal.js@10.6.0: + resolution: {integrity: sha512-YpgQiITW3JXGntzdUmyUR1V812Hn8T1YVXhCu+wO3OpS4eU9l4YdD3qjyiKdV6mvV29zapkMeD390UVEf2lkUg==} + + decode-named-character-reference@1.2.0: + resolution: {integrity: sha512-c6fcElNV6ShtZXmsgNgFFV5tVX2PaV4g+MOAkb8eXHvn6sryJBrZa9r0zV6+dtTyoCKxtDy5tyQ5ZwQuidtd+Q==} + + decode-uri-component@0.4.1: + resolution: {integrity: sha512-+8VxcR21HhTy8nOt6jf20w0c9CADrw1O8d+VZ/YzzCt4bJ3uBjw+D1q2osAB8RnpwwaeYBxy0HyKQxD5JBMuuQ==} + engines: {node: '>=14.16'} + + decompress-response@4.2.1: + resolution: {integrity: sha512-jOSne2qbyE+/r8G1VU+G/82LBs2Fs4LAsTiLSHOCOMZQl2OKZ6i8i4IyHemTe+/yIXOtTcRQMzPcgyhoFlqPkw==} + engines: {node: '>=8'} + + decompress-response@6.0.0: + resolution: {integrity: sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==} + engines: {node: '>=10'} + + dedent@0.7.0: + resolution: {integrity: sha512-Q6fKUPqnAHAyhiUgFU7BUzLiv0kd8saH9al7tnu5Q/okj6dnupxyTgFIBjVzJATdfIAm9NAsvXNzjaKa+bxVyA==} + + dedent@1.7.0: + resolution: {integrity: sha512-HGFtf8yhuhGhqO07SV79tRp+br4MnbdjeVxotpn1QBl30pcLLCQjX5b2295ll0fv8RKDKsmWYrl05usHM9CewQ==} + peerDependencies: + babel-plugin-macros: ^3.1.0 + peerDependenciesMeta: + babel-plugin-macros: + optional: true + + deep-equal@2.2.3: + resolution: {integrity: sha512-ZIwpnevOurS8bpT4192sqAowWM76JDKSHYzMLty3BZGSswgq6pBaH3DhCSW5xVAZICZyKdOBPjwww5wfgT/6PA==} + engines: {node: '>= 0.4'} + + deep-extend@0.6.0: + resolution: {integrity: sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==} + engines: {node: '>=4.0.0'} + + deep-is@0.1.4: + resolution: {integrity: sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==} + + deepmerge@2.2.1: + resolution: {integrity: sha512-R9hc1Xa/NOBi9WRVUWg19rl1UB7Tt4kuPd+thNJgFZoxXsTz7ncaPaeIm+40oSGuP33DfMb4sZt1QIGiJzC4EA==} + engines: {node: '>=0.10.0'} + + deepmerge@4.3.1: + resolution: {integrity: sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==} + engines: {node: '>=0.10.0'} + + default-browser-id@3.0.0: + resolution: {integrity: sha512-OZ1y3y0SqSICtE8DE4S8YOE9UZOJ8wO16fKWVP5J1Qz42kV9jcnMVFrEE/noXb/ss3Q4pZIH79kxofzyNNtUNA==} + engines: {node: '>=12'} + + default-browser@3.1.0: + resolution: {integrity: sha512-SOHecvSoairSAWxEHP/0qcsld/KtI3DargfEuELQDyHIYmS2EMgdGhHOTC1GxaYr+NLUV6kDroeiSBfnNHnn8w==} + engines: {node: '>=12'} + + default-gateway@6.0.3: + resolution: {integrity: sha512-fwSOJsbbNzZ/CUFpqFBqYfYNLj1NbMPm8MMCIzHjC83iSJRBEGmDUxU+WP661BaBQImeC2yHwXtz+P/O9o+XEg==} + engines: {node: '>= 10'} + + define-data-property@1.1.4: + resolution: {integrity: sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==} + engines: {node: '>= 0.4'} + + define-lazy-prop@2.0.0: + resolution: {integrity: sha512-Ds09qNh8yw3khSjiJjiUInaGX9xlqZDY7JVryGxdxV7NPeuqQfplOpQ66yJFZut3jLa5zOwkXw1g9EI2uKh4Og==} + engines: {node: '>=8'} + + define-properties@1.2.1: + resolution: {integrity: sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==} + engines: {node: '>= 0.4'} + + delayed-stream@1.0.0: + resolution: {integrity: sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==} + engines: {node: '>=0.4.0'} + + delegates@1.0.0: + resolution: {integrity: sha512-bd2L678uiWATM6m5Z1VzNCErI3jiGzt6HGY8OVICs40JQq/HALfbyNJmp0UDakEY4pMMaN0Ly5om/B1VI/+xfQ==} + + denque@2.1.0: + resolution: {integrity: sha512-HVQE3AAb/pxF8fQAoiqpvg9i3evqug3hoiwakOyZAwJm+6vZehbkYXZ0l4JxS+I3QxM97v5aaRNhj8v5oBhekw==} + engines: {node: '>=0.10'} + + depd@1.1.2: + resolution: {integrity: sha512-7emPTl6Dpo6JRXOXjLRxck+FlLRX5847cLKEn00PLAgc3g2hTZZgr+e4c2v6QpSmLeFP3n5yUo7ft6avBK/5jQ==} + engines: {node: '>= 0.6'} + + depd@2.0.0: + resolution: {integrity: sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==} + engines: {node: '>= 0.8'} + + dequal@2.0.3: + resolution: {integrity: sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==} + engines: {node: '>=6'} + + destroy@1.2.0: + resolution: {integrity: sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==} + engines: {node: '>= 0.8', npm: 1.2.8000 || >= 1.4.16} + + detect-libc@1.0.3: + resolution: {integrity: sha512-pGjwhsmsp4kL2RTz08wcOlGN83otlqHeD/Z5T8GXZB+/YcpQ/dgo+lbU8ZsGxV0HIvqqxo9l7mqYwyYMD9bKDg==} + engines: {node: '>=0.10'} + hasBin: true + + detect-libc@2.1.2: + resolution: {integrity: sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==} + engines: {node: '>=8'} + + detect-newline@3.1.0: + resolution: {integrity: sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA==} + engines: {node: '>=8'} + + detect-node@2.1.0: + resolution: {integrity: sha512-T0NIuQpnTvFDATNuHN5roPwSBG83rFsuO+MXXH9/3N1eFbn4wcPjttvjMLEPWJ0RGUYgQE7cGgS3tNxbqCGM7g==} + + detect-port-alt@1.1.6: + resolution: {integrity: sha512-5tQykt+LqfJFBEYaDITx7S7cR7mJ/zQmLXZ2qt5w04ainYZw6tBf9dBunMjVeVOdYVRUzUOE4HkY5J7+uttb5Q==} + engines: {node: '>= 4.2.1'} + hasBin: true + + device-detector-js@3.0.3: + resolution: {integrity: sha512-jM89LJAvP6uOd84at8OlD9dWP8KeYCCHUde0RT0HQo/stdoRH4b54Xl/fntx2nEXCmqiFhmo+/cJetS2VGUHPw==} + engines: {node: '>= 8.11.4'} + + devlop@1.1.0: + resolution: {integrity: sha512-RWmIqhcFf1lRYBvNmr7qTNuyCt/7/ns2jbpp1+PalgE/rDQcBT0fioSMUpJ93irlUhC5hrg4cYqe6U+0ImW0rA==} + + didyoumean@1.2.2: + resolution: {integrity: sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw==} + + diff-sequences@27.5.1: + resolution: {integrity: sha512-k1gCAXAsNgLwEL+Y8Wvl+M6oEFj5bgazfZULpS5CneoPPXRaCCW7dm+q21Ky2VEE5X+VeRDBVg1Pcvvsr4TtNQ==} + engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0} + + diff@4.0.2: + resolution: {integrity: sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==} + engines: {node: '>=0.3.1'} + + diff@5.2.0: + resolution: {integrity: sha512-uIFDxqpRZGZ6ThOk84hEfqWoHx2devRFvpTZcTHur85vImfaxUbTW9Ryh4CpCuDnToOP1CEtXKIgytHBPVff5A==} + engines: {node: '>=0.3.1'} + + dir-glob@3.0.1: + resolution: {integrity: sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==} + engines: {node: '>=8'} + + dlv@1.1.3: + resolution: {integrity: sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==} + + dns-packet@5.6.1: + resolution: {integrity: sha512-l4gcSouhcgIKRvyy99RNVOgxXiicE+2jZoNmaNmZ6JXiGajBOJAesk1OBlJuM5k2c+eudGdLxDqXuPCKIj6kpw==} + engines: {node: '>=6'} + + doctrine@2.1.0: + resolution: {integrity: sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==} + engines: {node: '>=0.10.0'} + + doctrine@3.0.0: + resolution: {integrity: sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==} + engines: {node: '>=6.0.0'} + + dom-accessibility-api@0.5.16: + resolution: {integrity: sha512-X7BJ2yElsnOJ30pZF4uIIDfBEVgF4XEBxL9Bxhy6dnrm5hkzqmsWHGTiHqRiITNhMyFLyAiWndIJP7Z1NTteDg==} + + dom-converter@0.2.0: + resolution: {integrity: sha512-gd3ypIPfOMr9h5jIKq8E3sHOTCjeirnl0WK5ZdS1AW0Odt0b1PaWaHdJ4Qk4klv+YB9aJBS7mESXjFoDQPu6DA==} + + dom-helpers@5.2.1: + resolution: {integrity: sha512-nRCa7CK3VTrM2NmGkIy4cbK7IZlgBE/PYMn55rrXefr5xXDP0LdtfPnblFDoVdcAfslJ7or6iqAUnx0CCGIWQA==} + + dom-serializer@0.2.2: + resolution: {integrity: sha512-2/xPb3ORsQ42nHYiSunXkDjPLBaEj/xTwUO4B7XCZQTRk7EBtTOPaygh10YAAh2OI1Qrp6NWfpAhzswj0ydt9g==} + + dom-serializer@1.4.1: + resolution: {integrity: sha512-VHwB3KfrcOOkelEG2ZOfxqLZdfkil8PtJi4P8N2MMXucZq2yLp75ClViUlOVwyoHEDjYU433Aq+5zWP61+RGag==} + + dom-serializer@2.0.0: + resolution: {integrity: sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==} + + domelementtype@1.3.1: + resolution: {integrity: sha512-BSKB+TSpMpFI/HOxCNr1O8aMOTZ8hT3pM3GQ0w/mWRmkhEDSFJkkyzz4XQsBV44BChwGkrDfMyjVD0eA2aFV3w==} + + domelementtype@2.3.0: + resolution: {integrity: sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==} + + domexception@2.0.1: + resolution: {integrity: sha512-yxJ2mFy/sibVQlu5qHjOkf9J3K6zgmCxgJ94u2EdvDOV09H+32LtRswEcUsmUWN72pVLOEnTSRaIVVzVQgS0dg==} + engines: {node: '>=8'} + deprecated: Use your platform's native DOMException instead + + domexception@4.0.0: + resolution: {integrity: sha512-A2is4PLG+eeSfoTMA95/s4pvAoSo2mKtiM5jlHkAVewmiO8ISFTFKZjH7UAM1Atli/OT/7JHOrJRJiMKUZKYBw==} + engines: {node: '>=12'} + deprecated: Use your platform's native DOMException instead + + domhandler@4.3.1: + resolution: {integrity: sha512-GrwoxYN+uWlzO8uhUXRl0P+kHE4GtVPfYzVLcUxPL7KNdHKj66vvlhiweIHqYYXWlw+T8iLMp42Lm67ghw4WMQ==} + engines: {node: '>= 4'} + + domhandler@5.0.3: + resolution: {integrity: sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==} + engines: {node: '>= 4'} + + dompurify@3.3.3: + resolution: {integrity: sha512-Oj6pzI2+RqBfFG+qOaOLbFXLQ90ARpcGG6UePL82bJLtdsa6CYJD7nmiU8MW9nQNOtCHV3lZ/Bzq1X0QYbBZCA==} + + domutils@1.7.0: + resolution: {integrity: sha512-Lgd2XcJ/NjEw+7tFvfKxOzCYKZsdct5lczQ2ZaQY8Djz7pfAD3Gbp8ySJWtreII/vDlMVmxwa6pHmdxIYgttDg==} + + domutils@2.8.0: + resolution: {integrity: sha512-w96Cjofp72M5IIhpjgobBimYEfoPjx1Vx0BSX9P30WBdZW2WIKU0T1Bd0kz2eNZ9ikjKgHbEyKx8BB6H1L3h3A==} + + domutils@3.2.2: + resolution: {integrity: sha512-6kZKyUajlDuqlHKVX1w7gyslj9MPIXzIFiz/rGu35uC1wMi+kMhQwGhl4lt9unC9Vb9INnY9Z3/ZA3+FhASLaw==} + + dot-case@3.0.4: + resolution: {integrity: sha512-Kv5nKlh6yRrdrGvxeJ2e5y2eRUpkUosIW4A2AS38zwSz27zu7ufDwQPi5Jhs3XAlGNetl3bmnGhQsMtkKJnj3w==} + + dotenv-expand@5.1.0: + resolution: {integrity: sha512-YXQl1DSa4/PQyRfgrv6aoNjhasp/p4qs9FjJ4q4cQk+8m4r6k4ZSiEyytKG8f8W9gi8WsQtIObNmKd+tMzNTmA==} + + dotenv@10.0.0: + resolution: {integrity: sha512-rlBi9d8jpv9Sf1klPjNfFAuWDjKLwTIJJ/VxtoTwIR6hnZxcEOQCZg2oIL3MWBYw5GpUDKOEnND7LXTbIpQ03Q==} + engines: {node: '>=10'} + + dotenv@16.6.1: + resolution: {integrity: sha512-uBq4egWHTcTt33a72vpSG0z3HnPuIl6NqYcTrKEg2azoEyl2hpW0zqlxysq2pK9HlDIHyHyakeYaYnSAwd8bow==} + engines: {node: '>=12'} + + dunder-proto@1.0.1: + resolution: {integrity: sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==} + engines: {node: '>= 0.4'} + + duplexer@0.1.2: + resolution: {integrity: sha512-jtD6YG370ZCIi/9GTaJKQxWTZD045+4R4hTk/x1UyoqadyJ9x9CgSi1RlVDQF8U2sxLLSnFkCaMihqljHIWgMg==} + + eastasianwidth@0.2.0: + resolution: {integrity: sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==} + + ecc-jsbn@0.1.2: + resolution: {integrity: sha512-eh9O+hwRHNbG4BLTjEl3nw044CkGm5X6LoaCf7LPp7UU8Qrt47JYNi6nPX8xjW97TKGKm1ouctg0QSpZe9qrnw==} + + ee-first@1.1.1: + resolution: {integrity: sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==} + + ejs@3.1.10: + resolution: {integrity: sha512-UeJmFfOrAQS8OJWPZ4qtgHyWExa088/MtK5UEyoJGFH67cDEXkZSviOiKRCZ4Xij0zxI3JECgYs3oKx+AizQBA==} + engines: {node: '>=0.10.0'} + hasBin: true + + electron-to-chromium@1.5.234: + resolution: {integrity: sha512-RXfEp2x+VRYn8jbKfQlRImzoJU01kyDvVPBmG39eU2iuRVhuS6vQNocB8J0/8GrIMLnPzgz4eW6WiRnJkTuNWg==} + + emittery@0.10.2: + resolution: {integrity: sha512-aITqOwnLanpHLNXZJENbOgjUBeHocD+xsSJmNrjovKBW5HbSpW3d1pEls7GFQPUWXiwG9+0P4GtHfEqC/4M0Iw==} + engines: {node: '>=12'} + + emittery@0.8.1: + resolution: {integrity: sha512-uDfvUjVrfGJJhymx/kz6prltenw1u7WrCg1oa94zYY8xxVpLLUu045LAT0dhDZdXG58/EpPL/5kA180fQ/qudg==} + engines: {node: '>=10'} + + emoji-regex@8.0.0: + resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==} + + emoji-regex@9.2.2: + resolution: {integrity: sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==} + + emojis-list@3.0.0: + resolution: {integrity: sha512-/kyM18EfinwXZbno9FyUGeFh87KC8HRQBQGildHZbEuRyWFOmv1U10o9BBp8XVZDVNNuQKyIGIu5ZYAAXJ0V2Q==} + engines: {node: '>= 4'} + + enabled@2.0.0: + resolution: {integrity: sha512-AKrN98kuwOzMIdAizXGI86UFBoo26CL21UM763y1h/GMSJ4/OHU9k2YlsmBpyScFo/wbLzWQJBMCW4+IO3/+OQ==} + + encodeurl@1.0.2: + resolution: {integrity: sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==} + engines: {node: '>= 0.8'} + + encodeurl@2.0.0: + resolution: {integrity: sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==} + engines: {node: '>= 0.8'} + + encoding@0.1.13: + resolution: {integrity: sha512-ETBauow1T35Y/WZMkio9jiM0Z5xjHHmJ4XmjZOq1l/dXz3lr2sRn87nJy20RupqSh1F2m3HHPSp8ShIPQJrJ3A==} + + end-of-stream@1.4.5: + resolution: {integrity: sha512-ooEGc6HP26xXq/N+GCGOT0JKCLDGrq2bQUZrQ7gyrJiZANJ/8YDTxTpQBXGMn+WbIQXNVpyWymm7KYVICQnyOg==} + + engine.io-client@6.6.3: + resolution: {integrity: sha512-T0iLjnyNWahNyv/lcjS2y4oE358tVS/SYQNxYXGAJ9/GLgH4VCvOQ/mhTjqU88mLZCQgiG8RIegFHYCdVC+j5w==} + + engine.io-parser@5.2.3: + resolution: {integrity: sha512-HqD3yTBfnBxIrbnM1DoD6Pcq8NECnh8d4As1Qgh0z5Gg3jRRIqijury0CL3ghu/edArpUYiYqQiDUQBIs4np3Q==} + engines: {node: '>=10.0.0'} + + engine.io@6.6.4: + resolution: {integrity: sha512-ZCkIjSYNDyGn0R6ewHDtXgns/Zre/NT6Agvq1/WobF7JXgFff4SeDroKiCO3fNJreU9YG429Sc81o4w5ok/W5g==} + engines: {node: '>=10.2.0'} + + enhanced-resolve@5.18.3: + resolution: {integrity: sha512-d4lC8xfavMeBjzGr2vECC3fsGXziXZQyJxD868h2M/mBI3PwAuODxAkLkq5HYuvrPYcUtiLzsTo8U3PgX3Ocww==} + engines: {node: '>=10.13.0'} + + enquirer@2.4.1: + resolution: {integrity: sha512-rRqJg/6gd538VHvR3PSrdRBb/1Vy2YfzHqzvbhGIQpDRKIa4FgV/54b5Q1xYSxOOwKvjXweS26E0Q+nAMwp2pQ==} + engines: {node: '>=8.6'} + + entities@2.2.0: + resolution: {integrity: sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A==} + + entities@4.5.0: + resolution: {integrity: sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==} + engines: {node: '>=0.12'} + + entities@6.0.1: + resolution: {integrity: sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g==} + engines: {node: '>=0.12'} + + env-paths@2.2.1: + resolution: {integrity: sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==} + engines: {node: '>=6'} + + err-code@2.0.3: + resolution: {integrity: sha512-2bmlRpNKBxT/CRmPOlyISQpNj+qSeYvcym/uT0Jx2bMOlKLtSy1ZmLuVxSEKKyor/N5yhvp/ZiG1oE3DEYMSFA==} + + error-ex@1.3.4: + resolution: {integrity: sha512-sqQamAnR14VgCr1A618A3sGrygcpK+HEbenA/HiEAkkUwcZIIB/tgWqHFxWgOyDh4nB4JCRimh79dR5Ywc9MDQ==} + + error-stack-parser@2.1.4: + resolution: {integrity: sha512-Sk5V6wVazPhq5MhpO+AUxJn5x7XSXGl1R93Vn7i+zS15KDVxQijejNCrz8340/2bgLBjR9GtEG8ZVKONDjcqGQ==} + + es-abstract@1.24.0: + resolution: {integrity: sha512-WSzPgsdLtTcQwm4CROfS5ju2Wa1QQcVeT37jFjYzdFz1r9ahadC8B8/a4qxJxM+09F18iumCdRmlr96ZYkQvEg==} + engines: {node: '>= 0.4'} + + es-array-method-boxes-properly@1.0.0: + resolution: {integrity: sha512-wd6JXUmyHmt8T5a2xreUwKcGPq6f1f+WwIJkijUqiGcJz1qqnZgP6XIK+QyIWU5lT7imeNxUll48bziG+TSYcA==} + + es-define-property@1.0.1: + resolution: {integrity: sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==} + engines: {node: '>= 0.4'} + + es-errors@1.3.0: + resolution: {integrity: sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==} + engines: {node: '>= 0.4'} + + es-get-iterator@1.1.3: + resolution: {integrity: sha512-sPZmqHBe6JIiTfN5q2pEi//TwxmAFHwj/XEuYjTuse78i8KxaqMTTzxPoFKuzRpDpTJ+0NAbpfenkmH2rePtuw==} + + es-iterator-helpers@1.2.1: + resolution: {integrity: sha512-uDn+FE1yrDzyC0pCo961B2IHbdM8y/ACZsKD4dG6WqrjV53BADjwa7D+1aom2rsNVfLyDgU/eigvlJGJ08OQ4w==} + engines: {node: '>= 0.4'} + + es-module-lexer@1.7.0: + resolution: {integrity: sha512-jEQoCwk8hyb2AZziIOLhDqpm5+2ww5uIE6lkO/6jcOCusfk6LhMHpXXfBLXTZ7Ydyt0j4VoUQv6uGNYbdW+kBA==} + + es-object-atoms@1.1.1: + resolution: {integrity: sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==} + engines: {node: '>= 0.4'} + + es-set-tostringtag@2.1.0: + resolution: {integrity: sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==} + engines: {node: '>= 0.4'} + + es-shim-unscopables@1.1.0: + resolution: {integrity: sha512-d9T8ucsEhh8Bi1woXCf+TIKDIROLG5WCkxg8geBCbvk22kzwC5G2OnXVMO6FUsvQlgUUXQ2itephWDLqDzbeCw==} + engines: {node: '>= 0.4'} + + es-to-primitive@1.3.0: + resolution: {integrity: sha512-w+5mJ3GuFL+NjVtJlvydShqE1eN3h3PbI7/5LAsYJP/2qtuMXjfL2LpHSRqo4b4eSF5K/DH1JXKUAHSB2UW50g==} + engines: {node: '>= 0.4'} + + esbuild@0.27.1: + resolution: {integrity: sha512-yY35KZckJJuVVPXpvjgxiCuVEJT67F6zDeVTv4rizyPrfGBUpZQsvmxnN+C371c2esD/hNMjj4tpBhuueLN7aA==} + engines: {node: '>=18'} + hasBin: true + + escalade@3.2.0: + resolution: {integrity: sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==} + engines: {node: '>=6'} + + escape-html@1.0.3: + resolution: {integrity: sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==} + + escape-string-regexp@1.0.5: + resolution: {integrity: sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==} + engines: {node: '>=0.8.0'} + + escape-string-regexp@2.0.0: + resolution: {integrity: sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==} + engines: {node: '>=8'} + + escape-string-regexp@4.0.0: + resolution: {integrity: sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==} + engines: {node: '>=10'} + + escape-string-regexp@5.0.0: + resolution: {integrity: sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw==} + engines: {node: '>=12'} + + escodegen@1.14.3: + resolution: {integrity: sha512-qFcX0XJkdg+PB3xjZZG/wKSuT1PnQWx57+TVSjIMmILd2yC/6ByYElPwJnslDsuWuSAp4AwJGumarAAmJch5Kw==} + engines: {node: '>=4.0'} + hasBin: true + + escodegen@2.1.0: + resolution: {integrity: sha512-2NlIDTwUWJN0mRPQOdtQBzbUHvdGY2P1VXSyU83Q3xKxM7WHX2Ql8dKq782Q9TgQUNOLEzEYu9bzLNj1q88I5w==} + engines: {node: '>=6.0'} + hasBin: true + + eslint-config-prettier@8.10.2: + resolution: {integrity: sha512-/IGJ6+Dka158JnP5n5YFMOszjDWrXggGz1LaK/guZq9vZTmniaKlHcsscvkAhn9y4U+BU3JuUdYvtAMcv30y4A==} + hasBin: true + peerDependencies: + eslint: '>=7.0.0' + + eslint-config-react-app@7.0.1: + resolution: {integrity: sha512-K6rNzvkIeHaTd8m/QEh1Zko0KI7BACWkkneSs6s9cKZC/J27X3eZR6Upt1jkmZ/4FK+XUOPPxMEN7+lbUXfSlA==} + engines: {node: '>=14.0.0'} + peerDependencies: + eslint: ^8.0.0 + typescript: '*' + peerDependenciesMeta: + typescript: + optional: true + + eslint-import-resolver-node@0.3.9: + resolution: {integrity: sha512-WFj2isz22JahUv+B788TlO3N6zL3nNJGU8CcZbPZvVEkBPaJdCV4vy5wyghty5ROFbCRnm132v8BScu5/1BQ8g==} + + eslint-module-utils@2.12.1: + resolution: {integrity: sha512-L8jSWTze7K2mTg0vos/RuLRS5soomksDPoJLXIslC7c8Wmut3bx7CPpJijDcBZtxQ5lrbUdM+s0OlNbz0DCDNw==} + engines: {node: '>=4'} + peerDependencies: + '@typescript-eslint/parser': '*' + eslint: '*' + eslint-import-resolver-node: '*' + eslint-import-resolver-typescript: '*' + eslint-import-resolver-webpack: '*' + peerDependenciesMeta: + '@typescript-eslint/parser': + optional: true + eslint: + optional: true + eslint-import-resolver-node: + optional: true + eslint-import-resolver-typescript: + optional: true + eslint-import-resolver-webpack: + optional: true + + eslint-plugin-flowtype@8.0.3: + resolution: {integrity: sha512-dX8l6qUL6O+fYPtpNRideCFSpmWOUVx5QcaGLVqe/vlDiBSe4vYljDWDETwnyFzpl7By/WVIu6rcrniCgH9BqQ==} + engines: {node: '>=12.0.0'} + peerDependencies: + '@babel/plugin-syntax-flow': ^7.14.5 + '@babel/plugin-transform-react-jsx': ^7.14.9 + eslint: ^8.1.0 + + eslint-plugin-import@2.32.0: + resolution: {integrity: sha512-whOE1HFo/qJDyX4SnXzP4N6zOWn79WhnCUY/iDR0mPfQZO8wcYE4JClzI2oZrhBnnMUCBCHZhO6VQyoBU95mZA==} + engines: {node: '>=4'} + peerDependencies: + '@typescript-eslint/parser': '*' + eslint: ^2 || ^3 || ^4 || ^5 || ^6 || ^7.2.0 || ^8 || ^9 + peerDependenciesMeta: + '@typescript-eslint/parser': + optional: true + + eslint-plugin-jest@25.7.0: + resolution: {integrity: sha512-PWLUEXeeF7C9QGKqvdSbzLOiLTx+bno7/HC9eefePfEb257QFHg7ye3dh80AZVkaa/RQsBB1Q/ORQvg2X7F0NQ==} + engines: {node: ^12.13.0 || ^14.15.0 || >=16.0.0} + peerDependencies: + '@typescript-eslint/eslint-plugin': ^4.0.0 || ^5.0.0 + eslint: ^6.0.0 || ^7.0.0 || ^8.0.0 + jest: '*' + peerDependenciesMeta: + '@typescript-eslint/eslint-plugin': + optional: true + jest: + optional: true + + eslint-plugin-jsx-a11y@6.10.2: + resolution: {integrity: sha512-scB3nz4WmG75pV8+3eRUQOHZlNSUhFNq37xnpgRkCCELU3XMvXAxLk1eqWWyE22Ki4Q01Fnsw9BA3cJHDPgn2Q==} + engines: {node: '>=4.0'} + peerDependencies: + eslint: ^3 || ^4 || ^5 || ^6 || ^7 || ^8 || ^9 + + eslint-plugin-markdown@3.0.1: + resolution: {integrity: sha512-8rqoc148DWdGdmYF6WSQFT3uQ6PO7zXYgeBpHAOAakX/zpq+NvFYbDA/H7PYzHajwtmaOzAwfxyl++x0g1/N9A==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + deprecated: Please use @eslint/markdown instead + peerDependencies: + eslint: ^6.0.0 || ^7.0.0 || ^8.0.0 + + eslint-plugin-prettier@3.4.1: + resolution: {integrity: sha512-htg25EUYUeIhKHXjOinK4BgCcDwtLHjqaxCDsMy5nbnUMkKFvIhMVCp+5GFUXQ4Nr8lBsPqtGAqBenbpFqAA2g==} + engines: {node: '>=6.0.0'} + peerDependencies: + eslint: '>=5.0.0' + eslint-config-prettier: '*' + prettier: '>=1.13.0' + peerDependenciesMeta: + eslint-config-prettier: + optional: true + + eslint-plugin-react-hooks@4.6.2: + resolution: {integrity: sha512-QzliNJq4GinDBcD8gPB5v0wh6g8q3SUi6EFF0x8N/BL9PoVs0atuGc47ozMRyOWAKdwaZ5OnbOEa3WR+dSGKuQ==} + engines: {node: '>=10'} + peerDependencies: + eslint: ^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0 + + eslint-plugin-react@7.37.5: + resolution: {integrity: sha512-Qteup0SqU15kdocexFNAJMvCJEfa2xUKNV4CC1xsVMrIIqEy3SQ/rqyxCWNzfrd3/ldy6HMlD2e0JDVpDg2qIA==} + engines: {node: '>=4'} + peerDependencies: + eslint: ^3 || ^4 || ^5 || ^6 || ^7 || ^8 || ^9.7 + + eslint-plugin-testing-library@5.11.1: + resolution: {integrity: sha512-5eX9e1Kc2PqVRed3taaLnAAqPZGEX75C+M/rXzUAI3wIg/ZxzUm1OVAwfe/O+vE+6YXOLetSe9g5GKD2ecXipw==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0, npm: '>=6'} + peerDependencies: + eslint: ^7.5.0 || ^8.0.0 + + eslint-plugin-unused-imports@2.0.0: + resolution: {integrity: sha512-3APeS/tQlTrFa167ThtP0Zm0vctjr4M44HMpeg1P4bK6wItarumq0Ma82xorMKdFsWpphQBlRPzw/pxiVELX1A==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + peerDependencies: + '@typescript-eslint/eslint-plugin': ^5.0.0 + eslint: ^8.0.0 + peerDependenciesMeta: + '@typescript-eslint/eslint-plugin': + optional: true + + eslint-rule-composer@0.3.0: + resolution: {integrity: sha512-bt+Sh8CtDmn2OajxvNO+BX7Wn4CIWMpTRm3MaiKPCQcnnlm0CS2mhui6QaoeQugs+3Kj2ESKEEGJUdVafwhiCg==} + engines: {node: '>=4.0.0'} + + eslint-scope@5.1.1: + resolution: {integrity: sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==} + engines: {node: '>=8.0.0'} + + eslint-scope@7.2.2: + resolution: {integrity: sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + + eslint-visitor-keys@2.1.0: + resolution: {integrity: sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw==} + engines: {node: '>=10'} + + eslint-visitor-keys@3.4.3: + resolution: {integrity: sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + + eslint-webpack-plugin@3.2.0: + resolution: {integrity: sha512-avrKcGncpPbPSUHX6B3stNGzkKFto3eL+DKM4+VyMrVnhPc3vRczVlCq3uhuFOdRvDHTVXuzwk1ZKUrqDQHQ9w==} + engines: {node: '>= 12.13.0'} + peerDependencies: + eslint: ^7.0.0 || ^8.0.0 + webpack: ^5.0.0 + + eslint@8.57.1: + resolution: {integrity: sha512-ypowyDxpVSYpkXr9WPv2PAZCtNip1Mv5KTW0SCurXv/9iOpcrH9PaqUElksqEB6pChqHGDRCFTyrZlGhnLNGiA==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + deprecated: This version is no longer supported. Please see https://eslint.org/version-support for other options. + hasBin: true + + esm@3.2.25: + resolution: {integrity: sha512-U1suiZ2oDVWv4zPO56S0NcR5QriEahGtdN2OR6FiOG4WJvcjBVFB0qI4+eKoWFH483PKGuLuu6V8Z4T5g63UVA==} + engines: {node: '>=6'} + + espree@9.6.1: + resolution: {integrity: sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + + esprima@1.2.2: + resolution: {integrity: sha512-+JpPZam9w5DuJ3Q67SqsMGtiHKENSMRVoxvArfJZK01/BfLEObtZ6orJa/MtoGNR/rfMgp5837T41PAmTwAv/A==} + engines: {node: '>=0.4.0'} + hasBin: true + + esprima@4.0.1: + resolution: {integrity: sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==} + engines: {node: '>=4'} + hasBin: true + + esquery@1.6.0: + resolution: {integrity: sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==} + engines: {node: '>=0.10'} + + esrecurse@4.3.0: + resolution: {integrity: sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==} + engines: {node: '>=4.0'} + + estraverse@4.3.0: + resolution: {integrity: sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==} + engines: {node: '>=4.0'} + + estraverse@5.3.0: + resolution: {integrity: sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==} + engines: {node: '>=4.0'} + + estree-walker@1.0.1: + resolution: {integrity: sha512-1fMXF3YP4pZZVozF8j/ZLfvnR8NSIljt56UhbZ5PeeDmmGHpgpdwQt7ITlGvYaQukCvuBRMLEiKiYC+oeIg4cg==} + + esutils@2.0.3: + resolution: {integrity: sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==} + engines: {node: '>=0.10.0'} + + etag@1.8.1: + resolution: {integrity: sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==} + engines: {node: '>= 0.6'} + + event-stream@3.3.4: + resolution: {integrity: sha512-QHpkERcGsR0T7Qm3HNJSyXKEEj8AHNxkY3PK8TS2KJvQ7NiSHe3DDpwVKKtoYprL/AreyzFBeIkBIWChAqn60g==} + + eventemitter2@6.4.7: + resolution: {integrity: sha512-tYUSVOGeQPKt/eC1ABfhHy5Xd96N3oIijJvN3O9+TsC28T5V9yX9oEfEK5faP0EFSNVOG97qtAS68GBrQB2hDg==} + + eventemitter3@4.0.7: + resolution: {integrity: sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==} + + eventemitter3@5.0.1: + resolution: {integrity: sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA==} + + events-universal@1.0.1: + resolution: {integrity: sha512-LUd5euvbMLpwOF8m6ivPCbhQeSiYVNb8Vs0fQ8QjXo0JTkEHpz8pxdQf0gStltaPpw0Cca8b39KxvK9cfKRiAw==} + + events@3.3.0: + resolution: {integrity: sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==} + engines: {node: '>=0.8.x'} + + execa@0.2.2: + resolution: {integrity: sha512-zmBGzLd3nhA/NB9P7VLoceAO6vyYPftvl809Vjwe5U2fYI9tYWbeKqP3wZlAw9WS+znnkogf/bhSU+Gcn2NbkQ==} + engines: {node: '>=0.12'} + + execa@4.1.0: + resolution: {integrity: sha512-j5W0//W7f8UxAn8hXVnwG8tLwdiUy4FJLcSupCg6maBYZDpyBvTApK7KyuI4bKj8KOh1r2YH+6ucuYtJv1bTZA==} + engines: {node: '>=10'} + + execa@5.1.1: + resolution: {integrity: sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==} + engines: {node: '>=10'} + + execa@7.2.0: + resolution: {integrity: sha512-UduyVP7TLB5IcAQl+OzLyLcS/l32W/GLg+AhHJ+ow40FOk2U3SAllPwR44v4vmdFwIWqpdwxxpQbF1n5ta9seA==} + engines: {node: ^14.18.0 || ^16.14.0 || >=18.0.0} + + executable@4.1.1: + resolution: {integrity: sha512-8iA79xD3uAch729dUG8xaaBBFGaEa0wdD2VkYLFHwlqosEj/jT66AzcreRDSgV7ehnNLBW2WR5jIXwGKjVdTLg==} + engines: {node: '>=4'} + + exit@0.1.2: + resolution: {integrity: sha512-Zk/eNKV2zbjpKzrsQ+n1G6poVbErQxJ0LBOJXaKZ1EViLzH+hrLu9cdXI4zw9dBQJslwBEpbQ2P1oS7nDxs6jQ==} + engines: {node: '>= 0.8.0'} + + expand-template@2.0.3: + resolution: {integrity: sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg==} + engines: {node: '>=6'} + + expect@27.5.1: + resolution: {integrity: sha512-E1q5hSUG2AmYQwQJ041nvgpkODHQvB+RKlB4IYdru6uJsyFTRyZAP463M+1lINorwbqAmUggi6+WwkD8lCS/Dw==} + engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0} + + expect@30.2.0: + resolution: {integrity: sha512-u/feCi0GPsI+988gU2FLcsHyAHTU0MX1Wg68NhAnN7z/+C5wqG+CY8J53N9ioe8RXgaoz0nBR/TYMf3AycUuPw==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} + + express-basic-auth@1.2.1: + resolution: {integrity: sha512-L6YQ1wQ/mNjVLAmK3AG1RK6VkokA1BIY6wmiH304Xtt/cLTps40EusZsU1Uop+v9lTDPxdtzbFmdXfFO3KEnwA==} + + express-rate-limit@6.11.2: + resolution: {integrity: sha512-a7uwwfNTh1U60ssiIkuLFWHt4hAC5yxlLGU2VP0X4YNlyEDZAqF4tK3GD3NSitVBrCQmQ0++0uOyFOgC2y4DDw==} + engines: {node: '>= 14'} + peerDependencies: + express: ^4 || ^5 + + express@4.21.2: + resolution: {integrity: sha512-28HqgMZAmih1Czt9ny7qr6ek2qddF4FclbMzwhCREB6OFfH+rXAnuNCwo1/wFvrtbgsQDb4kSbX9de9lFbrXnA==} + engines: {node: '>= 0.10.0'} + + extend@3.0.2: + resolution: {integrity: sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==} + + extract-zip@2.0.1: + resolution: {integrity: sha512-GDhU9ntwuKyGXdZBUgTIe+vXnWj0fppUEtMDL0+idd5Sta8TGpHssn/eusA9mrPr9qNDym6SxAYZjNvCn/9RBg==} + engines: {node: '>= 10.17.0'} + hasBin: true + + extsprintf@1.3.0: + resolution: {integrity: sha512-11Ndz7Nv+mvAC1j0ktTa7fAb0vLyGGX+rMHNBYQviQDGU0Hw7lhctJANqbPhu9nV9/izT/IntTgZ7Im/9LJs9g==} + engines: {'0': node >=0.6.0} + + fast-deep-equal@3.1.3: + resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==} + + fast-diff@1.3.0: + resolution: {integrity: sha512-VxPP4NqbUjj6MaAOafWeUn2cXWLcCtljklUtZf0Ind4XQ+QPtmA0b18zZy0jIQx+ExRVCR/ZQpBmik5lXshNsw==} + + fast-equals@5.4.0: + resolution: {integrity: sha512-jt2DW/aNFNwke7AUd+Z+e6pz39KO5rzdbbFCg2sGafS4mk13MI7Z8O5z9cADNn5lhGODIgLwug6TZO2ctf7kcw==} + engines: {node: '>=6.0.0'} + + fast-fifo@1.3.2: + resolution: {integrity: sha512-/d9sfos4yxzpwkDkuN7k2SqFKtYNmCTzgfEpz82x34IM9/zc8KGxQoXg1liNC/izpRM/MBdt44Nmx41ZWqk+FQ==} + + fast-glob@3.3.3: + resolution: {integrity: sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==} + engines: {node: '>=8.6.0'} + + fast-json-stable-stringify@2.1.0: + resolution: {integrity: sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==} + + fast-levenshtein@2.0.6: + resolution: {integrity: sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==} + + fast-uri@3.1.0: + resolution: {integrity: sha512-iPeeDKJSWf4IEOasVVrknXpaBV0IApz/gp7S2bb7Z4Lljbl2MGJRqInZiUrQwV16cpzw/D3S5j5Julj/gT52AA==} + + fast-xml-parser@5.2.5: + resolution: {integrity: sha512-pfX9uG9Ki0yekDHx2SiuRIyFdyAr1kMIMitPvb0YBo8SUfKvia7w7FIyd/l6av85pFYRhZscS75MwMnbvY+hcQ==} + hasBin: true + + fastq@1.19.1: + resolution: {integrity: sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ==} + + fault@1.0.4: + resolution: {integrity: sha512-CJ0HCB5tL5fYTEA7ToAq5+kTwd++Borf1/bifxd9iT70QcXr4MRrO3Llf8Ifs70q+SJcGHFtnIE/Nw6giCtECA==} + + faye-websocket@0.11.4: + resolution: {integrity: sha512-CzbClwlXAuiRQAlUyfqPgvPoNKTckTPGfwZV4ZdAhVcP2lh9KUxJg2b5GkE7XbjKQ3YJnQ9z6D9ntLAlB+tP8g==} + engines: {node: '>=0.8.0'} + + fb-watchman@2.0.2: + resolution: {integrity: sha512-p5161BqbuCaSnB8jIbzQHOlpgsPmK5rJVDfDKO91Axs5NC1uu3HRQm6wt9cd9/+GtQQIO53JdGXXoyDpTAsgYA==} + + fbemitter@3.0.0: + resolution: {integrity: sha512-KWKaceCwKQU0+HPoop6gn4eOHk50bBv/VxjJtGMfwmJt3D29JpN4H4eisCtIPA+a8GVBam+ldMMpMjJUvpDyHw==} + + fbjs-css-vars@1.0.2: + resolution: {integrity: sha512-b2XGFAFdWZWg0phtAWLHCk836A1Xann+I+Dgd3Gk64MHKZO44FfoD1KxyvbSh0qZsIoXQGGlVztIY+oitJPpRQ==} + + fbjs@3.0.5: + resolution: {integrity: sha512-ztsSx77JBtkuMrEypfhgc3cI0+0h+svqeie7xHbh1k/IKdcydnvadp/mUaGgjAOXQmQSxsqgaRhS3q9fy+1kxg==} + + fd-slicer@1.1.0: + resolution: {integrity: sha512-cE1qsB/VwyQozZ+q1dGxR8LBYNZeofhEdUNGSMbQD3Gw2lAzX9Zb3uIU6Ebc/Fmyjo9AWWfnn0AUCHqtevs/8g==} + + fecha@4.2.3: + resolution: {integrity: sha512-OP2IUU6HeYKJi3i0z4A19kHMQoLVs4Hc+DPqqxI2h/DPZHTm/vjsfC6P0b4jCMy14XizLBqvndQ+UilD7707Jw==} + + fetch-blob@3.2.0: + resolution: {integrity: sha512-7yAQpD2UMJzLi1Dqv7qFYnPbaPx7ZfFK6PiIxQ4PfkGPyNyl2Ugx+a/umUonmKqjhM4DnfbMvdX6otXq83soQQ==} + engines: {node: ^12.20 || >= 14.13} + + figures@3.2.0: + resolution: {integrity: sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg==} + engines: {node: '>=8'} + + file-entry-cache@6.0.1: + resolution: {integrity: sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==} + engines: {node: ^10.12.0 || >=12.0.0} + + file-loader@6.2.0: + resolution: {integrity: sha512-qo3glqyTa61Ytg4u73GultjHGjdRyig3tG6lPtyX/jOEJvHif9uB0/OCI2Kif6ctF3caQTW2G5gym21oAsI4pw==} + engines: {node: '>= 10.13.0'} + peerDependencies: + webpack: ^4.0.0 || ^5.0.0 + + file-stream-rotator@0.6.1: + resolution: {integrity: sha512-u+dBid4PvZw17PmDeRcNOtCP9CCK/9lRN2w+r1xIS7yOL9JFrIBKTvrYsxT4P0pGtThYTn++QS5ChHaUov3+zQ==} + + file-uri-to-path@1.0.0: + resolution: {integrity: sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==} + + filelist@1.0.4: + resolution: {integrity: sha512-w1cEuf3S+DrLCQL7ET6kz+gmlJdbq9J7yXCSjK/OZCPA+qEN1WyF4ZAf0YYJa4/shHJra2t/d/r8SV4Ji+x+8Q==} + + filesize@8.0.7: + resolution: {integrity: sha512-pjmC+bkIF8XI7fWaH8KxHcZL3DPybs1roSKP4rKDvy20tAWwIObE4+JIseG2byfGKhud5ZnM4YSGKBz7Sh0ndQ==} + engines: {node: '>= 0.4.0'} + + fill-range@7.1.1: + resolution: {integrity: sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==} + engines: {node: '>=8'} + + filter-obj@5.1.0: + resolution: {integrity: sha512-qWeTREPoT7I0bifpPUXtxkZJ1XJzxWtfoWWkdVGqa+eCr3SHW/Ocp89o8vLvbUuQnadybJpjOKu4V+RwO6sGng==} + engines: {node: '>=14.16'} + + finalhandler@1.3.1: + resolution: {integrity: sha512-6BN9trH7bp3qvnrRyzsBz+g3lZxTNZTbVO2EV1CS0WIcDbawYVdYvGflME/9QP0h0pYlCDBCTjYa9nZzMDpyxQ==} + engines: {node: '>= 0.8'} + + find-cache-dir@3.3.2: + resolution: {integrity: sha512-wXZV5emFEjrridIgED11OoUKLxiYjAcqot/NJdAkOhlJ+vGzwhOAfcG5OX1jP+S0PcjEn8bdMJv+g2jwQ3Onig==} + engines: {node: '>=8'} + + find-root@1.1.0: + resolution: {integrity: sha512-NKfW6bec6GfKc0SGx1e07QZY9PE99u0Bft/0rzSD5k3sO/vwkVUpDUKVm5Gpp5Ue3YfShPFTX2070tDs5kB9Ng==} + + find-up@3.0.0: + resolution: {integrity: sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==} + engines: {node: '>=6'} + + find-up@4.1.0: + resolution: {integrity: sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==} + engines: {node: '>=8'} + + find-up@5.0.0: + resolution: {integrity: sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==} + engines: {node: '>=10'} + + flat-cache@3.2.0: + resolution: {integrity: sha512-CYcENa+FtcUKLmhhqyctpclsq7QF38pKjZHsGNiSQF5r4FtoKDWabFDl3hzaEQMvT1LHEysw5twgLvpYYb4vbw==} + engines: {node: ^10.12.0 || >=12.0.0} + + flatted@3.3.3: + resolution: {integrity: sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg==} + + flowise-embed-react@3.1.5: + resolution: {integrity: sha512-rOjUy5iZdlhyd2/7U3wYdFOQKSFWq+wPJdVTrP5XeFjlGFdpCzM+uzaID3Uw+hHWIqlFYUIT5DeP/6eIvPeBmg==} + peerDependencies: + flowise-embed: '*' + react: 18.x + + flowise-embed@3.1.5: + resolution: {integrity: sha512-A7VJMLhsLVLl4eyRJlQn2Oqm5hQz8DmY+iSPuK5u/MiQ7Jip9W60gFO6NMTTJ8Xm9JbVx0QkajGOCQDW88hpWw==} + + flowise-react-json-view@1.21.7: + resolution: {integrity: sha512-oFjwtSLJkUWk6waLh8heCQ4o9b60FJRA2X8LefaZp5WaJvj/Rr2HILjk+ocf1JkfTcq8jc6t2jfIybg4leWsaQ==} + peerDependencies: + react: ^17.0.0 || ^16.3.0 || ^15.5.4 + react-dom: ^17.0.0 || ^16.3.0 || ^15.5.4 + + flux@4.0.4: + resolution: {integrity: sha512-NCj3XlayA2UsapRpM7va6wU1+9rE5FIL7qoMcmxWHRzbp0yujihMBm9BBHZ1MDIk5h5o2Bl6eGiCe8rYELAmYw==} + peerDependencies: + react: ^15.0.2 || ^16.0.0 || ^17.0.0 + + fn.name@1.1.0: + resolution: {integrity: sha512-GRnmB5gPyJpAhTQdSZTSp9uaPSvl09KoYcMQtsB9rQoOmzs9dH6ffeccH+Z+cv6P68Hu5bC6JjRh4Ah/mHSNRw==} + + follow-redirects@1.15.11: + resolution: {integrity: sha512-deG2P0JfjrTxl50XGCDyfI97ZGVCxIpfKYmfyrQ54n5FO/0gfIES8C/Psl6kWVDolizcaaxZJnTS0QSMxvnsBQ==} + engines: {node: '>=4.0'} + peerDependencies: + debug: '*' + peerDependenciesMeta: + debug: + optional: true + + for-each@0.3.5: + resolution: {integrity: sha512-dKx12eRCVIzqCxFGplyFKJMPvLEWgmNtUrpTiJIR5u97zEhRG8ySrtboPHZXx7daLxQVrl643cTzbab2tkQjxg==} + engines: {node: '>= 0.4'} + + foreground-child@3.3.1: + resolution: {integrity: sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==} + engines: {node: '>=14'} + + forever-agent@0.6.1: + resolution: {integrity: sha512-j0KLYPhm6zeac4lz3oJ3o65qvgQCcPubiyotZrXqEaG4hNagNYO8qdlUrX5vwqv9ohqeT/Z3j6+yW067yWWdUw==} + + fork-ts-checker-webpack-plugin@6.5.3: + resolution: {integrity: sha512-SbH/l9ikmMWycd5puHJKTkZJKddF4iRLyW3DeZ08HTI7NGyLS38MXd/KGgeWumQO7YNQbW2u/NtPT2YowbPaGQ==} + engines: {node: '>=10', yarn: '>=1.0.0'} + peerDependencies: + eslint: '>= 6' + typescript: '>= 2.7' + vue-template-compiler: '*' + webpack: '>= 4' + peerDependenciesMeta: + eslint: + optional: true + vue-template-compiler: + optional: true + + form-data@4.0.4: + resolution: {integrity: sha512-KrGhL9Q4zjj0kiUt5OO4Mr/A/jlI2jDYs5eHBpYHPcBEVSiipAvn2Ko2HnPe20rmcuuvMHNdZFp+4IlGTMF0Ow==} + engines: {node: '>= 6'} + + format@0.2.2: + resolution: {integrity: sha512-wzsgA6WOq+09wrU1tsJ09udeR/YZRaeArL9e1wPbFg3GG2yDnC2ldKpxs4xunpFF9DgqCqOIra3bc1HWrJ37Ww==} + engines: {node: '>=0.4.x'} + + formdata-polyfill@4.0.10: + resolution: {integrity: sha512-buewHzMvYL29jdeQTVILecSaZKnt/RJWjoZCF5OW60Z67/GmSLBkOFM7qh1PI3zFNtJbaZL5eQu1vLfazOwj4g==} + engines: {node: '>=12.20.0'} + + formik@2.4.6: + resolution: {integrity: sha512-A+2EI7U7aG296q2TLGvNapDNTZp1khVt5Vk0Q/fyfSROss0V/V6+txt2aJnwEos44IxTCW/LYAi/zgWzlevj+g==} + peerDependencies: + react: '>=16.8.0' + + forwarded@0.2.0: + resolution: {integrity: sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==} + engines: {node: '>= 0.6'} + + fraction.js@4.3.7: + resolution: {integrity: sha512-ZsDfxO51wGAXREY55a7la9LScWpwv9RxIrYABrlvOFBlH/ShPnrtsXeuUIfXKKOVicNxQ+o8JTbJvjS4M89yew==} + + framer-motion@4.1.17: + resolution: {integrity: sha512-thx1wvKzblzbs0XaK2X0G1JuwIdARcoNOW7VVwjO8BUltzXPyONGAElLu6CiCScsOQRI7FIk/45YTFtJw5Yozw==} + peerDependencies: + react: '>=16.8 || ^17.0.0' + react-dom: '>=16.8 || ^17.0.0' + + framesync@5.3.0: + resolution: {integrity: sha512-oc5m68HDO/tuK2blj7ZcdEBRx3p1PjrgHazL8GYEpvULhrtGIFbQArN6cQS2QhW8mitffaB+VYzMjDqBxxQeoA==} + + fresh@0.5.2: + resolution: {integrity: sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==} + engines: {node: '>= 0.6'} + + from@0.1.7: + resolution: {integrity: sha512-twe20eF1OxVxp/ML/kq2p1uc6KvFK/+vs8WjEbeKmV2He22MKm7YF2ANIt+EOqhJ5L3K/SuuPhk0hWQDjOM23g==} + + fs-extra@10.1.0: + resolution: {integrity: sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==} + engines: {node: '>=12'} + + fs-extra@9.1.0: + resolution: {integrity: sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==} + engines: {node: '>=10'} + + fs-minipass@2.1.0: + resolution: {integrity: sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==} + engines: {node: '>= 8'} + + fs-monkey@1.1.0: + resolution: {integrity: sha512-QMUezzXWII9EV5aTFXW1UBVUO77wYPpjqIF8/AviUCThNeSYZykpoTixUeaNNBwmCev0AMDWMAni+f8Hxb1IFw==} + + fsevents@2.3.3: + resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==} + engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} + os: [darwin] + + function-bind@1.1.2: + resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==} + + function.prototype.name@1.1.8: + resolution: {integrity: sha512-e5iwyodOHhbMr/yNrc7fDYG4qlbIvI5gajyzPnb5TCwyhjApznQh1BMFou9b30SevY43gCJKXycoCBjMbsuW0Q==} + engines: {node: '>= 0.4'} + + functions-have-names@1.2.3: + resolution: {integrity: sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==} + + gauge@3.0.2: + resolution: {integrity: sha512-+5J6MS/5XksCuXq++uFRsnUd7Ovu1XenbeuIuNRJxYWjgQbPuFhT14lAvsWfqfAmnwluf1OwMjz39HjfLPci0Q==} + engines: {node: '>=10'} + deprecated: This package is no longer supported. + + gauge@4.0.4: + resolution: {integrity: sha512-f9m+BEN5jkg6a0fZjleidjN51VE1X+mPFQ2DJ0uv1V39oCLCbsGe6yjbBnp7eK7z/+GAon99a3nHuqbuuthyPg==} + engines: {node: ^12.13.0 || ^14.15.0 || >=16.0.0} + deprecated: This package is no longer supported. + + generate-function@2.3.1: + resolution: {integrity: sha512-eeB5GfMNeevm/GRYq20ShmsaGcmI81kIX2K9XQx5miC8KdHaC6Jm0qQ8ZNeGOi7wYB8OsdxKs+Y2oVuTFuVwKQ==} + + generator-function@2.0.1: + resolution: {integrity: sha512-SFdFmIJi+ybC0vjlHN0ZGVGHc3lgE0DxPAT0djjVg+kjOnSqclqmj0KQ7ykTOLP6YxoqOvuAODGdcHJn+43q3g==} + engines: {node: '>= 0.4'} + + generic-pool@3.9.0: + resolution: {integrity: sha512-hymDOu5B53XvN4QT9dBmZxPX4CWhBPPLguTZ9MMFeFa/Kg0xWVfylOVNlJji/E7yTZWFd/q9GO5TxDLq156D7g==} + engines: {node: '>= 4'} + + gensync@1.0.0-beta.2: + resolution: {integrity: sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==} + engines: {node: '>=6.9.0'} + + get-caller-file@2.0.5: + resolution: {integrity: sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==} + engines: {node: 6.* || 8.* || >= 10.*} + + get-intrinsic@1.3.0: + resolution: {integrity: sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==} + engines: {node: '>= 0.4'} + + get-own-enumerable-property-symbols@3.0.2: + resolution: {integrity: sha512-I0UBV/XOz1XkIJHEUDMZAbzCThU/H8DxmSfmdGcKPnVhu2VfFqr34jr9777IyaTYvxjedWhqVIilEDsCdP5G6g==} + + get-package-type@0.1.0: + resolution: {integrity: sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==} + engines: {node: '>=8.0.0'} + + get-port@6.1.2: + resolution: {integrity: sha512-BrGGraKm2uPqurfGVj/z97/zv8dPleC6x9JBNRTrDNtCkkRF4rPwrQXFgL7+I+q8QSdU4ntLQX2D7KIxSy8nGw==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + + get-proto@1.0.1: + resolution: {integrity: sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==} + engines: {node: '>= 0.4'} + + get-stream@5.2.0: + resolution: {integrity: sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==} + engines: {node: '>=8'} + + get-stream@6.0.1: + resolution: {integrity: sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==} + engines: {node: '>=10'} + + get-symbol-description@1.1.0: + resolution: {integrity: sha512-w9UMqWwJxHNOvoNzSJ2oPF5wvYcvP7jUvYzhp67yEhTi17ZDBBC1z9pTdGuzjD+EFIqLSYRweZjqfiPzQ06Ebg==} + engines: {node: '>= 0.4'} + + get-them-args@1.3.2: + resolution: {integrity: sha512-LRn8Jlk+DwZE4GTlDbT3Hikd1wSHgLMme/+7ddlqKd7ldwR6LjJgTVWzBnR01wnYGe4KgrXjg287RaI22UHmAw==} + + getos@3.2.1: + resolution: {integrity: sha512-U56CfOK17OKgTVqozZjUKNdkfEv6jk5WISBJ8SHoagjE6L69zOwl3Z+O8myjY9MEW3i2HPWQBt/LTbCgcC973Q==} + + getpass@0.1.7: + resolution: {integrity: sha512-0fzj9JxOLfJ+XGLhR8ze3unN0KZCgZwiSSDz168VERjK8Wl8kVSdcu2kspd4s4wtAa1y/qrVRiAA0WclVsu0ng==} + + github-from-package@0.0.0: + resolution: {integrity: sha512-SyHy3T1v2NUXn29OsWdxmK6RwHD+vkj3v8en8AOBZ1wBQ/hCAQ5bAQTD02kW4W9tUp/3Qh6J8r9EvntiyCmOOw==} + + glob-parent@6.0.2: + resolution: {integrity: sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==} + engines: {node: '>=10.13.0'} + + glob-to-regex.js@1.2.0: + resolution: {integrity: sha512-QMwlOQKU/IzqMUOAZWubUOT8Qft+Y0KQWnX9nK3ch0CJg0tTp4TvGZsTfudYKv2NzoQSyPcnA6TYeIQ3jGichQ==} + engines: {node: '>=10.0'} + peerDependencies: + tslib: '2' + + glob-to-regexp@0.4.1: + resolution: {integrity: sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==} + + glob@10.5.0: + resolution: {integrity: sha512-DfXN8DfhJ7NH3Oe7cFmu3NCu1wKbkReJ8TorzSAFbSKrlNaQSKfIzqYqVY8zlbs2NLBbWpRiU52GX2PbaBVNkg==} + deprecated: Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me + hasBin: true + + global-dirs@3.0.1: + resolution: {integrity: sha512-NBcGGFbBA9s1VzD41QXDG+3++t9Mn5t1FpLdhESY6oKY4gYTFpX4wO3sqGUa0Srjtbfj3szX0RnemmrVRUdULA==} + engines: {node: '>=10'} + + global-modules@2.0.0: + resolution: {integrity: sha512-NGbfmJBp9x8IxyJSd1P+otYK8vonoJactOogrVfFRIAEY1ukil8RSKDz2Yo7wh1oihl51l/r6W4epkeKJHqL8A==} + engines: {node: '>=6'} + + global-prefix@3.0.0: + resolution: {integrity: sha512-awConJSVCHVGND6x3tmMaKcQvwXLhjdkmomy2W+Goaui8YPgYgXJZewhg3fWC+DlfqqQuWg8AwqjGTD2nAPVWg==} + engines: {node: '>=6'} + + globals@13.24.0: + resolution: {integrity: sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==} + engines: {node: '>=8'} + + globalthis@1.0.4: + resolution: {integrity: sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ==} + engines: {node: '>= 0.4'} + + globby@11.1.0: + resolution: {integrity: sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==} + engines: {node: '>=10'} + + globby@13.2.2: + resolution: {integrity: sha512-Y1zNGV+pzQdh7H39l9zgB4PJqjRNqydvdYCDG4HFXM4XuvSaQQlEc91IU1yALL8gUTDomgBAfz3XJdmUS+oo0w==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + + globrex@0.1.2: + resolution: {integrity: sha512-uHJgbwAMwNFf5mLst7IWLNg14x1CkeqglJb/K3doi4dw6q2IvAAmM/Y81kevy83wP+Sst+nutFTYOGg3d1lsxg==} + + gopd@1.2.0: + resolution: {integrity: sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==} + engines: {node: '>= 0.4'} + + graceful-fs@4.2.11: + resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==} + + graphemer@1.4.0: + resolution: {integrity: sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==} + + gzip-size@6.0.0: + resolution: {integrity: sha512-ax7ZYomf6jqPTQ4+XCpUGyXKHk5WweS+e05MBO4/y3WJ5RkmPXNKvX+bx1behVILVwr6JSQvZAku021CHPXG3Q==} + engines: {node: '>=10'} + + handle-thing@2.0.1: + resolution: {integrity: sha512-9Qn4yBxelxoh2Ow62nP+Ka/kMnOXRi8BXnRaUwezLNhqelnN49xKz4F/dPP8OYLxLxq6JDtZb2i9XznUQbNPTg==} + + harmony-reflect@1.6.2: + resolution: {integrity: sha512-HIp/n38R9kQjDEziXyDTuW3vvoxxyxjxFzXLrBr18uB47GnSt+G9D29fqrpM5ZkspMcPICud3XsBJQ4Y2URg8g==} + + has-bigints@1.1.0: + resolution: {integrity: sha512-R3pbpkcIqv2Pm3dUwgjclDRVmWpTJW2DcMzcIhEXEx1oh/CEMObMm3KLmRJOdvhM7o4uQBnwr8pzRK2sJWIqfg==} + engines: {node: '>= 0.4'} + + has-flag@3.0.0: + resolution: {integrity: sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==} + engines: {node: '>=4'} + + has-flag@4.0.0: + resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==} + engines: {node: '>=8'} + + has-property-descriptors@1.0.2: + resolution: {integrity: sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==} + + has-proto@1.2.0: + resolution: {integrity: sha512-KIL7eQPfHQRC8+XluaIw7BHUwwqL19bQn4hzNgdr+1wXoU0KKj6rufu47lhY7KbJR2C6T6+PfyN0Ea7wkSS+qQ==} + engines: {node: '>= 0.4'} + + has-symbols@1.1.0: + resolution: {integrity: sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==} + engines: {node: '>= 0.4'} + + has-tostringtag@1.0.2: + resolution: {integrity: sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==} + engines: {node: '>= 0.4'} + + has-unicode@2.0.1: + resolution: {integrity: sha512-8Rf9Y83NBReMnx0gFzA8JImQACstCYWUplepDa9xprwwtmgEZUF0h/i5xSA625zB/I37EtrswSST6OXxwaaIJQ==} + + hasown@2.0.2: + resolution: {integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==} + engines: {node: '>= 0.4'} + + hast-util-from-dom@4.2.0: + resolution: {integrity: sha512-t1RJW/OpJbCAJQeKi3Qrj1cAOLA0+av/iPFori112+0X7R3wng+jxLA+kXec8K4szqPRGI8vPxbbpEYvvpwaeQ==} + + hast-util-is-element@2.1.3: + resolution: {integrity: sha512-O1bKah6mhgEq2WtVMk+Ta5K7pPMqsBBlmzysLdcwKVrqzZQ0CHqUPiIVspNhAG1rvxpvJjtGee17XfauZYKqVA==} + + hast-util-parse-selector@2.2.5: + resolution: {integrity: sha512-7j6mrk/qqkSehsM92wQjdIgWM2/BW61u/53G6xmC8i1OmEdKLHbk419QKQUjz6LglWsfqoiHmyMRkP1BGjecNQ==} + + hast-util-parse-selector@3.1.1: + resolution: {integrity: sha512-jdlwBjEexy1oGz0aJ2f4GKMaVKkA9jwjr4MjAAI22E5fM/TXVZHuS5OpONtdeIkRKqAaryQ2E9xNQxijoThSZA==} + + hast-util-to-text@3.1.2: + resolution: {integrity: sha512-tcllLfp23dJJ+ju5wCCZHVpzsQQ43+moJbqVX3jNWPB7z/KFC4FyZD6R7y94cHL6MQ33YtMZL8Z0aIXXI4XFTw==} + + hast-util-whitespace@2.0.1: + resolution: {integrity: sha512-nAxA0v8+vXSBDt3AnRUNjyRIQ0rD+ntpbAp4LnPkumc5M9yUbSMa4XDU9Q6etY4f1Wp4bNgvc1yjiZtsTTrSng==} + + hastscript@6.0.0: + resolution: {integrity: sha512-nDM6bvd7lIqDUiYEiu5Sl/+6ReP0BMk/2f4U/Rooccxkj0P5nm+acM5PrGJ/t5I8qPGiqZSE6hVAwZEdZIvP4w==} + + hastscript@7.2.0: + resolution: {integrity: sha512-TtYPq24IldU8iKoJQqvZOuhi5CyCQRAbvDOX0x1eW6rsHSxa/1i2CCiptNTotGHJ3VoHRGmqiv6/D3q113ikkw==} + + he@1.2.0: + resolution: {integrity: sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==} + hasBin: true + + hey-listen@1.0.8: + resolution: {integrity: sha512-COpmrF2NOg4TBWUJ5UVyaCU2A88wEMkUPK4hNqyCkqHbxT92BbvfjoSozkAIIm6XhicGlJHhFdullInrdhwU8Q==} + + highlight.js@10.7.3: + resolution: {integrity: sha512-tzcUFauisWKNHaRkN4Wjl/ZA07gENAjFl3J/c480dprkGTg5EQstgaNFqBfUqCq54kZRIEcreTsAgF/m2quD7A==} + + highlight.js@11.11.1: + resolution: {integrity: sha512-Xwwo44whKBVCYoliBQwaPvtd/2tYFkRQtXDWj1nackaV2JPXx3L0+Jvd8/qCJ2p+ML0/XVkJ2q+Mr+UVdpJK5w==} + engines: {node: '>=12.0.0'} + + highlightjs-vue@1.0.0: + resolution: {integrity: sha512-PDEfEF102G23vHmPhLyPboFCD+BkMGu+GuJe2d9/eH4FsCwvgBpnc9n0pGE+ffKdph38s6foEZiEjdgHdzp+IA==} + + history@5.3.0: + resolution: {integrity: sha512-ZqaKwjjrAYUYfLG+htGaIIZ4nioX2L70ZUMIFysS3xvBsSG4x/n1V6TXV3N8ZYNuFGlDirFg32T7B6WOUPDYcQ==} + + hoist-non-react-statics@3.3.2: + resolution: {integrity: sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==} + + hoopy@0.1.4: + resolution: {integrity: sha512-HRcs+2mr52W0K+x8RzcLzuPPmVIKMSv97RGHy0Ea9y/mpcaK+xTrjICA04KAHi4GRzxliNqNJEFYWHghy3rSfQ==} + engines: {node: '>= 6.0.0'} + + hpack.js@2.1.6: + resolution: {integrity: sha512-zJxVehUdMGIKsRaNt7apO2Gqp0BdqW5yaiGHXXmbpvxgBYVZnAql+BJb4RO5ad2MgpbZKn5G6nMnegrH1FcNYQ==} + + html-dom-parser@3.1.7: + resolution: {integrity: sha512-cDgNF4YgF6J3H+d9mcldGL19p0GzVdS3iGuDNzYWQpU47q3+IRM85X3Xo07E+nntF4ek4s78A9V24EwxlPTjig==} + + html-encoding-sniffer@2.0.1: + resolution: {integrity: sha512-D5JbOMBIR/TVZkubHT+OyT2705QvogUW4IBn6nHd756OwieSF9aDYFj4dv6HHEVGYbHaLETa3WggZYWWMyy3ZQ==} + engines: {node: '>=10'} + + html-encoding-sniffer@3.0.0: + resolution: {integrity: sha512-oWv4T4yJ52iKrufjnyZPkrN0CH3QnrUqdB6In1g5Fe1mia8GmF36gnfNySxoZtxD5+NmYw1EElVXiBk93UeskA==} + engines: {node: '>=12'} + + html-entities@2.6.0: + resolution: {integrity: sha512-kig+rMn/QOVRvr7c86gQ8lWXq+Hkv6CbAH1hLu+RG338StTpE8Z0b44SDVaqVu7HGKf27frdmUYEs9hTUX/cLQ==} + + html-escaper@2.0.2: + resolution: {integrity: sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==} + + html-minifier-terser@6.1.0: + resolution: {integrity: sha512-YXxSlJBZTP7RS3tWnQw74ooKa6L9b9i9QYXY21eUEvhZ3u9XLfv6OnFsQq6RxkhHygsaUMvYsZRV5rU/OVNZxw==} + engines: {node: '>=12'} + hasBin: true + + html-react-parser@3.0.16: + resolution: {integrity: sha512-ysQZtRFPcg+McVb4B05oNWSnqM14zagpvTgGcI5e1/BvCl38YwzWzKibrbBmXeemg70olN1bAoeixo7o06G5Eg==} + peerDependencies: + react: 0.14 || 15 || 16 || 17 || 18 + + html-webpack-plugin@5.6.4: + resolution: {integrity: sha512-V/PZeWsqhfpE27nKeX9EO2sbR+D17A+tLf6qU+ht66jdUsN0QLKJN27Z+1+gHrVMKgndBahes0PU6rRihDgHTw==} + engines: {node: '>=10.13.0'} + peerDependencies: + '@rspack/core': 0.x || 1.x + webpack: ^5.20.0 + peerDependenciesMeta: + '@rspack/core': + optional: true + webpack: + optional: true + + htmlparser2@6.1.0: + resolution: {integrity: sha512-gyyPk6rgonLFEDGoeRgQNaEUvdJ4ktTmmUh/h2t7s+M8oPpIPxgNACWa+6ESR57kXstwqPiCut0V8NRpcwgU7A==} + + htmlparser2@8.0.2: + resolution: {integrity: sha512-GYdjWKDkbRLkZ5geuHs5NY1puJ+PXwP7+fHPRz06Eirsb9ugf6d8kkXav6ADhcODhFFPMIXyxkxSuMf3D6NCFA==} + + http-cache-semantics@4.2.0: + resolution: {integrity: sha512-dTxcvPXqPvXBQpq5dUr6mEMJX4oIEFv6bwom3FDwKRDsuIjjJGANqhBuoAn9c1RQJIdAKav33ED65E2ys+87QQ==} + + http-deceiver@1.2.7: + resolution: {integrity: sha512-LmpOGxTfbpgtGVxJrj5k7asXHCgNZp5nLfp+hWc8QQRqtb7fUy6kRY3BO1h9ddF6yIPYUARgxGOwB42DnxIaNw==} + + http-errors@1.6.3: + resolution: {integrity: sha512-lks+lVC8dgGyh97jxvxeYTWQFvh4uw4yC12gVl63Cg30sjPX4wuGcdkICVXDAESr6OJGjqGA8Iz5mkeN6zlD7A==} + engines: {node: '>= 0.6'} + + http-errors@2.0.0: + resolution: {integrity: sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==} + engines: {node: '>= 0.8'} + + http-parser-js@0.5.10: + resolution: {integrity: sha512-Pysuw9XpUq5dVc/2SMHpuTY01RFl8fttgcyunjL7eEMhGM3cI4eOmiCycJDVCo/7O7ClfQD3SaI6ftDzqOXYMA==} + + http-proxy-agent@4.0.1: + resolution: {integrity: sha512-k0zdNgqWTGA6aeIRVpvfVob4fL52dTfaehylg0Y4UvSySvOq/Y+BOyPrgpUrA7HylqvU8vIZGsRuXmspskV0Tg==} + engines: {node: '>= 6'} + + http-proxy-agent@5.0.0: + resolution: {integrity: sha512-n2hY8YdoRE1i7r6M0w9DIw5GgZN0G25P8zLCRQ8rjXtTU3vsNFBI/vWK/UIeE6g5MUUz6avwAPXmL6Fy9D/90w==} + engines: {node: '>= 6'} + + http-proxy-middleware@3.0.3: + resolution: {integrity: sha512-usY0HG5nyDUwtqpiZdETNbmKtw3QQ1jwYFZ9wi5iHzX2BcILwQKtYDJPo7XHTsu5Z0B2Hj3W9NNnbd+AjFWjqg==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + + http-proxy@1.18.1: + resolution: {integrity: sha512-7mz/721AbnJwIVbnaSv1Cz3Am0ZLT/UBwkC92VlxhXv/k/BBQfM2fXElQNC27BVGr0uwUpplYPQM9LnaBMR5NQ==} + engines: {node: '>=8.0.0'} + + http-signature@1.4.0: + resolution: {integrity: sha512-G5akfn7eKbpDN+8nPS/cb57YeA1jLTVxjpCj7tmm3QKPdyDy7T+qSC40e9ptydSWvkwjSXw1VbkpyEm39ukeAg==} + engines: {node: '>=0.10'} + + http-status-codes@2.3.0: + resolution: {integrity: sha512-RJ8XvFvpPM/Dmc5SV+dC4y5PCeOhT3x1Hq0NU3rjGeg5a/CqlhZ7uudknPwZFz4aeAXDcbAyaeP7GAo9lvngtA==} + + https-proxy-agent@5.0.1: + resolution: {integrity: sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==} + engines: {node: '>= 6'} + + https-proxy-agent@7.0.6: + resolution: {integrity: sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==} + engines: {node: '>= 14'} + + human-signals@1.1.1: + resolution: {integrity: sha512-SEQu7vl8KjNL2eoGBLF3+wAjpsNfA9XMlXAYj/3EdaNfAlxKthD1xjEQfGOUhllCGGJVNY34bRr6lPINhNjyZw==} + engines: {node: '>=8.12.0'} + + human-signals@2.1.0: + resolution: {integrity: sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==} + engines: {node: '>=10.17.0'} + + human-signals@4.3.1: + resolution: {integrity: sha512-nZXjEF2nbo7lIw3mgYjItAfgQXog3OjJogSbKa2CQIIvSGWcKgeJnQlNXip6NglNzYH45nSRiEVimMvYL8DDqQ==} + engines: {node: '>=14.18.0'} + + humanize-ms@1.2.1: + resolution: {integrity: sha512-Fl70vYtsAFb/C06PTS9dZBo7ihau+Tu/DNCk/OyHhea07S+aeMWpFFkUaXRa8fI+ScZbEI8dfSxwY7gxZ9SAVQ==} + + husky@8.0.3: + resolution: {integrity: sha512-+dQSyqPh4x1hlO1swXBiNb2HzTDN1I2IGLQx1GrBuiqFJfoMrnZWwVmatvSiO+Iz8fBUnf+lekwNo4c2LlXItg==} + engines: {node: '>=14'} + hasBin: true + + hyperdyperid@1.2.0: + resolution: {integrity: sha512-Y93lCzHYgGWdrJ66yIktxiaGULYc6oGiABxhcO5AufBeOyoIdZF7bIfLaOrbM0iGIOXQQgxxRrFEnb+Y6w1n4A==} + engines: {node: '>=10.18'} + + iconv-lite@0.4.24: + resolution: {integrity: sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==} + engines: {node: '>=0.10.0'} + + iconv-lite@0.5.2: + resolution: {integrity: sha512-kERHXvpSaB4aU3eANwidg79K8FlrN77m8G9V+0vOR3HYaRifrlwMEpT7ZBJqLSEIHnEgJTHcWK82wwLwwKwtag==} + engines: {node: '>=0.10.0'} + + iconv-lite@0.6.3: + resolution: {integrity: sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==} + engines: {node: '>=0.10.0'} + + iconv-lite@0.7.0: + resolution: {integrity: sha512-cf6L2Ds3h57VVmkZe+Pn+5APsT7FpqJtEhhieDCvrE2MK5Qk9MyffgQyuxQTm6BChfeZNtcOLHp9IcWRVcIcBQ==} + engines: {node: '>=0.10.0'} + + icss-utils@5.1.0: + resolution: {integrity: sha512-soFhflCVWLfRNOPU3iv5Z9VUdT44xFRbzjLsEzSr5AQmgqPMTHdU3PMT1Cf1ssx8fLNJDA1juftYl+PUcv3MqA==} + engines: {node: ^10 || ^12 || >= 14} + peerDependencies: + postcss: ^8.1.0 + + idb@7.1.1: + resolution: {integrity: sha512-gchesWBzyvGHRO9W8tzUWFDycow5gwjvFKfyV9FF32Y7F50yZMp7mP+T2mJIWFx49zicqyC4uefHM17o6xKIVQ==} + + identity-obj-proxy@3.0.0: + resolution: {integrity: sha512-00n6YnVHKrinT9t0d9+5yZC6UBNJANpYEQvL2LlX6Ab9lnmxzIRcEmTPuyGScvl1+jKuCICX1Z0Ab1pPKKdikA==} + engines: {node: '>=4'} + + ieee754@1.2.1: + resolution: {integrity: sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==} + + ignore-by-default@1.0.1: + resolution: {integrity: sha512-Ius2VYcGNk7T90CppJqcIkS5ooHUZyIQK+ClZfMfMNFEF9VSE73Fq+906u/CWu92x4gzZMWOwfFYckPObzdEbA==} + + ignore@5.3.2: + resolution: {integrity: sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==} + engines: {node: '>= 4'} + + immer@11.1.4: + resolution: {integrity: sha512-XREFCPo6ksxVzP4E0ekD5aMdf8WMwmdNaz6vuvxgI40UaEiu6q3p8X52aU6GdyvLY3XXX/8R7JOTXStz/nBbRw==} + + immer@9.0.21: + resolution: {integrity: sha512-bc4NBHqOqSfRW7POMkHd51LvClaeMXpm8dx0e8oE2GORbq5aRK7Bxl4FyzVLdGtLmvLKL7BTDBG5ACQm4HWjTA==} + + immutable@5.1.3: + resolution: {integrity: sha512-+chQdDfvscSF1SJqv2gn4SRO2ZyS3xL3r7IW/wWEEzrzLisnOlKiQu5ytC/BVNcS15C39WT2Hg/bjKjDMcu+zg==} + + import-fresh@3.3.1: + resolution: {integrity: sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==} + engines: {node: '>=6'} + + import-local@3.2.0: + resolution: {integrity: sha512-2SPlun1JUPWoM6t3F0dw0FkCF/jWY8kttcY4f599GLTSjh2OCuuhdTkJQsEcZzBqbXZGKMK2OqW1oZsjtf/gQA==} + engines: {node: '>=8'} + hasBin: true + + imurmurhash@0.1.4: + resolution: {integrity: sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==} + engines: {node: '>=0.8.19'} + + indent-string@4.0.0: + resolution: {integrity: sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==} + engines: {node: '>=8'} + + infer-owner@1.0.4: + resolution: {integrity: sha512-IClj+Xz94+d7irH5qRyfJonOdfTzuDaifE6ZPWfx0N0+/ATZCbuTPq2prFl526urkQd90WyUKIh1DfBQ2hMz9A==} + + inherits@2.0.3: + resolution: {integrity: sha512-x00IRNXNy63jwGkJmzPigoySHbaqpNuzKbBOmzK+g2OdZpQ9w+sxCN+VSB3ja7IAge2OP2qpfxTjeNcyjmW1uw==} + + inherits@2.0.4: + resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==} + + ini@1.3.8: + resolution: {integrity: sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==} + + ini@2.0.0: + resolution: {integrity: sha512-7PnF4oN3CvZF23ADhA5wRaYEQpJ8qygSkbtTXWBeXWXmEVRXK+1ITciHWwHhsjv1TmW0MgacIv6hEi5pX5NQdA==} + engines: {node: '>=10'} + + inline-style-parser@0.1.1: + resolution: {integrity: sha512-7NXolsK4CAS5+xvdj5OMMbI962hU/wvwoxk+LWR9Ek9bVtyuuYScDN6eS0rUm6TxApFpw7CX1o4uJzcd4AyD3Q==} + + internal-slot@1.1.0: + resolution: {integrity: sha512-4gd7VpWNQNB4UKKCFFVcp1AVv+FMOgs9NKzjHKusc8jTMhd5eL1NqQqOpE0KzMds804/yHlglp3uxgluOqAPLw==} + engines: {node: '>= 0.4'} + + internmap@2.0.3: + resolution: {integrity: sha512-5Hh7Y1wQbvY5ooGgPbDaL5iYLAPzMTUrjMulskHLH6wnv/A+1q5rgEaiuqEjB+oxGXIVZs1FF+R/KPN3ZSQYYg==} + engines: {node: '>=12'} + + interpret@1.4.0: + resolution: {integrity: sha512-agE4QfB2Lkp9uICn7BAqoscw4SZP9kTE2hxiFI3jBPmXJfdqiahTbUuKGsMoN2GtqL9AxhYioAcVvgsb1HvRbA==} + engines: {node: '>= 0.10'} + + ioredis@5.8.1: + resolution: {integrity: sha512-Qho8TgIamqEPdgiMadJwzRMW3TudIg6vpg4YONokGDudy4eqRIJtDbVX72pfLBcWxvbn3qm/40TyGUObdW4tLQ==} + engines: {node: '>=12.22.0'} + + ip-address@10.0.1: + resolution: {integrity: sha512-NWv9YLW4PoW2B7xtzaS3NCot75m6nK7Icdv0o3lfMceJVRfSoQwqD4wEH5rLwoKJwUiZ/rfpiVBhnaF0FK4HoA==} + engines: {node: '>= 12'} + + ipaddr.js@1.9.1: + resolution: {integrity: sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==} + engines: {node: '>= 0.10'} + + ipaddr.js@2.2.0: + resolution: {integrity: sha512-Ag3wB2o37wslZS19hZqorUnrnzSkpOVy+IiiDEiTqNubEYpYuHWIf6K4psgN2ZWKExS4xhVCrRVfb/wfW8fWJA==} + engines: {node: '>= 10'} + + is-alphabetical@1.0.4: + resolution: {integrity: sha512-DwzsA04LQ10FHTZuL0/grVDk4rFoVH1pjAToYwBrHSxcrBIGQuXrQMtD5U1b0U2XVgKZCTLLP8u2Qxqhy3l2Vg==} + + is-alphanumerical@1.0.4: + resolution: {integrity: sha512-UzoZUr+XfVz3t3v4KyGEniVL9BDRoQtY7tOyrRybkVNjDFWyo1yhXNGrrBTQxp3ib9BLAWs7k2YKBQsFRkZG9A==} + + is-arguments@1.2.0: + resolution: {integrity: sha512-7bVbi0huj/wrIAOzb8U1aszg9kdi3KN/CyU19CTI7tAoZYEZoL9yCDXpbXN+uPsuWnP02cyug1gleqq+TU+YCA==} + engines: {node: '>= 0.4'} + + is-array-buffer@3.0.5: + resolution: {integrity: sha512-DDfANUiiG2wC1qawP66qlTugJeL5HyzMpfr8lLK+jMQirGzNod0B12cFB/9q838Ru27sBwfw78/rdoU7RERz6A==} + engines: {node: '>= 0.4'} + + is-arrayish@0.2.1: + resolution: {integrity: sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==} + + is-async-function@2.1.1: + resolution: {integrity: sha512-9dgM/cZBnNvjzaMYHVoxxfPj2QXt22Ev7SuuPrs+xav0ukGB0S6d4ydZdEiM48kLx5kDV+QBPrpVnFyefL8kkQ==} + engines: {node: '>= 0.4'} + + is-bigint@1.1.0: + resolution: {integrity: sha512-n4ZT37wG78iz03xPRKJrHTdZbe3IicyucEtdRsV5yglwc3GyUfbAfpSeD0FJ41NbUNSt5wbhqfp1fS+BgnvDFQ==} + engines: {node: '>= 0.4'} + + is-binary-path@2.1.0: + resolution: {integrity: sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==} + engines: {node: '>=8'} + + is-boolean-object@1.2.2: + resolution: {integrity: sha512-wa56o2/ElJMYqjCjGkXri7it5FbebW5usLw/nPmCMs5DeZ7eziSYZhSmPRn0txqeW4LnAmQQU7FgqLpsEFKM4A==} + engines: {node: '>= 0.4'} + + is-buffer@2.0.5: + resolution: {integrity: sha512-i2R6zNFDwgEHJyQUtJEk0XFi1i0dPFn/oqjK3/vPCcDeJvW5NQ83V8QbicfF1SupOaB0h8ntgBC2YiE7dfyctQ==} + engines: {node: '>=4'} + + is-callable@1.2.7: + resolution: {integrity: sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==} + engines: {node: '>= 0.4'} + + is-core-module@2.16.1: + resolution: {integrity: sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==} + engines: {node: '>= 0.4'} + + is-data-view@1.0.2: + resolution: {integrity: sha512-RKtWF8pGmS87i2D6gqQu/l7EYRlVdfzemCJN/P3UOs//x1QE7mfhvzHIApBTRf7axvT6DMGwSwBXYCT0nfB9xw==} + engines: {node: '>= 0.4'} + + is-date-object@1.1.0: + resolution: {integrity: sha512-PwwhEakHVKTdRNVOw+/Gyh0+MzlCl4R6qKvkhuvLtPMggI1WAHt9sOwZxQLSGpUaDnrdyDsomoRgNnCfKNSXXg==} + engines: {node: '>= 0.4'} + + is-decimal@1.0.4: + resolution: {integrity: sha512-RGdriMmQQvZ2aqaQq3awNA6dCGtKpiDFcOzrTWrDAT2MiWrKQVPmxLGHl7Y2nNu6led0kEyoX0enY0qXYsv9zw==} + + is-docker@2.2.1: + resolution: {integrity: sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==} + engines: {node: '>=8'} + hasBin: true + + is-extglob@2.1.1: + resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==} + engines: {node: '>=0.10.0'} + + is-finalizationregistry@1.1.1: + resolution: {integrity: sha512-1pC6N8qWJbWoPtEjgcL2xyhQOP491EQjeUo3qTKcmV8YSDDJrOepfG8pcC7h/QgnQHYSv0mJ3Z/ZWxmatVrysg==} + engines: {node: '>= 0.4'} + + is-fullwidth-code-point@3.0.0: + resolution: {integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==} + engines: {node: '>=8'} + + is-fullwidth-code-point@4.0.0: + resolution: {integrity: sha512-O4L094N2/dZ7xqVdrXhh9r1KODPJpFms8B5sGdJLPy664AgvXsreZUyCQQNItZRDlYug4xStLjNp/sz3HvBowQ==} + engines: {node: '>=12'} + + is-generator-fn@2.1.0: + resolution: {integrity: sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ==} + engines: {node: '>=6'} + + is-generator-function@1.1.2: + resolution: {integrity: sha512-upqt1SkGkODW9tsGNG5mtXTXtECizwtS2kA161M+gJPc1xdb/Ax629af6YrTwcOeQHbewrPNlE5Dx7kzvXTizA==} + engines: {node: '>= 0.4'} + + is-glob@4.0.3: + resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==} + engines: {node: '>=0.10.0'} + + is-hexadecimal@1.0.4: + resolution: {integrity: sha512-gyPJuv83bHMpocVYoqof5VDiZveEoGoFL8m3BXNb2VW8Xs+rz9kqO8LOQ5DH6EsuvilT1ApazU0pyl+ytbPtlw==} + + is-installed-globally@0.4.0: + resolution: {integrity: sha512-iwGqO3J21aaSkC7jWnHP/difazwS7SFeIqxv6wEtLU8Y5KlzFTjyqcSIT0d8s4+dDhKytsk9PJZ2BkS5eZwQRQ==} + engines: {node: '>=10'} + + is-lambda@1.0.1: + resolution: {integrity: sha512-z7CMFGNrENq5iFB9Bqo64Xk6Y9sg+epq1myIcdHaGnbMTYOxvzsEtdYqQUylB7LxfkvgrrjP32T6Ywciio9UIQ==} + + is-map@2.0.3: + resolution: {integrity: sha512-1Qed0/Hr2m+YqxnM09CjA2d/i6YZNfF6R2oRAOj36eUdS6qIV/huPJNSEpKbupewFs+ZsJlxsjjPbc0/afW6Lw==} + engines: {node: '>= 0.4'} + + is-module@1.0.0: + resolution: {integrity: sha512-51ypPSPCoTEIN9dy5Oy+h4pShgJmPCygKfyRCISBI+JoWT/2oJvK8QPxmwv7b/p239jXrm9M1mlQbyKJ5A152g==} + + is-negative-zero@2.0.3: + resolution: {integrity: sha512-5KoIu2Ngpyek75jXodFvnafB6DJgr3u8uuK0LEZJjrU19DrMD3EVERaR8sjz8CCGgpZvxPl9SuE1GMVPFHx1mw==} + engines: {node: '>= 0.4'} + + is-number-object@1.1.1: + resolution: {integrity: sha512-lZhclumE1G6VYD8VHe35wFaIif+CTy5SJIi5+3y4psDgWu4wPDoBhF8NxUOinEc7pHgiTsT6MaBb92rKhhD+Xw==} + engines: {node: '>= 0.4'} + + is-number@7.0.0: + resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==} + engines: {node: '>=0.12.0'} + + is-obj@1.0.1: + resolution: {integrity: sha512-l4RyHgRqGN4Y3+9JHVrNqO+tN0rV5My76uW5/nuO4K1b6vw5G8d/cmFjP9tRfEsdhZNt0IFdZuK/c2Vr4Nb+Qg==} + engines: {node: '>=0.10.0'} + + is-path-inside@3.0.3: + resolution: {integrity: sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==} + engines: {node: '>=8'} + + is-plain-obj@4.1.0: + resolution: {integrity: sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg==} + engines: {node: '>=12'} + + is-plain-object@5.0.0: + resolution: {integrity: sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q==} + engines: {node: '>=0.10.0'} + + is-potential-custom-element-name@1.0.1: + resolution: {integrity: sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==} + + is-property@1.0.2: + resolution: {integrity: sha512-Ks/IoX00TtClbGQr4TWXemAnktAQvYB7HzcCxDGqEZU6oCmb2INHuOoKxbtR+HFkmYWBKv/dOZtGRiAjDhj92g==} + + is-regex@1.2.1: + resolution: {integrity: sha512-MjYsKHO5O7mCsmRGxWcLWheFqN9DJ/2TmngvjKXihe6efViPqc274+Fx/4fYj/r03+ESvBdTXK0V6tA3rgez1g==} + engines: {node: '>= 0.4'} + + is-regexp@1.0.0: + resolution: {integrity: sha512-7zjFAPO4/gwyQAAgRRmqeEeyIICSdmCqa3tsVHMdBzaXXRiqopZL4Cyghg/XulGWrtABTpbnYYzzIRffLkP4oA==} + engines: {node: '>=0.10.0'} + + is-root@2.1.0: + resolution: {integrity: sha512-AGOriNp96vNBd3HtU+RzFEc75FfR5ymiYv8E553I71SCeXBiMsVDUtdio1OEFvrPyLIQ9tVR5RxXIFe5PUFjMg==} + engines: {node: '>=6'} + + is-set@2.0.3: + resolution: {integrity: sha512-iPAjerrse27/ygGLxw+EBR9agv9Y6uLeYVJMu+QNCoouJ1/1ri0mGrcWpfCqFZuzzx3WjtwxG098X+n4OuRkPg==} + engines: {node: '>= 0.4'} + + is-shared-array-buffer@1.0.4: + resolution: {integrity: sha512-ISWac8drv4ZGfwKl5slpHG9OwPNty4jOWPRIhBpxOoD+hqITiwuipOQ2bNthAzwA3B4fIjO4Nln74N0S9byq8A==} + engines: {node: '>= 0.4'} + + is-stream@2.0.1: + resolution: {integrity: sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==} + engines: {node: '>=8'} + + is-stream@3.0.0: + resolution: {integrity: sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + + is-string@1.1.1: + resolution: {integrity: sha512-BtEeSsoaQjlSPBemMQIrY1MY0uM6vnS1g5fmufYOtnxLGUZM2178PKbhsk7Ffv58IX+ZtcvoGwccYsh0PglkAA==} + engines: {node: '>= 0.4'} + + is-symbol@1.1.1: + resolution: {integrity: sha512-9gGx6GTtCQM73BgmHQXfDmLtfjjTUDSyoxTCbp5WtoixAhfgsDirWIcVQ/IHpvI5Vgd5i/J5F7B9cN/WlVbC/w==} + engines: {node: '>= 0.4'} + + is-typed-array@1.1.15: + resolution: {integrity: sha512-p3EcsicXjit7SaskXHs1hA91QxgTw46Fv6EFKKGS5DRFLD8yKnohjF3hxoju94b/OcMZoQukzpPpBE9uLVKzgQ==} + engines: {node: '>= 0.4'} + + is-typedarray@1.0.0: + resolution: {integrity: sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA==} + + is-unicode-supported@0.1.0: + resolution: {integrity: sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==} + engines: {node: '>=10'} + + is-weakmap@2.0.2: + resolution: {integrity: sha512-K5pXYOm9wqY1RgjpL3YTkF39tni1XajUIkawTLUo9EZEVUFga5gSQJF8nNS7ZwJQ02y+1YCNYcMh+HIf1ZqE+w==} + engines: {node: '>= 0.4'} + + is-weakref@1.1.1: + resolution: {integrity: sha512-6i9mGWSlqzNMEqpCp93KwRS1uUOodk2OJ6b+sq7ZPDSy2WuI5NFIxp/254TytR8ftefexkWn5xNiHUNpPOfSew==} + engines: {node: '>= 0.4'} + + is-weakset@2.0.4: + resolution: {integrity: sha512-mfcwb6IzQyOKTs84CQMrOwW4gQcaTOAWJ0zzJCl2WSPDrWk/OzDaImWFH3djXhb24g4eudZfLRozAvPGw4d9hQ==} + engines: {node: '>= 0.4'} + + is-wsl@2.2.0: + resolution: {integrity: sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==} + engines: {node: '>=8'} + + isarray@1.0.0: + resolution: {integrity: sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==} + + isarray@2.0.5: + resolution: {integrity: sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==} + + isexe@2.0.0: + resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} + + isstream@0.1.2: + resolution: {integrity: sha512-Yljz7ffyPbrLpLngrMtZ7NduUgVvi6wG9RJ9IUcyCd59YQ911PBJphODUcbOVbqYfxe1wuYf/LJ8PauMRwsM/g==} + + istanbul-lib-coverage@3.2.2: + resolution: {integrity: sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg==} + engines: {node: '>=8'} + + istanbul-lib-instrument@5.2.1: + resolution: {integrity: sha512-pzqtp31nLv/XFOzXGuvhCb8qhjmTVo5vjVk19XE4CRlSWz0KoeJ3bw9XsA7nOp9YBf4qHjwBxkDzKcME/J29Yg==} + engines: {node: '>=8'} + + istanbul-lib-report@3.0.1: + resolution: {integrity: sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw==} + engines: {node: '>=10'} + + istanbul-lib-source-maps@4.0.1: + resolution: {integrity: sha512-n3s8EwkdFIJCG3BPKBYvskgXGoy88ARzvegkitk60NxRdwltLOTaH7CUiMRXvwYorl0Q712iEjcWB+fK/MrWVw==} + engines: {node: '>=10'} + + istanbul-reports@3.2.0: + resolution: {integrity: sha512-HGYWWS/ehqTV3xN10i23tkPkpH46MLCIMFNCaaKNavAXTF1RkqxawEPtnjnGZ6XKSInBKkiOA5BKS+aZiY3AvA==} + engines: {node: '>=8'} + + iterator.prototype@1.1.5: + resolution: {integrity: sha512-H0dkQoCa3b2VEeKQBOxFph+JAbcrQdE7KC0UkqwpLmv2EC4P41QXP+rqo9wYodACiG5/WM5s9oDApTU8utwj9g==} + engines: {node: '>= 0.4'} + + jackspeak@3.4.3: + resolution: {integrity: sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==} + + jake@10.9.4: + resolution: {integrity: sha512-wpHYzhxiVQL+IV05BLE2Xn34zW1S223hvjtqk0+gsPrwd/8JNLXJgZZM/iPFsYc1xyphF+6M6EvdE5E9MBGkDA==} + engines: {node: '>=10'} + hasBin: true + + jest-changed-files@27.5.1: + resolution: {integrity: sha512-buBLMiByfWGCoMsLLzGUUSpAmIAGnbR2KJoMN10ziLhOLvP4e0SlypHnAel8iqQXTrcbmfEY9sSqae5sgUsTvw==} + engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0} + + jest-circus@27.5.1: + resolution: {integrity: sha512-D95R7x5UtlMA5iBYsOHFFbMD/GVA4R/Kdq15f7xYWUfWHBto9NYRsOvnSauTgdF+ogCpJ4tyKOXhUifxS65gdw==} + engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0} + + jest-cli@27.5.1: + resolution: {integrity: sha512-Hc6HOOwYq4/74/c62dEE3r5elx8wjYqxY0r0G/nFrLDPMFRu6RA/u8qINOIkvhxG7mMQ5EJsOGfRpI8L6eFUVw==} + engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0} + hasBin: true + peerDependencies: + node-notifier: ^8.0.1 || ^9.0.0 || ^10.0.0 + peerDependenciesMeta: + node-notifier: + optional: true + + jest-config@27.5.1: + resolution: {integrity: sha512-5sAsjm6tGdsVbW9ahcChPAFCk4IlkQUknH5AvKjuLTSlcO/wCZKyFdn7Rg0EkC+OGgWODEy2hDpWB1PgzH0JNA==} + engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0} + peerDependencies: + ts-node: '>=9.0.0' + peerDependenciesMeta: + ts-node: + optional: true + + jest-diff@27.5.1: + resolution: {integrity: sha512-m0NvkX55LDt9T4mctTEgnZk3fmEg3NRYutvMPWM/0iPnkFj2wIeF45O1718cMSOFO1vINkqmxqD8vE37uTEbqw==} + engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0} + + jest-diff@30.2.0: + resolution: {integrity: sha512-dQHFo3Pt4/NLlG5z4PxZ/3yZTZ1C7s9hveiOj+GCN+uT109NC2QgsoVZsVOAvbJ3RgKkvyLGXZV9+piDpWbm6A==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} + + jest-docblock@27.5.1: + resolution: {integrity: sha512-rl7hlABeTsRYxKiUfpHrQrG4e2obOiTQWfMEH3PxPjOtdsfLQO4ReWSZaQ7DETm4xu07rl4q/h4zcKXyU0/OzQ==} + engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0} + + jest-each@27.5.1: + resolution: {integrity: sha512-1Ff6p+FbhT/bXQnEouYy00bkNSY7OUpfIcmdl8vZ31A1UUaurOLPA8a8BbJOF2RDUElwJhmeaV7LnagI+5UwNQ==} + engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0} + + jest-environment-jsdom@27.5.1: + resolution: {integrity: sha512-TFBvkTC1Hnnnrka/fUb56atfDtJ9VMZ94JkjTbggl1PEpwrYtUBKMezB3inLmWqQsXYLcMwNoDQwoBTAvFfsfw==} + engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0} + + jest-environment-node@27.5.1: + resolution: {integrity: sha512-Jt4ZUnxdOsTGwSRAfKEnE6BcwsSPNOijjwifq5sDFSA2kesnXTvNqKHYgM0hDq3549Uf/KzdXNYn4wMZJPlFLw==} + engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0} + + jest-get-type@27.5.1: + resolution: {integrity: sha512-2KY95ksYSaK7DMBWQn6dQz3kqAf3BB64y2udeG+hv4KfSOb9qwcYQstTJc1KCbsix+wLZWZYN8t7nwX3GOBLRw==} + engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0} + + jest-haste-map@27.5.1: + resolution: {integrity: sha512-7GgkZ4Fw4NFbMSDSpZwXeBiIbx+t/46nJ2QitkOjvwPYyZmqttu2TDSimMHP1EkPOi4xUZAN1doE5Vd25H4Jng==} + engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0} + + jest-jasmine2@27.5.1: + resolution: {integrity: sha512-jtq7VVyG8SqAorDpApwiJJImd0V2wv1xzdheGHRGyuT7gZm6gG47QEskOlzsN1PG/6WNaCo5pmwMHDf3AkG2pQ==} + engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0} + + jest-leak-detector@27.5.1: + resolution: {integrity: sha512-POXfWAMvfU6WMUXftV4HolnJfnPOGEu10fscNCA76KBpRRhcMN2c8d3iT2pxQS3HLbA+5X4sOUPzYO2NUyIlHQ==} + engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0} + + jest-matcher-utils@27.5.1: + resolution: {integrity: sha512-z2uTx/T6LBaCoNWNFWwChLBKYxTMcGBRjAt+2SbP929/Fflb9aa5LGma654Rz8z9HLxsrUaYzxE9T/EFIL/PAw==} + engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0} + + jest-matcher-utils@30.2.0: + resolution: {integrity: sha512-dQ94Nq4dbzmUWkQ0ANAWS9tBRfqCrn0bV9AMYdOi/MHW726xn7eQmMeRTpX2ViC00bpNaWXq+7o4lIQ3AX13Hg==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} + + jest-message-util@27.5.1: + resolution: {integrity: sha512-rMyFe1+jnyAAf+NHwTclDz0eAaLkVDdKVHHBFWsBWHnnh5YeJMNWWsv7AbFYXfK3oTqvL7VTWkhNLu1jX24D+g==} + engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0} + + jest-message-util@28.1.3: + resolution: {integrity: sha512-PFdn9Iewbt575zKPf1286Ht9EPoJmYT7P0kY+RibeYZ2XtOr53pDLEFoTWXbd1h4JiGiWpTBC84fc8xMXQMb7g==} + engines: {node: ^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0} + + jest-message-util@30.2.0: + resolution: {integrity: sha512-y4DKFLZ2y6DxTWD4cDe07RglV88ZiNEdlRfGtqahfbIjfsw1nMCPx49Uev4IA/hWn3sDKyAnSPwoYSsAEdcimw==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} + + jest-mock@27.5.1: + resolution: {integrity: sha512-K4jKbY1d4ENhbrG2zuPWaQBvDly+iZ2yAW+T1fATN78hc0sInwn7wZB8XtlNnvHug5RMwV897Xm4LqmPM4e2Og==} + engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0} + + jest-mock@30.2.0: + resolution: {integrity: sha512-JNNNl2rj4b5ICpmAcq+WbLH83XswjPbjH4T7yvGzfAGCPh1rw+xVNbtk+FnRslvt9lkCcdn9i1oAoKUuFsOxRw==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} + + jest-pnp-resolver@1.2.3: + resolution: {integrity: sha512-+3NpwQEnRoIBtx4fyhblQDPgJI0H1IEIkX7ShLUjPGA7TtUTvI1oiKi3SR4oBR0hQhQR80l4WAe5RrXBwWMA8w==} + engines: {node: '>=6'} + peerDependencies: + jest-resolve: '*' + peerDependenciesMeta: + jest-resolve: + optional: true + + jest-regex-util@27.5.1: + resolution: {integrity: sha512-4bfKq2zie+x16okqDXjXn9ql2B0dScQu+vcwe4TvFVhkVyuWLqpZrZtXxLLWoXYgn0E87I6r6GRYHF7wFZBUvg==} + engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0} + + jest-regex-util@28.0.2: + resolution: {integrity: sha512-4s0IgyNIy0y9FK+cjoVYoxamT7Zeo7MhzqRGx7YDYmaQn1wucY9rotiGkBzzcMXTtjrCAP/f7f+E0F7+fxPNdw==} + engines: {node: ^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0} + + jest-regex-util@30.0.1: + resolution: {integrity: sha512-jHEQgBXAgc+Gh4g0p3bCevgRCVRkB4VB70zhoAE48gxeSr1hfUOsM/C2WoJgVL7Eyg//hudYENbm3Ne+/dRVVA==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} + + jest-resolve-dependencies@27.5.1: + resolution: {integrity: sha512-QQOOdY4PE39iawDn5rzbIePNigfe5B9Z91GDD1ae/xNDlu9kaat8QQ5EKnNmVWPV54hUdxCVwwj6YMgR2O7IOg==} + engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0} + + jest-resolve@27.5.1: + resolution: {integrity: sha512-FFDy8/9E6CV83IMbDpcjOhumAQPDyETnU2KZ1O98DwTnz8AOBsW/Xv3GySr1mOZdItLR+zDZ7I/UdTFbgSOVCw==} + engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0} + + jest-runner@27.5.1: + resolution: {integrity: sha512-g4NPsM4mFCOwFKXO4p/H/kWGdJp9V8kURY2lX8Me2drgXqG7rrZAx5kv+5H7wtt/cdFIjhqYx1HrlqWHaOvDaQ==} + engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0} + + jest-runtime@27.5.1: + resolution: {integrity: sha512-o7gxw3Gf+H2IGt8fv0RiyE1+r83FJBRruoA+FXrlHw6xEyBsU8ugA6IPfTdVyA0w8HClpbK+DGJxH59UrNMx8A==} + engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0} + + jest-serializer@27.5.1: + resolution: {integrity: sha512-jZCyo6iIxO1aqUxpuBlwTDMkzOAJS4a3eYz3YzgxxVQFwLeSA7Jfq5cbqCY+JLvTDrWirgusI/0KwxKMgrdf7w==} + engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0} + + jest-snapshot@27.5.1: + resolution: {integrity: sha512-yYykXI5a0I31xX67mgeLw1DZ0bJB+gpq5IpSuCAoyDi0+BhgU/RIrL+RTzDmkNTchvDFWKP8lp+w/42Z3us5sA==} + engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0} + + jest-util@27.5.1: + resolution: {integrity: sha512-Kv2o/8jNvX1MQ0KGtw480E/w4fBCDOnH6+6DmeKi6LZUIlKA5kwY0YNdlzaWTiVgxqAqik11QyxDOKk543aKXw==} + engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0} + + jest-util@28.1.3: + resolution: {integrity: sha512-XdqfpHwpcSRko/C35uLYFM2emRAltIIKZiJ9eAmhjsj0CqZMa0p1ib0R5fWIqGhn1a103DebTbpqIaP1qCQ6tQ==} + engines: {node: ^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0} + + jest-util@30.2.0: + resolution: {integrity: sha512-QKNsM0o3Xe6ISQU869e+DhG+4CK/48aHYdJZGlFQVTjnbvgpcKyxpzk29fGiO7i/J8VENZ+d2iGnSsvmuHywlA==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} + + jest-validate@27.5.1: + resolution: {integrity: sha512-thkNli0LYTmOI1tDB3FI1S1RTp/Bqyd9pTarJwL87OIBFuqEb5Apv5EaApEudYg4g86e3CT6kM0RowkhtEnCBQ==} + engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0} + + jest-watch-typeahead@1.1.0: + resolution: {integrity: sha512-Va5nLSJTN7YFtC2jd+7wsoe1pNe5K4ShLux/E5iHEwlB9AxaxmggY7to9KUqKojhaJw3aXqt5WAb4jGPOolpEw==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + peerDependencies: + jest: ^27.0.0 || ^28.0.0 + + jest-watcher@27.5.1: + resolution: {integrity: sha512-z676SuD6Z8o8qbmEGhoEUFOM1+jfEiL3DXHK/xgEiG2EyNYfFG60jluWcupY6dATjfEsKQuibReS1djInQnoVw==} + engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0} + + jest-watcher@28.1.3: + resolution: {integrity: sha512-t4qcqj9hze+jviFPUN3YAtAEeFnr/azITXQEMARf5cMwKY2SMBRnCQTXLixTl20OR6mLh9KLMrgVJgJISym+1g==} + engines: {node: ^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0} + + jest-worker@26.6.2: + resolution: {integrity: sha512-KWYVV1c4i+jbMpaBC+U++4Va0cp8OisU185o73T1vo99hqi7w8tSJfUXYswwqqrjzwxa6KpRK54WhPvwf5w6PQ==} + engines: {node: '>= 10.13.0'} + + jest-worker@27.5.1: + resolution: {integrity: sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg==} + engines: {node: '>= 10.13.0'} + + jest-worker@28.1.3: + resolution: {integrity: sha512-CqRA220YV/6jCo8VWvAt1KKx6eek1VIHMPeLEbpcfSfkEeWyBNppynM/o6q+Wmw+sOhos2ml34wZbSX3G13//g==} + engines: {node: ^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0} + + jest@27.5.1: + resolution: {integrity: sha512-Yn0mADZB89zTtjkPJEXwrac3LHudkQMR+Paqa8uxJHCBr9agxztUifWCyiYrjhMPBoUVBjyny0I7XH6ozDr7QQ==} + engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0} + hasBin: true + peerDependencies: + node-notifier: ^8.0.1 || ^9.0.0 || ^10.0.0 + peerDependenciesMeta: + node-notifier: + optional: true + + jiti@1.21.7: + resolution: {integrity: sha512-/imKNG4EbWNrVjoNC/1H5/9GFy+tqjGBHCaSsN+P2RnPqjsLmv6UD3Ej+Kj8nBWaRAwyk7kK5ZUc+OEatnTR3A==} + hasBin: true + + joi@18.0.1: + resolution: {integrity: sha512-IiQpRyypSnLisQf3PwuN2eIHAsAIGZIrLZkd4zdvIar2bDyhM91ubRjy8a3eYablXsh9BeI/c7dmPYHca5qtoA==} + engines: {node: '>= 20'} + + js-tokens@4.0.0: + resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} + + js-yaml@3.14.1: + resolution: {integrity: sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==} + hasBin: true + + js-yaml@4.1.0: + resolution: {integrity: sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==} + hasBin: true + + jsbn@0.1.1: + resolution: {integrity: sha512-UVU9dibq2JcFWxQPA6KCqj5O42VOmAY3zQUfEKxU0KpTGXwNoCjkX1e13eHNvw/xPynt6pU0rZ1htjWTNTSXsg==} + + jsdom@16.7.0: + resolution: {integrity: sha512-u9Smc2G1USStM+s/x1ru5Sxrl6mPYCbByG1U/hUmqaVsm4tbNyS7CicOSRyuGQYZhTu0h84qkZZQ/I+dzizSVw==} + engines: {node: '>=10'} + peerDependencies: + canvas: ^2.5.0 + peerDependenciesMeta: + canvas: + optional: true + + jsdom@20.0.3: + resolution: {integrity: sha512-SYhBvTh89tTfCD/CRdSOm13mOBa42iTaTyfyEWBdKcGdPxPtLFBXuHR8XHb33YNYaP+lLbmSvBTsnoesCNJEsQ==} + engines: {node: '>=14'} + peerDependencies: + canvas: ^2.5.0 + peerDependenciesMeta: + canvas: + optional: true + + jsesc@3.1.0: + resolution: {integrity: sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==} + engines: {node: '>=6'} + hasBin: true + + json-buffer@3.0.1: + resolution: {integrity: sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==} + + json-parse-even-better-errors@2.3.1: + resolution: {integrity: sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==} + + json-schema-traverse@0.4.1: + resolution: {integrity: sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==} + + json-schema-traverse@1.0.0: + resolution: {integrity: sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==} + + json-schema@0.4.0: + resolution: {integrity: sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA==} + + json-stable-stringify-without-jsonify@1.0.1: + resolution: {integrity: sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==} + + json-stringify-safe@5.0.1: + resolution: {integrity: sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA==} + + json5@2.2.3: + resolution: {integrity: sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==} + engines: {node: '>=6'} + hasBin: true + + jsonfile@6.2.0: + resolution: {integrity: sha512-FGuPw30AdOIUTRMC2OMRtQV+jkVj2cfPqSeWXv1NEAJ1qZ5zb1X6z1mFhbfOB/iy3ssJCD+3KuZ8r8C3uVFlAg==} + + jsonpath@1.1.1: + resolution: {integrity: sha512-l6Cg7jRpixfbgoWgkrl77dgEj8RPvND0wMH6TwQmi9Qs4TFfS9u5cUFnbeKTwj5ga5Y3BTGGNI28k117LJ009w==} + + jsonpointer@5.0.1: + resolution: {integrity: sha512-p/nXbhSEcu3pZRdkW1OfJhpsVtW1gd4Wa1fnQc9YLiTfAjn0312eMKimbdIQzuZl9aa9xUGaRlP9T/CJE/ditQ==} + engines: {node: '>=0.10.0'} + + jsprim@2.0.2: + resolution: {integrity: sha512-gqXddjPqQ6G40VdnI6T6yObEC+pDNvyP95wdQhkWkg7crHH3km5qP1FsOXEkzEQwnz6gz5qGTn1c2Y52wP3OyQ==} + engines: {'0': node >=0.6.0} + + jsx-ast-utils@3.3.5: + resolution: {integrity: sha512-ZZow9HBI5O6EPgSJLUb8n2NKgmVWTwCvHGwFuJlMjvLFqlGG6pjirPhtdsseaLZjSibD8eegzmYpUZwoIlj2cQ==} + engines: {node: '>=4.0'} + + katex@0.16.25: + resolution: {integrity: sha512-woHRUZ/iF23GBP1dkDQMh1QBad9dmr8/PAwNA54VrSOVYgI12MAcE14TqnDdQOdzyEonGzMepYnqBMYdsoAr8Q==} + hasBin: true + + keycloak-js@26.2.1: + resolution: {integrity: sha512-bZt6fQj/TLBAmivXSxSlqAJxBx/knNZDQGJIW4ensGYGN4N6tUKV8Zj3Y7/LOV8eIpvWsvqV70fbACihK8Ze0Q==} + + keyv@4.5.4: + resolution: {integrity: sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==} + + kill-port@2.0.1: + resolution: {integrity: sha512-e0SVOV5jFo0mx8r7bS29maVWp17qGqLBZ5ricNSajON6//kmb7qqqNnml4twNE8Dtj97UQD+gNFOaipS/q1zzQ==} + hasBin: true + + kind-of@6.0.3: + resolution: {integrity: sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==} + engines: {node: '>=0.10.0'} + + kleur@3.0.3: + resolution: {integrity: sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==} + engines: {node: '>=6'} + + kleur@4.1.5: + resolution: {integrity: sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ==} + engines: {node: '>=6'} + + klona@2.0.6: + resolution: {integrity: sha512-dhG34DXATL5hSxJbIexCft8FChFXtmskoZYnoPWjXQuebWYCNkVeV3KkGegCK9CP1oswI/vQibS2GY7Em/sJJA==} + engines: {node: '>= 8'} + + kuler@2.0.0: + resolution: {integrity: sha512-Xq9nH7KlWZmXAtodXDDRE7vs6DU1gTU8zYDHDiWLSip45Egwq3plLHzPn27NgvzL2r1LMPC1vdqh98sQxtqj4A==} + + language-subtag-registry@0.3.23: + resolution: {integrity: sha512-0K65Lea881pHotoGEa5gDlMxt3pctLi2RplBb7Ezh4rRdLEOtgi7n4EwK9lamnUCkKBqaeKRVebTq6BAxSkpXQ==} + + language-tags@1.0.9: + resolution: {integrity: sha512-MbjN408fEndfiQXbFQ1vnd+1NoLDsnQW41410oQBXiyXDMYH5z505juWa4KUE1LqxRC7DgOgZDbKLxHIwm27hA==} + engines: {node: '>=0.10'} + + launch-editor@2.11.1: + resolution: {integrity: sha512-SEET7oNfgSaB6Ym0jufAdCeo3meJVeCaaDyzRygy0xsp2BFKCprcfHljTq4QkzTLUxEKkFK6OK4811YM2oSrRg==} + + lazy-ass@1.6.0: + resolution: {integrity: sha512-cc8oEVoctTvsFZ/Oje/kGnHbpWHYBe8IAJe4C0QNc3t8uM/0Y8+erSz/7Y1ALuXTEZTMvxXwO6YbX1ey3ujiZw==} + engines: {node: '> 0.8'} + + lazystream@1.0.1: + resolution: {integrity: sha512-b94GiNHQNy6JNTrt5w6zNyffMrNkXZb3KTkCZJb2V1xaEGCk093vkZ2jk3tpaeP33/OiXC+WvK9AxUebnf5nbw==} + engines: {node: '>= 0.6.3'} + + leven@3.1.0: + resolution: {integrity: sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==} + engines: {node: '>=6'} + + levn@0.3.0: + resolution: {integrity: sha512-0OO4y2iOHix2W6ujICbKIaEQXvFQHue65vUG3pb5EUomzPI90z9hsA1VsO/dbIIpC53J8gxM9Q4Oho0jrCM/yA==} + engines: {node: '>= 0.8.0'} + + levn@0.4.1: + resolution: {integrity: sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==} + engines: {node: '>= 0.8.0'} + + lilconfig@2.1.0: + resolution: {integrity: sha512-utWOt/GHzuUxnLKxB6dk81RoOeoNeHgbrXiuGk4yyF5qlRz+iIVWu56E2fqGHFrXz0QNUhLB/8nKqvRH66JKGQ==} + engines: {node: '>=10'} + + lilconfig@3.1.3: + resolution: {integrity: sha512-/vlFKAoH5Cgt3Ie+JLhRbwOsCQePABiU3tJ1egGvyQ+33R/vcwM2Zl2QR/LzjsBeItPt3oSVXapn+m4nQDvpzw==} + engines: {node: '>=14'} + + lines-and-columns@1.2.4: + resolution: {integrity: sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==} + + lint-staged@13.3.0: + resolution: {integrity: sha512-mPRtrYnipYYv1FEE134ufbWpeggNTo+O/UPzngoaKzbzHAthvR55am+8GfHTnqNRQVRRrYQLGW9ZyUoD7DsBHQ==} + engines: {node: ^16.14.0 || >=18.0.0} + hasBin: true + + listr2@3.14.0: + resolution: {integrity: sha512-TyWI8G99GX9GjE54cJ+RrNMcIFBfwMPxc3XTFiAYGN4s10hWROGtOg7+O6u6LE3mNkyld7RSLE6nrKBvTfcs3g==} + engines: {node: '>=10.0.0'} + peerDependencies: + enquirer: '>= 2.3.0 < 3' + peerDependenciesMeta: + enquirer: + optional: true + + listr2@6.6.1: + resolution: {integrity: sha512-+rAXGHh0fkEWdXBmX+L6mmfmXmXvDGEKzkjxO+8mP3+nI/r/CWznVBvsibXdxda9Zz0OW2e2ikphN3OwCT/jSg==} + engines: {node: '>=16.0.0'} + peerDependencies: + enquirer: '>= 2.3.0 < 3' + peerDependenciesMeta: + enquirer: + optional: true + + loader-runner@4.3.1: + resolution: {integrity: sha512-IWqP2SCPhyVFTBtRcgMHdzlf9ul25NwaFx4wCEH/KjAXuuHY4yNjvPXsBokp8jCB936PyWRaPKUNh8NvylLp2Q==} + engines: {node: '>=6.11.5'} + + loader-utils@2.0.4: + resolution: {integrity: sha512-xXqpXoINfFhgua9xiqD8fPFHgkoq1mmmpE92WlDbm9rNRd/EbRb+Gqf908T2DMfuHjjJlksiK2RbHVOdD/MqSw==} + engines: {node: '>=8.9.0'} + + loader-utils@3.3.1: + resolution: {integrity: sha512-FMJTLMXfCLMLfJxcX9PFqX5qD88Z5MRGaZCVzfuqeZSPsyiBzs+pahDQjbIWz2QIzPZz0NX9Zy4FX3lmK6YHIg==} + engines: {node: '>= 12.13.0'} + + locate-path@3.0.0: + resolution: {integrity: sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==} + engines: {node: '>=6'} + + locate-path@5.0.0: + resolution: {integrity: sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==} + engines: {node: '>=8'} + + locate-path@6.0.0: + resolution: {integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==} + engines: {node: '>=10'} + + lodash-es@4.17.21: + resolution: {integrity: sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw==} + + lodash.camelcase@4.3.0: + resolution: {integrity: sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA==} + + lodash.curry@4.1.1: + resolution: {integrity: sha512-/u14pXGviLaweY5JI0IUzgzF2J6Ne8INyzAZjImcryjgkZ+ebruBxy2/JaOOkTqScddcYtakjhSaeemV8lR0tA==} + + lodash.debounce@4.0.8: + resolution: {integrity: sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==} + + lodash.defaults@4.2.0: + resolution: {integrity: sha512-qjxPLHd3r5DnsdGacqOMU6pb/avJzdh9tFX2ymgoZE27BmjXrNy/y4LoaiTeAb+O3gL8AfpJGtqfX/ae2leYYQ==} + + lodash.flow@3.5.0: + resolution: {integrity: sha512-ff3BX/tSioo+XojX4MOsOMhJw0nZoUEF011LX8g8d3gvjVbxd89cCio4BCXronjxcTUIJUoqKEUA+n4CqvvRPw==} + + lodash.isarguments@3.1.0: + resolution: {integrity: sha512-chi4NHZlZqZD18a0imDHnZPrDeBbTtVN7GXMwuGdRH9qotxAjYs3aVLKc7zNOG9eddR5Ksd8rvFEBc9SsggPpg==} + + lodash.memoize@4.1.2: + resolution: {integrity: sha512-t7j+NzmgnQzTAYXcsHYLgimltOV1MXHtlOWf6GjL9Kj8GK5FInw5JotxvbOs+IvV1/Dzo04/fCGfLVs7aXb4Ag==} + + lodash.merge@4.6.2: + resolution: {integrity: sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==} + + lodash.once@4.1.1: + resolution: {integrity: sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg==} + + lodash.sortby@4.7.0: + resolution: {integrity: sha512-HDWXG8isMntAyRF5vZ7xKuEvOhT4AhlRt/3czTSjvGUxjYCBVRQY48ViDHyfYz9VIoBkW4TMGQNapx+l3RUwdA==} + + lodash.uniq@4.5.0: + resolution: {integrity: sha512-xfBaXQd9ryd9dlSDvnvI0lvxfLJlYAZzXomUYzLKtUeOQvOP5piqAWuGtrhWeqaXK9hhoM/iyJc5AV+XfsX3HQ==} + + lodash@4.17.21: + resolution: {integrity: sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==} + + log-symbols@4.1.0: + resolution: {integrity: sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==} + engines: {node: '>=10'} + + log-update@4.0.0: + resolution: {integrity: sha512-9fkkDevMefjg0mmzWFBW8YkFP91OrizzkW3diF7CpG+S2EYdy4+TVfGwz1zeF8x7hCx1ovSPTOE9Ngib74qqUg==} + engines: {node: '>=10'} + + log-update@5.0.1: + resolution: {integrity: sha512-5UtUDQ/6edw4ofyljDNcOVJQ4c7OjDro4h3y8e1GQL5iYElYclVHJ3zeWchylvMaKnDbDilC8irOVyexnA/Slw==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + + logform@2.7.0: + resolution: {integrity: sha512-TFYA4jnP7PVbmlBIfhlSe+WKxs9dklXMTEGcBCIvLhE/Tn3H6Gk1norupVW7m5Cnd4bLcr08AytbyV/xj7f/kQ==} + engines: {node: '>= 12.0.0'} + + long@5.3.2: + resolution: {integrity: sha512-mNAgZ1GmyNhD7AuqnTG3/VQ26o760+ZYBPKjPvugO8+nLbYfX6TVpJPseBvopbdY+qpZ/lKUnmEc1LeZYS3QAA==} + + longest-streak@3.1.0: + resolution: {integrity: sha512-9Ri+o0JYgehTaVBBDoMqIl8GXtbWg711O3srftcHhZ0dqnETqLaoIK0x17fUw9rFSlK/0NlsKe0Ahhyl5pXE2g==} + + loose-envify@1.4.0: + resolution: {integrity: sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==} + hasBin: true + + lower-case@2.0.2: + resolution: {integrity: sha512-7fm3l3NAF9WfN6W3JOmf5drwpVqX78JtoGJ3A6W0a6ZnldM41w2fV5D490psKFTpMds8TJse/eHLFFsNHHjHgg==} + + lowlight@1.20.0: + resolution: {integrity: sha512-8Ktj+prEb1RoCPkEOrPMYUN/nCggB7qAWe3a7OpMjWQkh3l2RD5wKRQ+o8Q8YuI9RG/xs95waaI/E6ym/7NsTw==} + + lowlight@3.3.0: + resolution: {integrity: sha512-0JNhgFoPvP6U6lE/UdVsSq99tn6DhjjpAj5MxG49ewd2mOBVtwWYIT8ClyABhq198aXXODMU6Ox8DrGy/CpTZQ==} + + lru-cache@10.4.3: + resolution: {integrity: sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==} + + lru-cache@4.1.5: + resolution: {integrity: sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g==} + + lru-cache@5.1.1: + resolution: {integrity: sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==} + + lru-cache@6.0.0: + resolution: {integrity: sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==} + engines: {node: '>=10'} + + lru-cache@7.18.3: + resolution: {integrity: sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA==} + engines: {node: '>=12'} + + lru.min@1.1.2: + resolution: {integrity: sha512-Nv9KddBcQSlQopmBHXSsZVY5xsdlZkdH/Iey0BlcBYggMd4two7cZnKOK9vmy3nY0O5RGH99z1PCeTpPqszUYg==} + engines: {bun: '>=1.0.0', deno: '>=1.30.0', node: '>=8.0.0'} + + lz-string@1.5.0: + resolution: {integrity: sha512-h5bgJWpxJNswbU7qCrV0tIKQCaS3blPDrqKWx+QxzuzL1zGUzij9XCWLrSLsJPu5t+eWA/ycetzYAO5IOMcWAQ==} + hasBin: true + + magic-string@0.25.9: + resolution: {integrity: sha512-RmF0AsMzgt25qzqqLc1+MbHmhdx0ojF2Fvs4XnOqz2ZOBXzzkEwc/dJQZCYHAn7v1jbVOjAZfK8msRn4BxO4VQ==} + + magic-string@0.27.0: + resolution: {integrity: sha512-8UnnX2PeRAPZuN12svgR9j7M1uWMovg/CEnIwIG0LFkXSJJe4PdfUGiTGl8V9bsBHFUtfVINcSyYxd7q+kx9fA==} + engines: {node: '>=12'} + + make-dir@3.1.0: + resolution: {integrity: sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==} + engines: {node: '>=8'} + + make-dir@4.0.0: + resolution: {integrity: sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==} + engines: {node: '>=10'} + + make-error@1.3.6: + resolution: {integrity: sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==} + + make-fetch-happen@9.1.0: + resolution: {integrity: sha512-+zopwDy7DNknmwPQplem5lAZX/eCOzSvSNNcSKm5eVwTkOBzoktEfXsa9L23J/GIRhxRsaxzkPEhrJEpE2F4Gg==} + engines: {node: '>= 10'} + + makeerror@1.0.12: + resolution: {integrity: sha512-JmqCvUhmt43madlpFzG4BQzG2Z3m6tvQDNKdClZnO3VbIudJYmxsT0FNJMeiB2+JTSlTQTSbU8QdesVmwJcmLg==} + + map-stream@0.1.0: + resolution: {integrity: sha512-CkYQrPYZfWnu/DAmVCpTSX/xHpKZ80eKh2lAkyA6AJTef6bW+6JpbQZN5rofum7da+SyN1bi5ctTm+lTfcCW3g==} + + markdown-table@3.0.4: + resolution: {integrity: sha512-wiYz4+JrLyb/DqW2hkFJxP7Vd7JuTDm77fvbM8VfEQdmSMqcImWeeRbHwZjBjIFki/VaMK2BhFi7oUUZeM5bqw==} + + material-colors@1.2.6: + resolution: {integrity: sha512-6qE4B9deFBIa9YSpOc9O0Sgc43zTeVYbgDT5veRKSlB2+ZuHNoVVxA1L/ckMUayV9Ay9y7Z/SZCLcGteW9i7bg==} + + math-intrinsics@1.1.0: + resolution: {integrity: sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==} + engines: {node: '>= 0.4'} + + mathjax-full@3.2.2: + resolution: {integrity: sha512-+LfG9Fik+OuI8SLwsiR02IVdjcnRCy5MufYLi0C3TdMT56L/pjB0alMVGgoWJF8pN9Rc7FESycZB9BMNWIid5w==} + deprecated: Version 4 replaces this package with the scoped package @mathjax/src + + mdast-util-definitions@5.1.2: + resolution: {integrity: sha512-8SVPMuHqlPME/z3gqVwWY4zVXn8lqKv/pAhC57FuJ40ImXyBpmO5ukh98zB2v7Blql2FiHjHv9LVztSIqjY+MA==} + + mdast-util-find-and-replace@2.2.2: + resolution: {integrity: sha512-MTtdFRz/eMDHXzeK6W3dO7mXUlF82Gom4y0oOgvHhh/HXZAGvIQDUvQ0SuUx+j2tv44b8xTHOm8K/9OoRFnXKw==} + + mdast-util-from-markdown@0.8.5: + resolution: {integrity: sha512-2hkTXtYYnr+NubD/g6KGBS/0mFmBcifAsI0yIWRiRo0PjVs6SSOSOdtzbp6kSGnShDN6G5aWZpKQ2lWRy27mWQ==} + + mdast-util-from-markdown@1.3.1: + resolution: {integrity: sha512-4xTO/M8c82qBcnQc1tgpNtubGUW/Y1tBQ1B0i5CtSoelOLKFYlElIr3bvgREYYO5iRqbMY1YuqZng0GVOI8Qww==} + + mdast-util-gfm-autolink-literal@1.0.3: + resolution: {integrity: sha512-My8KJ57FYEy2W2LyNom4n3E7hKTuQk/0SES0u16tjA9Z3oFkF4RrC/hPAPgjlSpezsOvI8ObcXcElo92wn5IGA==} + + mdast-util-gfm-footnote@1.0.2: + resolution: {integrity: sha512-56D19KOGbE00uKVj3sgIykpwKL179QsVFwx/DCW0u/0+URsryacI4MAdNJl0dh+u2PSsD9FtxPFbHCzJ78qJFQ==} + + mdast-util-gfm-strikethrough@1.0.3: + resolution: {integrity: sha512-DAPhYzTYrRcXdMjUtUjKvW9z/FNAMTdU0ORyMcbmkwYNbKocDpdk+PX1L1dQgOID/+vVs1uBQ7ElrBQfZ0cuiQ==} + + mdast-util-gfm-table@1.0.7: + resolution: {integrity: sha512-jjcpmNnQvrmN5Vx7y7lEc2iIOEytYv7rTvu+MeyAsSHTASGCCRA79Igg2uKssgOs1i1po8s3plW0sTu1wkkLGg==} + + mdast-util-gfm-task-list-item@1.0.2: + resolution: {integrity: sha512-PFTA1gzfp1B1UaiJVyhJZA1rm0+Tzn690frc/L8vNX1Jop4STZgOE6bxUhnzdVSB+vm2GU1tIsuQcA9bxTQpMQ==} + + mdast-util-gfm@2.0.2: + resolution: {integrity: sha512-qvZ608nBppZ4icQlhQQIAdc6S3Ffj9RGmzwUKUWuEICFnd1LVkN3EktF7ZHAgfcEdvZB5owU9tQgt99e2TlLjg==} + + mdast-util-math@2.0.2: + resolution: {integrity: sha512-8gmkKVp9v6+Tgjtq6SYx9kGPpTf6FVYRa53/DLh479aldR9AyP48qeVOgNZ5X7QUK7nOy4yw7vg6mbiGcs9jWQ==} + + mdast-util-phrasing@3.0.1: + resolution: {integrity: sha512-WmI1gTXUBJo4/ZmSk79Wcb2HcjPJBzM1nlI/OUWA8yk2X9ik3ffNbBGsU+09BFmXaL1IBb9fiuvq6/KMiNycSg==} + + mdast-util-to-hast@12.3.0: + resolution: {integrity: sha512-pits93r8PhnIoU4Vy9bjW39M2jJ6/tdHyja9rrot9uujkN7UTU9SDnE6WNJz/IGyQk3XHX6yNNtrBH6cQzm8Hw==} + + mdast-util-to-markdown@1.5.0: + resolution: {integrity: sha512-bbv7TPv/WC49thZPg3jXuqzuvI45IL2EVAr/KxF0BSdHsU0ceFHOmwQn6evxAh1GaoK/6GQ1wp4R4oW2+LFL/A==} + + mdast-util-to-string@2.0.0: + resolution: {integrity: sha512-AW4DRS3QbBayY/jJmD8437V1Gombjf8RSOUCMFBuo5iHi58AGEgVCKQ+ezHkZZDpAQS75hcBMpLqjpJTjtUL7w==} + + mdast-util-to-string@3.2.0: + resolution: {integrity: sha512-V4Zn/ncyN1QNSqSBxTrMOLpjr+IKdHl2v3KVLoWmDPscP4r9GcCi71gjgvUV1SFSKh92AjAG4peFuBl2/YgCJg==} + + mdn-data@2.0.14: + resolution: {integrity: sha512-dn6wd0uw5GsdswPFfsgMp5NSB0/aDe6fK94YJV/AJDYXL6HVLWBsxeq7js7Ad+mU2K9LAlwpk6kN2D5mwCPVow==} + + mdn-data@2.0.4: + resolution: {integrity: sha512-iV3XNKw06j5Q7mi6h+9vbx23Tv7JkjEVgKHW4pimwyDGWm0OIQntJJ+u1C6mg6mK1EaTv42XQ7w76yuzH7M2cA==} + + media-typer@0.3.0: + resolution: {integrity: sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==} + engines: {node: '>= 0.6'} + + memfs@3.5.3: + resolution: {integrity: sha512-UERzLsxzllchadvbPs5aolHh65ISpKpM+ccLbOJ8/vvpBKmAWf+la7dXFy7Mr0ySHbdHrFv5kGFCUHHe6GFEmw==} + engines: {node: '>= 4.0.0'} + + memfs@4.57.1: + resolution: {integrity: sha512-WvzrWPwMQT+PtbX2Et64R4qXKK0fj/8pO85MrUCzymX3twwCiJCdvntW3HdhG1teLJcHDDLIKx5+c3HckWYZtQ==} + peerDependencies: + tslib: '2' + + memory-pager@1.5.0: + resolution: {integrity: sha512-ZS4Bp4r/Zoeq6+NLJpP+0Zzm0pR8whtGPf1XExKLJBAczGMnSi3It14OiNCStjQjM6NU1okjQGSxgEZN8eBYKg==} + + merge-descriptors@1.0.3: + resolution: {integrity: sha512-gaNvAS7TZ897/rVaZ0nMtAyxNyi/pdbjbAwUpFQpN70GqnVfOiXpeUUMKRBmzXaSQ8DdTX4/0ms62r2K+hE6mQ==} + + merge-stream@2.0.0: + resolution: {integrity: sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==} + + merge2@1.4.1: + resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==} + engines: {node: '>= 8'} + + methods@1.1.2: + resolution: {integrity: sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==} + engines: {node: '>= 0.6'} + + mhchemparser@4.2.1: + resolution: {integrity: sha512-kYmyrCirqJf3zZ9t/0wGgRZ4/ZJw//VwaRVGA75C4nhE60vtnIzhl9J9ndkX/h6hxSN7pjg/cE0VxbnNM+bnDQ==} + + micromark-core-commonmark@1.1.0: + resolution: {integrity: sha512-BgHO1aRbolh2hcrzL2d1La37V0Aoz73ymF8rAcKnohLy93titmv62E0gP8Hrx9PKcKrqCZ1BbLGbP3bEhoXYlw==} + + micromark-extension-gfm-autolink-literal@1.0.5: + resolution: {integrity: sha512-z3wJSLrDf8kRDOh2qBtoTRD53vJ+CWIyo7uyZuxf/JAbNJjiHsOpG1y5wxk8drtv3ETAHutCu6N3thkOOgueWg==} + + micromark-extension-gfm-footnote@1.1.2: + resolution: {integrity: sha512-Yxn7z7SxgyGWRNa4wzf8AhYYWNrwl5q1Z8ii+CSTTIqVkmGZF1CElX2JI8g5yGoM3GAman9/PVCUFUSJ0kB/8Q==} + + micromark-extension-gfm-strikethrough@1.0.7: + resolution: {integrity: sha512-sX0FawVE1o3abGk3vRjOH50L5TTLr3b5XMqnP9YDRb34M0v5OoZhG+OHFz1OffZ9dlwgpTBKaT4XW/AsUVnSDw==} + + micromark-extension-gfm-table@1.0.7: + resolution: {integrity: sha512-3ZORTHtcSnMQEKtAOsBQ9/oHp9096pI/UvdPtN7ehKvrmZZ2+bbWhi0ln+I9drmwXMt5boocn6OlwQzNXeVeqw==} + + micromark-extension-gfm-tagfilter@1.0.2: + resolution: {integrity: sha512-5XWB9GbAUSHTn8VPU8/1DBXMuKYT5uOgEjJb8gN3mW0PNW5OPHpSdojoqf+iq1xo7vWzw/P8bAHY0n6ijpXF7g==} + + micromark-extension-gfm-task-list-item@1.0.5: + resolution: {integrity: sha512-RMFXl2uQ0pNQy6Lun2YBYT9g9INXtWJULgbt01D/x8/6yJ2qpKyzdZD3pi6UIkzF++Da49xAelVKUeUMqd5eIQ==} + + micromark-extension-gfm@2.0.3: + resolution: {integrity: sha512-vb9OoHqrhCmbRidQv/2+Bc6pkP0FrtlhurxZofvOEy5o8RtuuvTq+RQ1Vw5ZDNrVraQZu3HixESqbG+0iKk/MQ==} + + micromark-extension-math@2.1.2: + resolution: {integrity: sha512-es0CcOV89VNS9wFmyn+wyFTKweXGW4CEvdaAca6SWRWPyYCbBisnjaHLjWO4Nszuiud84jCpkHsqAJoa768Pvg==} + + micromark-factory-destination@1.1.0: + resolution: {integrity: sha512-XaNDROBgx9SgSChd69pjiGKbV+nfHGDPVYFs5dOoDd7ZnMAE+Cuu91BCpsY8RT2NP9vo/B8pds2VQNCLiu0zhg==} + + micromark-factory-label@1.1.0: + resolution: {integrity: sha512-OLtyez4vZo/1NjxGhcpDSbHQ+m0IIGnT8BoPamh+7jVlzLJBH98zzuCoUeMxvM6WsNeh8wx8cKvqLiPHEACn0w==} + + micromark-factory-space@1.1.0: + resolution: {integrity: sha512-cRzEj7c0OL4Mw2v6nwzttyOZe8XY/Z8G0rzmWQZTBi/jjwyw/U4uqKtUORXQrR5bAZZnbTI/feRV/R7hc4jQYQ==} + + micromark-factory-title@1.1.0: + resolution: {integrity: sha512-J7n9R3vMmgjDOCY8NPw55jiyaQnH5kBdV2/UXCtZIpnHH3P6nHUKaH7XXEYuWwx/xUJcawa8plLBEjMPU24HzQ==} + + micromark-factory-whitespace@1.1.0: + resolution: {integrity: sha512-v2WlmiymVSp5oMg+1Q0N1Lxmt6pMhIHD457whWM7/GUlEks1hI9xj5w3zbc4uuMKXGisksZk8DzP2UyGbGqNsQ==} + + micromark-util-character@1.2.0: + resolution: {integrity: sha512-lXraTwcX3yH/vMDaFWCQJP1uIszLVebzUa3ZHdrgxr7KEU/9mL4mVgCpGbyhvNLNlauROiNUq7WN5u7ndbY6xg==} + + micromark-util-chunked@1.1.0: + resolution: {integrity: sha512-Ye01HXpkZPNcV6FiyoW2fGZDUw4Yc7vT0E9Sad83+bEDiCJ1uXu0S3mr8WLpsz3HaG3x2q0HM6CTuPdcZcluFQ==} + + micromark-util-classify-character@1.1.0: + resolution: {integrity: sha512-SL0wLxtKSnklKSUplok1WQFoGhUdWYKggKUiqhX+Swala+BtptGCu5iPRc+xvzJ4PXE/hwM3FNXsfEVgoZsWbw==} + + micromark-util-combine-extensions@1.1.0: + resolution: {integrity: sha512-Q20sp4mfNf9yEqDL50WwuWZHUrCO4fEyeDCnMGmG5Pr0Cz15Uo7KBs6jq+dq0EgX4DPwwrh9m0X+zPV1ypFvUA==} + + micromark-util-decode-numeric-character-reference@1.1.0: + resolution: {integrity: sha512-m9V0ExGv0jB1OT21mrWcuf4QhP46pH1KkfWy9ZEezqHKAxkj4mPCy3nIH1rkbdMlChLHX531eOrymlwyZIf2iw==} + + micromark-util-decode-string@1.1.0: + resolution: {integrity: sha512-YphLGCK8gM1tG1bd54azwyrQRjCFcmgj2S2GoJDNnh4vYtnL38JS8M4gpxzOPNyHdNEpheyWXCTnnTDY3N+NVQ==} + + micromark-util-encode@1.1.0: + resolution: {integrity: sha512-EuEzTWSTAj9PA5GOAs992GzNh2dGQO52UvAbtSOMvXTxv3Criqb6IOzJUBCmEqrrXSblJIJBbFFv6zPxpreiJw==} + + micromark-util-html-tag-name@1.2.0: + resolution: {integrity: sha512-VTQzcuQgFUD7yYztuQFKXT49KghjtETQ+Wv/zUjGSGBioZnkA4P1XXZPT1FHeJA6RwRXSF47yvJ1tsJdoxwO+Q==} + + micromark-util-normalize-identifier@1.1.0: + resolution: {integrity: sha512-N+w5vhqrBihhjdpM8+5Xsxy71QWqGn7HYNUvch71iV2PM7+E3uWGox1Qp90loa1ephtCxG2ftRV/Conitc6P2Q==} + + micromark-util-resolve-all@1.1.0: + resolution: {integrity: sha512-b/G6BTMSg+bX+xVCshPTPyAu2tmA0E4X98NSR7eIbeC6ycCqCeE7wjfDIgzEbkzdEVJXRtOG4FbEm/uGbCRouA==} + + micromark-util-sanitize-uri@1.2.0: + resolution: {integrity: sha512-QO4GXv0XZfWey4pYFndLUKEAktKkG5kZTdUNaTAkzbuJxn2tNBOr+QtxR2XpWaMhbImT2dPzyLrPXLlPhph34A==} + + micromark-util-subtokenize@1.1.0: + resolution: {integrity: sha512-kUQHyzRoxvZO2PuLzMt2P/dwVsTiivCK8icYTeR+3WgbuPqfHgPPy7nFKbeqRivBvn/3N3GBiNC+JRTMSxEC7A==} + + micromark-util-symbol@1.1.0: + resolution: {integrity: sha512-uEjpEYY6KMs1g7QfJ2eX1SQEV+ZT4rUD3UcF6l57acZvLNK7PBZL+ty82Z1qhK1/yXIY4bdx04FKMgR0g4IAag==} + + micromark-util-types@1.1.0: + resolution: {integrity: sha512-ukRBgie8TIAcacscVHSiddHjO4k/q3pnedmzMQ4iwDcK0FtFCohKOlFbaOL/mPgfnPsL3C1ZyxJa4sbWrBl3jg==} + + micromark@2.11.4: + resolution: {integrity: sha512-+WoovN/ppKolQOFIAajxi7Lu9kInbPxFuTBVEavFcL8eAfVstoc5MocPmqBeAdBOJV00uaVjegzH4+MA0DN/uA==} + + micromark@3.2.0: + resolution: {integrity: sha512-uD66tJj54JLYq0De10AhWycZWGQNUvDI55xPgk2sQM5kn1JYlhbCMTtEeT27+vAhW2FBQxLlOmS3pmA7/2z4aA==} + + micromatch@4.0.5: + resolution: {integrity: sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==} + engines: {node: '>=8.6'} + + micromatch@4.0.8: + resolution: {integrity: sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==} + engines: {node: '>=8.6'} + + mime-db@1.52.0: + resolution: {integrity: sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==} + engines: {node: '>= 0.6'} + + mime-db@1.54.0: + resolution: {integrity: sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==} + engines: {node: '>= 0.6'} + + mime-types@2.1.35: + resolution: {integrity: sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==} + engines: {node: '>= 0.6'} + + mime@1.6.0: + resolution: {integrity: sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==} + engines: {node: '>=4'} + hasBin: true + + mimic-fn@2.1.0: + resolution: {integrity: sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==} + engines: {node: '>=6'} + + mimic-fn@4.0.0: + resolution: {integrity: sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw==} + engines: {node: '>=12'} + + mimic-response@2.1.0: + resolution: {integrity: sha512-wXqjST+SLt7R009ySCglWBCFpjUygmCIfD790/kVbiGmUgfYGuB14PiTd5DwVxSV4NcYHjzMkoj5LjQZwTQLEA==} + engines: {node: '>=8'} + + mimic-response@3.1.0: + resolution: {integrity: sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==} + engines: {node: '>=10'} + + min-indent@1.0.1: + resolution: {integrity: sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg==} + engines: {node: '>=4'} + + mini-css-extract-plugin@2.9.4: + resolution: {integrity: sha512-ZWYT7ln73Hptxqxk2DxPU9MmapXRhxkJD6tkSR04dnQxm8BGu2hzgKLugK5yySD97u/8yy7Ma7E76k9ZdvtjkQ==} + engines: {node: '>= 12.13.0'} + peerDependencies: + webpack: ^5.0.0 + + minimalistic-assert@1.0.1: + resolution: {integrity: sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==} + + minimatch@3.1.2: + resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==} + + minimatch@5.1.6: + resolution: {integrity: sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==} + engines: {node: '>=10'} + + minimatch@9.0.5: + resolution: {integrity: sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==} + engines: {node: '>=16 || 14 >=14.17'} + + minimist@1.2.8: + resolution: {integrity: sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==} + + minipass-collect@1.0.2: + resolution: {integrity: sha512-6T6lH0H8OG9kITm/Jm6tdooIbogG9e0tLgpY6mphXSm/A9u8Nq1ryBG+Qspiub9LjWlBPsPS3tWQ/Botq4FdxA==} + engines: {node: '>= 8'} + + minipass-fetch@1.4.1: + resolution: {integrity: sha512-CGH1eblLq26Y15+Azk7ey4xh0J/XfJfrCox5LDJiKqI2Q2iwOLOKrlmIaODiSQS8d18jalF6y2K2ePUm0CmShw==} + engines: {node: '>=8'} + + minipass-flush@1.0.5: + resolution: {integrity: sha512-JmQSYYpPUqX5Jyn1mXaRwOda1uQ8HP5KAT/oDSLCzt1BYRhQU0/hDtsB1ufZfEEzMZ9aAVmsBw8+FWsIXlClWw==} + engines: {node: '>= 8'} + + minipass-pipeline@1.2.4: + resolution: {integrity: sha512-xuIq7cIOt09RPRJ19gdi4b+RiNvDFYe5JH+ggNvBqGqpQXcru3PcRmOZuHBKWK1Txf9+cQ+HMVN4d6z46LZP7A==} + engines: {node: '>=8'} + + minipass-sized@1.0.3: + resolution: {integrity: sha512-MbkQQ2CTiBMlA2Dm/5cY+9SWFEN8pzzOXi6rlM5Xxq0Yqbda5ZQy9sU75a673FE9ZK0Zsbr6Y5iP6u9nktfg2g==} + engines: {node: '>=8'} + + minipass@3.3.6: + resolution: {integrity: sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==} + engines: {node: '>=8'} + + minipass@5.0.0: + resolution: {integrity: sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ==} + engines: {node: '>=8'} + + minipass@7.1.2: + resolution: {integrity: sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==} + engines: {node: '>=16 || 14 >=14.17'} + + minizlib@2.1.2: + resolution: {integrity: sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==} + engines: {node: '>= 8'} + + mj-context-menu@0.6.1: + resolution: {integrity: sha512-7NO5s6n10TIV96d4g2uDpG7ZDpIhMh0QNfGdJw/W47JswFcosz457wqz/b5sAKvl12sxINGFCn80NZHKwxQEXA==} + + mkdirp-classic@0.5.3: + resolution: {integrity: sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==} + + mkdirp@0.5.6: + resolution: {integrity: sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==} + hasBin: true + + mkdirp@1.0.4: + resolution: {integrity: sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==} + engines: {node: '>=10'} + hasBin: true + + moment-timezone@0.5.48: + resolution: {integrity: sha512-f22b8LV1gbTO2ms2j2z13MuPogNoh5UzxL3nzNAYKGraILnbGc9NEE6dyiiiLv46DGRb8A4kg8UKWLjPthxBHw==} + + moment@2.30.1: + resolution: {integrity: sha512-uEmtNhbDOrWPFS+hdjFCBfy9f2YoyzRpwcl+DqpC6taX21FzsTLQVbMV/W7PzNSX6x/bhC1zA3c2UQ5NzH6how==} + + mongodb-connection-string-url@3.0.2: + resolution: {integrity: sha512-rMO7CGo/9BFwyZABcKAWL8UJwH/Kc2x0g72uhDWzG48URRax5TCIcJ7Rc3RZqffZzO/Gwff/jyKwCU9TN8gehA==} + + mongodb@6.20.0: + resolution: {integrity: sha512-Tl6MEIU3K4Rq3TSHd+sZQqRBoGlFsOgNrH5ltAcFBV62Re3Fd+FcaVf8uSEQFOJ51SDowDVttBTONMfoYWrWlQ==} + engines: {node: '>=16.20.1'} + peerDependencies: + '@aws-sdk/credential-providers': ^3.188.0 + '@mongodb-js/zstd': ^1.1.0 || ^2.0.0 + gcp-metadata: ^5.2.0 + kerberos: ^2.0.1 + mongodb-client-encryption: '>=6.0.0 <7' + snappy: ^7.3.2 + socks: ^2.7.1 + peerDependenciesMeta: + '@aws-sdk/credential-providers': + optional: true + '@mongodb-js/zstd': + optional: true + gcp-metadata: + optional: true + kerberos: + optional: true + mongodb-client-encryption: + optional: true + snappy: + optional: true + socks: + optional: true + + mri@1.2.0: + resolution: {integrity: sha512-tzzskb3bG8LvYGFF/mDTpq3jpI6Q9wc3LEmBaghu+DdCssd1FakN7Bc0hVNmEyGq1bq3RgfkCb3cmQLpNPOroA==} + engines: {node: '>=4'} + + ms@2.0.0: + resolution: {integrity: sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==} + + ms@2.1.2: + resolution: {integrity: sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==} + + ms@2.1.3: + resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} + + multer@1.4.5-lts.2: + resolution: {integrity: sha512-VzGiVigcG9zUAoCNU+xShztrlr1auZOlurXynNvO9GiWD1/mTBbUljOKY+qMeazBqXgRnjzeEgJI/wyjJUHg9A==} + engines: {node: '>= 6.0.0'} + deprecated: Multer 1.x is impacted by a number of vulnerabilities, which have been patched in 2.x. You should upgrade to the latest 2.x version. + + multicast-dns@7.2.5: + resolution: {integrity: sha512-2eznPJP8z2BFLX50tf0LuODrpINqP1RVIm/CObbTcBRITQgmC/TjcREF1NeTBzIcR5XO/ukWo+YHOjBbFwIupg==} + hasBin: true + + mysql2@3.15.2: + resolution: {integrity: sha512-kFm5+jbwR5mC+lo+3Cy46eHiykWSpUtTLOH3GE+AR7GeLq8PgfJcvpMiyVWk9/O53DjQsqm6a3VOOfq7gYWFRg==} + engines: {node: '>= 8.0'} + + mz@2.7.0: + resolution: {integrity: sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==} + + named-placeholders@1.1.3: + resolution: {integrity: sha512-eLoBxg6wE/rZkJPhU/xRX1WTpkFEwDJEN96oxFrTsqBdbT5ec295Q+CoHrL9IT0DipqKhmGcaZmwOt8OON5x1w==} + engines: {node: '>=12.0.0'} + + nan@2.23.0: + resolution: {integrity: sha512-1UxuyYGdoQHcGg87Lkqm3FzefucTa0NAiOcuRsDmysep3c1LVCRK2krrUDafMWtjSG04htvAmvg96+SDknOmgQ==} + + nanoclone@0.2.1: + resolution: {integrity: sha512-wynEP02LmIbLpcYw8uBKpcfF6dmg2vcpKqxeH5UcoKEYdExslsdUA4ugFauuaeYdTB76ez6gJW8XAZ6CgkXYxA==} + + nanoid@3.3.11: + resolution: {integrity: sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==} + engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} + hasBin: true + + napi-build-utils@2.0.0: + resolution: {integrity: sha512-GEbrYkbfF7MoNaoh2iGG84Mnf/WZfB0GdGEsM8wz7Expx/LlWf5U8t9nvJKXSp3qr5IsEbK04cBGhol/KwOsWA==} + + natural-compare-lite@1.4.0: + resolution: {integrity: sha512-Tj+HTDSJJKaZnfiuw+iaF9skdPpTo2GtEly5JHnWV/hfv2Qj/9RKsGISQtLh2ox3l5EAGw487hnBee0sIJ6v2g==} + + natural-compare@1.4.0: + resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==} + + negotiator@0.6.3: + resolution: {integrity: sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==} + engines: {node: '>= 0.6'} + + negotiator@0.6.4: + resolution: {integrity: sha512-myRT3DiWPHqho5PrJaIRyaMv2kgYf0mUVgBNOYMuCH5Ki1yEiQaf/ZJuQ62nvpc44wL5WDbTX7yGJi1Neevw8w==} + engines: {node: '>= 0.6'} + + neo-async@2.6.2: + resolution: {integrity: sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==} + + no-case@3.0.4: + resolution: {integrity: sha512-fgAN3jGAh+RoxUGZHTSOLJIqUc2wmoBwGR4tbpNAKmmovFoWq0OdRkb0VkldReO2a2iBT/OEulG9XSUc10r3zg==} + + node-abi@3.78.0: + resolution: {integrity: sha512-E2wEyrgX/CqvicaQYU3Ze1PFGjc4QYPGsjUrlYkqAE0WjHEZwgOsGMPMzkMse4LjJbDmaEuDX3CM036j5K2DSQ==} + engines: {node: '>=10'} + + node-addon-api@7.1.1: + resolution: {integrity: sha512-5m3bsyrjFWE1xf7nz7YXdN4udnVtXK6/Yfgn5qnahL6bCkf2yKt4k3nuTKAtT4r3IG8JNR2ncsIMdZuAzJjHQQ==} + + node-cleanup@2.1.2: + resolution: {integrity: sha512-qN8v/s2PAJwGUtr1/hYTpNKlD6Y9rc4p8KSmJXyGdYGZsDGKXrGThikLFP9OCHFeLeEpQzPwiAtdIvBLqm//Hw==} + + node-domexception@1.0.0: + resolution: {integrity: sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==} + engines: {node: '>=10.5.0'} + deprecated: Use your platform's native DOMException instead + + node-fetch@2.7.0: + resolution: {integrity: sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==} + engines: {node: 4.x || >=6.0.0} + peerDependencies: + encoding: ^0.1.0 + peerDependenciesMeta: + encoding: + optional: true + + node-fetch@3.3.2: + resolution: {integrity: sha512-dRB78srN/l6gqWulah9SrxeYnxeddIG30+GOqK/9OlLVyLg3HPnr6SqOWTWOXKRwC2eGYCkZ59NNuSgvSrpgOA==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + + node-forge@1.3.1: + resolution: {integrity: sha512-dPEtOeMvF9VMcYV/1Wb8CPoVAXtp6MKMlcbAt4ddqmGqUJ6fQZFXkNZNkNlfevtNkGtaSoXf/vNNNSvgrdXwtA==} + engines: {node: '>= 6.13.0'} + + node-gyp-build@4.8.4: + resolution: {integrity: sha512-LA4ZjwlnUblHVgq0oBF3Jl/6h/Nvs5fzBLwdEF4nuxnFdsfajde4WfxtJr3CaiH+F6ewcIB/q4jQ4UzPyid+CQ==} + hasBin: true + + node-gyp@8.4.1: + resolution: {integrity: sha512-olTJRgUtAb/hOXG0E93wZDs5YiJlgbXxTwQAFHyNlRsXQnYzUaF2aGgujZbw+hR8aF4ZG/rST57bWMWD16jr9w==} + engines: {node: '>= 10.12.0'} + hasBin: true + + node-int64@0.4.0: + resolution: {integrity: sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw==} + + node-releases@2.0.23: + resolution: {integrity: sha512-cCmFDMSm26S6tQSDpBCg/NR8NENrVPhAJSf+XbxBG4rPFaaonlEoE9wHQmun+cls499TQGSb7ZyPBRlzgKfpeg==} + + nodemon@2.0.22: + resolution: {integrity: sha512-B8YqaKMmyuCO7BowF1Z1/mkPqLk6cs/l63Ojtd6otKjMx47Dq1utxfRxcavH1I7VSaL8n5BUaoutadnsX3AAVQ==} + engines: {node: '>=8.10.0'} + hasBin: true + + nopt@5.0.0: + resolution: {integrity: sha512-Tbj67rffqceeLpcRXrT7vKAN8CwfPeIBgM7E6iBkmKLV7bEMwpGgYLGv0jACUsECaa/vuxP0IjEont6umdMgtQ==} + engines: {node: '>=6'} + hasBin: true + + normalize-path@3.0.0: + resolution: {integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==} + engines: {node: '>=0.10.0'} + + normalize-range@0.1.2: + resolution: {integrity: sha512-bdok/XvKII3nUpklnV6P2hxtMNrCboOjAcyBuQnWEhO665FwrSNRxU+AqpsyvO6LgGYPspN+lu5CLtw4jPRKNA==} + engines: {node: '>=0.10.0'} + + normalize-url@6.1.0: + resolution: {integrity: sha512-DlL+XwOy3NxAQ8xuC0okPgK46iuVNAK01YN7RueYBqqFeGsBjV9XmCAzAdgt+667bCl5kPh9EqKKDwnaPG1I7A==} + engines: {node: '>=10'} + + notistack@2.0.8: + resolution: {integrity: sha512-/IY14wkFp5qjPgKNvAdfL5Jp6q90+MjgKTPh4c81r/lW70KeuX6b9pE/4f8L4FG31cNudbN9siiFS5ql1aSLRw==} + peerDependencies: + '@emotion/react': ^11.4.1 + '@emotion/styled': ^11.3.0 + '@mui/material': ^5.0.0 + react: ^16.8.0 || ^17.0.0 || ^18.0.0 + react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 + peerDependenciesMeta: + '@emotion/react': + optional: true + '@emotion/styled': + optional: true + + npm-run-path@1.0.0: + resolution: {integrity: sha512-PrGAi1SLlqNvKN5uGBjIgnrTb8fl0Jz0a3JJmeMcGnIBh7UE9Gc4zsAMlwDajOMg2b1OgP6UPvoLUboTmMZPFA==} + engines: {node: '>=0.10.0'} + + npm-run-path@4.0.1: + resolution: {integrity: sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==} + engines: {node: '>=8'} + + npm-run-path@5.3.0: + resolution: {integrity: sha512-ppwTtiJZq0O/ai0z7yfudtBpWIoxM8yE6nHi1X47eFR2EWORqfbu6CnPlNsjeN683eT0qG6H/Pyf9fCcvjnnnQ==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + + npmlog@5.0.1: + resolution: {integrity: sha512-AqZtDUWOMKs1G/8lwylVjrdYgqA4d9nu8hc+0gzRxlDb1I10+FHBGMXs6aiQHFdCUUlqH99MUMuLfzWDNDtfxw==} + deprecated: This package is no longer supported. + + npmlog@6.0.2: + resolution: {integrity: sha512-/vBvz5Jfr9dT/aFWd0FIRf+T/Q2WBsLENygUaFUqstqsycmZAP/t5BvFJTK0viFmSUxiUKTUplWy5vt+rvKIxg==} + engines: {node: ^12.13.0 || ^14.15.0 || >=16.0.0} + deprecated: This package is no longer supported. + + nth-check@2.1.1: + resolution: {integrity: sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==} + + nwsapi@2.2.22: + resolution: {integrity: sha512-ujSMe1OWVn55euT1ihwCI1ZcAaAU3nxUiDwfDQldc51ZXaB9m2AyOn6/jh1BLe2t/G8xd6uKG1UBF2aZJeg2SQ==} + + object-assign@4.1.1: + resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==} + engines: {node: '>=0.10.0'} + + object-hash@3.0.0: + resolution: {integrity: sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==} + engines: {node: '>= 6'} + + object-inspect@1.13.4: + resolution: {integrity: sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==} + engines: {node: '>= 0.4'} + + object-is@1.1.6: + resolution: {integrity: sha512-F8cZ+KfGlSGi09lJT7/Nd6KJZ9ygtvYC0/UYYLI9nmQKLMnydpB9yvbv9K1uSkEu7FU9vYPmVwLg328tX+ot3Q==} + engines: {node: '>= 0.4'} + + object-keys@1.1.1: + resolution: {integrity: sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==} + engines: {node: '>= 0.4'} + + object.assign@4.1.7: + resolution: {integrity: sha512-nK28WOo+QIjBkDduTINE4JkF/UJJKyf2EJxvJKfblDpyg0Q+pkOHNTL0Qwy6NP6FhE/EnzV73BxxqcJaXY9anw==} + engines: {node: '>= 0.4'} + + object.entries@1.1.9: + resolution: {integrity: sha512-8u/hfXFRBD1O0hPUjioLhoWFHRmt6tKA4/vZPyckBr18l1KE9uHrFaFaUi8MDRTpi4uak2goyPTSNJLXX2k2Hw==} + engines: {node: '>= 0.4'} + + object.fromentries@2.0.8: + resolution: {integrity: sha512-k6E21FzySsSK5a21KRADBd/NGneRegFO5pLHfdQLpRDETUNJueLXs3WCzyQ3tFRDYgbq3KHGXfTbi2bs8WQ6rQ==} + engines: {node: '>= 0.4'} + + object.getownpropertydescriptors@2.1.8: + resolution: {integrity: sha512-qkHIGe4q0lSYMv0XI4SsBTJz3WaURhLvd0lKSgtVuOsJ2krg4SgMw3PIRQFMp07yi++UR3se2mkcLqsBNpBb/A==} + engines: {node: '>= 0.8'} + + object.groupby@1.0.3: + resolution: {integrity: sha512-+Lhy3TQTuzXI5hevh8sBGqbmurHbbIjAi0Z4S63nthVLmLxfbj4T54a4CfZrXIrt9iP4mVAPYMo/v99taj3wjQ==} + engines: {node: '>= 0.4'} + + object.values@1.2.1: + resolution: {integrity: sha512-gXah6aZrcUxjWg2zR2MwouP2eHlCBzdV4pygudehaKXSGW4v2AsRQUK+lwwXhii6KFZcunEnmSUoYp5CXibxtA==} + engines: {node: '>= 0.4'} + + obuf@1.1.2: + resolution: {integrity: sha512-PX1wu0AmAdPqOL1mWhqmlOd8kOIZQwGZw6rh7uby9fTc5lhaOWFLX3I6R1hrF9k3zUY40e6igsLGkDXK92LJNg==} + + on-finished@2.4.1: + resolution: {integrity: sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==} + engines: {node: '>= 0.8'} + + on-headers@1.1.0: + resolution: {integrity: sha512-737ZY3yNnXy37FHkQxPzt4UZ2UWPWiCZWLvFZ4fu5cueciegX0zGPnrlY6bwRg4FdQOe9YU8MkmJwGhoMybl8A==} + engines: {node: '>= 0.8'} + + once@1.4.0: + resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==} + + one-time@1.0.0: + resolution: {integrity: sha512-5DXOiRKwuSEcQ/l0kGCF6Q3jcADFv5tSmRaJck/OqkVFcOzutB134KRSfF0xDrL39MNnqxbHBbUUcjZIhTgb2g==} + + onetime@5.1.2: + resolution: {integrity: sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==} + engines: {node: '>=6'} + + onetime@6.0.0: + resolution: {integrity: sha512-1FlR+gjXK7X+AsAHso35MnyN5KqGwJRi/31ft6x0M194ht7S+rWAvd7PHss9xSKMzE0asv1pyIHaJYq+BbacAQ==} + engines: {node: '>=12'} + + open@8.4.2: + resolution: {integrity: sha512-7x81NCL719oNbsq/3mh+hVrAWmFuEYUqrq/Iw3kUzH8ReypT9QQ0BLoJS7/G9k6N81XjW4qHWtjWwe/9eLy1EQ==} + engines: {node: '>=12'} + + optionator@0.8.3: + resolution: {integrity: sha512-+IW9pACdk3XWmmTXG8m3upGUJst5XRGzxMRjXzAuJ1XnIFNvfhjjIuYkDvysnPQ7qzqVzLt78BCruntqRhWQbA==} + engines: {node: '>= 0.8.0'} + + optionator@0.9.4: + resolution: {integrity: sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==} + engines: {node: '>= 0.8.0'} + + ospath@1.2.2: + resolution: {integrity: sha512-o6E5qJV5zkAbIDNhGSIlyOhScKXgQrSRMilfph0clDfM0nEnBOlKlH4sWDmG95BW/CvwNz0vmm7dJVtU2KlMiA==} + + own-keys@1.0.1: + resolution: {integrity: sha512-qFOyK5PjiWZd+QQIh+1jhdb9LpxTF0qs7Pm8o5QHYZ0M3vKqSqzsZaEB6oWlxZ+q2sJBMI/Ktgd2N5ZwQoRHfg==} + engines: {node: '>= 0.4'} + + p-limit@2.3.0: + resolution: {integrity: sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==} + engines: {node: '>=6'} + + p-limit@3.1.0: + resolution: {integrity: sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==} + engines: {node: '>=10'} + + p-locate@3.0.0: + resolution: {integrity: sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==} + engines: {node: '>=6'} + + p-locate@4.1.0: + resolution: {integrity: sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==} + engines: {node: '>=8'} + + p-locate@5.0.0: + resolution: {integrity: sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==} + engines: {node: '>=10'} + + p-map@4.0.0: + resolution: {integrity: sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ==} + engines: {node: '>=10'} + + p-retry@4.6.2: + resolution: {integrity: sha512-312Id396EbJdvRONlngUx0NydfrIQ5lsYu0znKVUzVvArzEIt08V1qhtyESbGVd1FGX7UKtiFp5uwKZdM8wIuQ==} + engines: {node: '>=8'} + + p-try@2.2.0: + resolution: {integrity: sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==} + engines: {node: '>=6'} + + package-json-from-dist@1.0.1: + resolution: {integrity: sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==} + + param-case@3.0.4: + resolution: {integrity: sha512-RXlj7zCYokReqWpOPH9oYivUzLYZ5vAPIfEmCTNViosC78F8F0H9y7T7gG2M39ymgutxF5gcFEsyZQSph9Bp3A==} + + parent-module@1.0.1: + resolution: {integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==} + engines: {node: '>=6'} + + parse-entities@2.0.0: + resolution: {integrity: sha512-kkywGpCcRYhqQIchaWqZ875wzpS/bMKhz5HnN3p7wveJTkTtyAB/AlnS0f8DFSqYW1T82t6yEAkEcB+A1I3MbQ==} + + parse-json@5.2.0: + resolution: {integrity: sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==} + engines: {node: '>=8'} + + parse-srcset@1.0.2: + resolution: {integrity: sha512-/2qh0lav6CmI15FzA3i/2Bzk2zCgQhGMkvhOhKNcBVQ1ldgpbfiNTVslmooUmWJcADi1f1kIeynbDRVzNlfR6Q==} + + parse5@6.0.1: + resolution: {integrity: sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw==} + + parse5@7.3.0: + resolution: {integrity: sha512-IInvU7fabl34qmi9gY8XOVxhYyMyuH2xUNpb2q8/Y+7552KlejkRvqvD19nMoUW/uQGGbqNpA6Tufu5FL5BZgw==} + + parseurl@1.3.3: + resolution: {integrity: sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==} + engines: {node: '>= 0.8'} + + pascal-case@3.1.2: + resolution: {integrity: sha512-uWlGT3YSnK9x3BQJaOdcZwrnV6hPpd8jFH1/ucpiLRPh/2zCVJKS19E4GvYHvaCcACn3foXZ0cLB9Wrx1KGe5g==} + + path-exists@3.0.0: + resolution: {integrity: sha512-bpC7GYwiDYQ4wYLe+FA8lhRjhQCMcQGuSgGGqDkg/QerRWw9CmGRT0iSOVRSZJ29NMLZgIzqaljJ63oaL4NIJQ==} + engines: {node: '>=4'} + + path-exists@4.0.0: + resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==} + engines: {node: '>=8'} + + path-key@1.0.0: + resolution: {integrity: sha512-T3hWy7tyXlk3QvPFnT+o2tmXRzU4GkitkUWLp/WZ0S/FXd7XMx176tRurgTvHTNMJOQzTcesHNpBqetH86mQ9g==} + engines: {node: '>=0.10.0'} + + path-key@3.1.1: + resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==} + engines: {node: '>=8'} + + path-key@4.0.0: + resolution: {integrity: sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ==} + engines: {node: '>=12'} + + path-parse@1.0.7: + resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==} + + path-scurry@1.11.1: + resolution: {integrity: sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==} + engines: {node: '>=16 || 14 >=14.18'} + + path-to-regexp@0.1.12: + resolution: {integrity: sha512-RA1GjUVMnvYFxuqovrEqZoxxW5NUZqbwKtYz/Tt7nXerk0LbLblQmrsgdeOxV5SFHf0UDggjS/bSeOZwt1pmEQ==} + + path-type@4.0.0: + resolution: {integrity: sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==} + engines: {node: '>=8'} + + pause-stream@0.0.11: + resolution: {integrity: sha512-e3FBlXLmN/D1S+zHzanP4E/4Z60oFAa3O051qt1pxa7DEJWKAyil6upYVXCWadEnuoqa4Pkc9oUx9zsxYeRv8A==} + + pend@1.2.0: + resolution: {integrity: sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg==} + + perfect-scrollbar@1.5.6: + resolution: {integrity: sha512-rixgxw3SxyJbCaSpo1n35A/fwI1r2rdwMKOTCg/AcG+xOEyZcE8UHVjpZMFCVImzsFoCZeJTT+M/rdEIQYO2nw==} + + performance-now@2.1.0: + resolution: {integrity: sha512-7EAHlyLHI56VEIdK57uwHdHKIaAGbnXPiw0yWbarQZOKaKpvUIgW0jWRVLiatnM+XXlSwsanIBH/hzGMJulMow==} + + pg-cloudflare@1.2.7: + resolution: {integrity: sha512-YgCtzMH0ptvZJslLM1ffsY4EuGaU0cx4XSdXLRFae8bPP4dS5xL1tNB3k2o/N64cHJpwU7dxKli/nZ2lUa5fLg==} + + pg-connection-string@2.9.1: + resolution: {integrity: sha512-nkc6NpDcvPVpZXxrreI/FOtX3XemeLl8E0qFr6F2Lrm/I8WOnaWNhIPK2Z7OHpw7gh5XJThi6j6ppgNoaT1w4w==} + + pg-int8@1.0.1: + resolution: {integrity: sha512-WCtabS6t3c8SkpDBUlb1kjOs7l66xsGdKpIPZsg4wR+B3+u9UAum2odSsF9tnvxg80h4ZxLWMy4pRjOsFIqQpw==} + engines: {node: '>=4.0.0'} + + pg-pool@3.10.1: + resolution: {integrity: sha512-Tu8jMlcX+9d8+QVzKIvM/uJtp07PKr82IUOYEphaWcoBhIYkoHpLXN3qO59nAI11ripznDsEzEv8nUxBVWajGg==} + peerDependencies: + pg: '>=8.0' + + pg-protocol@1.10.3: + resolution: {integrity: sha512-6DIBgBQaTKDJyxnXaLiLR8wBpQQcGWuAESkRBX/t6OwA8YsqP+iVSiond2EDy6Y/dsGk8rh/jtax3js5NeV7JQ==} + + pg-types@2.2.0: + resolution: {integrity: sha512-qTAAlrEsl8s4OiEQY69wDvcMIdQN6wdz5ojQiOy6YRMuynxenON0O5oCpJI6lshc6scgAY8qvJ2On/p+CXY0GA==} + engines: {node: '>=4'} + + pg@8.16.3: + resolution: {integrity: sha512-enxc1h0jA/aq5oSDMvqyW3q89ra6XIIDZgCX9vkMrnz5DFTw/Ny3Li2lFQ+pt3L6MCgm/5o2o8HW9hiJji+xvw==} + engines: {node: '>= 16.0.0'} + peerDependencies: + pg-native: '>=3.0.1' + peerDependenciesMeta: + pg-native: + optional: true + + pgpass@1.0.5: + resolution: {integrity: sha512-FdW9r/jQZhSeohs1Z3sI1yxFQNFvMcnmfuj4WBMUTxOrAyLMaTcE1aAMBiTlbMNaXvBCQuVi0R7hd8udDSP7ug==} + + picocolors@0.2.1: + resolution: {integrity: sha512-cMlDqaLEqfSaW8Z7N5Jw+lyIW869EzT73/F5lhtY9cLGoVxSXznfgfXMO0Z5K0o0Q2TkTXq+0KFsdnSe3jDViA==} + + picocolors@1.1.1: + resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==} + + picomatch@2.3.1: + resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==} + engines: {node: '>=8.6'} + + picomatch@3.0.1: + resolution: {integrity: sha512-I3EurrIQMlRc9IaAZnqRR044Phh2DXY+55o7uJ0V+hYZAcQYSuFWsc9q5PvyDHUSCe1Qxn/iBz+78s86zWnGag==} + engines: {node: '>=10'} + + picomatch@4.0.3: + resolution: {integrity: sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==} + engines: {node: '>=12'} + + pidtree@0.6.0: + resolution: {integrity: sha512-eG2dWTVw5bzqGRztnHExczNxt5VGsE6OwTeCG3fdUf9KBsZzO3R5OIIIzWR+iZA0NtZ+RDVdaoE2dK1cn6jH4g==} + engines: {node: '>=0.10'} + hasBin: true + + pify@2.3.0: + resolution: {integrity: sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==} + engines: {node: '>=0.10.0'} + + pirates@4.0.7: + resolution: {integrity: sha512-TfySrs/5nm8fQJDcBDuUng3VOUKsd7S+zqvbOTiGXHfxX4wK31ard+hoNuvkicM/2YFzlpDgABOevKSsB4G/FA==} + engines: {node: '>= 6'} + + pkg-dir@4.2.0: + resolution: {integrity: sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==} + engines: {node: '>=8'} + + pkg-up@3.1.0: + resolution: {integrity: sha512-nDywThFk1i4BQK4twPQ6TA4RT8bDY96yeuCVBWL3ePARCiEKDRSrNGbFIgUJpLp+XeIR65v8ra7WuJOFUBtkMA==} + engines: {node: '>=8'} + + popmotion@9.3.6: + resolution: {integrity: sha512-ZTbXiu6zIggXzIliMi8LGxXBF5ST+wkpXGEjeTUDUOCdSQ356hij/xjeUdv0F8zCQNeqB1+PR5/BB+gC+QLAPw==} + + possible-typed-array-names@1.1.0: + resolution: {integrity: sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg==} + engines: {node: '>= 0.4'} + + postcss-attribute-case-insensitive@5.0.2: + resolution: {integrity: sha512-XIidXV8fDr0kKt28vqki84fRK8VW8eTuIa4PChv2MqKuT6C9UjmSKzen6KaWhWEoYvwxFCa7n/tC1SZ3tyq4SQ==} + engines: {node: ^12 || ^14 || >=16} + peerDependencies: + postcss: ^8.2 + + postcss-browser-comments@4.0.0: + resolution: {integrity: sha512-X9X9/WN3KIvY9+hNERUqX9gncsgBA25XaeR+jshHz2j8+sYyHktHw1JdKuMjeLpGktXidqDhA7b/qm1mrBDmgg==} + engines: {node: '>=8'} + peerDependencies: + browserslist: '>=4' + postcss: '>=8' + + postcss-calc@8.2.4: + resolution: {integrity: sha512-SmWMSJmB8MRnnULldx0lQIyhSNvuDl9HfrZkaqqE/WHAhToYsAvDq+yAsA/kIyINDszOp3Rh0GFoNuH5Ypsm3Q==} + peerDependencies: + postcss: ^8.2.2 + + postcss-clamp@4.1.0: + resolution: {integrity: sha512-ry4b1Llo/9zz+PKC+030KUnPITTJAHeOwjfAyyB60eT0AorGLdzp52s31OsPRHRf8NchkgFoG2y6fCfn1IV1Ow==} + engines: {node: '>=7.6.0'} + peerDependencies: + postcss: ^8.4.6 + + postcss-color-functional-notation@4.2.4: + resolution: {integrity: sha512-2yrTAUZUab9s6CpxkxC4rVgFEVaR6/2Pipvi6qcgvnYiVqZcbDHEoBDhrXzyb7Efh2CCfHQNtcqWcIruDTIUeg==} + engines: {node: ^12 || ^14 || >=16} + peerDependencies: + postcss: ^8.2 + + postcss-color-hex-alpha@8.0.4: + resolution: {integrity: sha512-nLo2DCRC9eE4w2JmuKgVA3fGL3d01kGq752pVALF68qpGLmx2Qrk91QTKkdUqqp45T1K1XV8IhQpcu1hoAQflQ==} + engines: {node: ^12 || ^14 || >=16} + peerDependencies: + postcss: ^8.4 + + postcss-color-rebeccapurple@7.1.1: + resolution: {integrity: sha512-pGxkuVEInwLHgkNxUc4sdg4g3py7zUeCQ9sMfwyHAT+Ezk8a4OaaVZ8lIY5+oNqA/BXXgLyXv0+5wHP68R79hg==} + engines: {node: ^12 || ^14 || >=16} + peerDependencies: + postcss: ^8.2 + + postcss-colormin@5.3.1: + resolution: {integrity: sha512-UsWQG0AqTFQmpBegeLLc1+c3jIqBNB0zlDGRWR+dQ3pRKJL1oeMzyqmH3o2PIfn9MBdNrVPWhDbT769LxCTLJQ==} + engines: {node: ^10 || ^12 || >=14.0} + peerDependencies: + postcss: ^8.2.15 + + postcss-convert-values@5.1.3: + resolution: {integrity: sha512-82pC1xkJZtcJEfiLw6UXnXVXScgtBrjlO5CBmuDQc+dlb88ZYheFsjTn40+zBVi3DkfF7iezO0nJUPLcJK3pvA==} + engines: {node: ^10 || ^12 || >=14.0} + peerDependencies: + postcss: ^8.2.15 + + postcss-custom-media@8.0.2: + resolution: {integrity: sha512-7yi25vDAoHAkbhAzX9dHx2yc6ntS4jQvejrNcC+csQJAXjj15e7VcWfMgLqBNAbOvqi5uIa9huOVwdHbf+sKqg==} + engines: {node: ^12 || ^14 || >=16} + peerDependencies: + postcss: ^8.3 + + postcss-custom-properties@12.1.11: + resolution: {integrity: sha512-0IDJYhgU8xDv1KY6+VgUwuQkVtmYzRwu+dMjnmdMafXYv86SWqfxkc7qdDvWS38vsjaEtv8e0vGOUQrAiMBLpQ==} + engines: {node: ^12 || ^14 || >=16} + peerDependencies: + postcss: ^8.2 + + postcss-custom-selectors@6.0.3: + resolution: {integrity: sha512-fgVkmyiWDwmD3JbpCmB45SvvlCD6z9CG6Ie6Iere22W5aHea6oWa7EM2bpnv2Fj3I94L3VbtvX9KqwSi5aFzSg==} + engines: {node: ^12 || ^14 || >=16} + peerDependencies: + postcss: ^8.3 + + postcss-dir-pseudo-class@6.0.5: + resolution: {integrity: sha512-eqn4m70P031PF7ZQIvSgy9RSJ5uI2171O/OO/zcRNYpJbvaeKFUlar1aJ7rmgiQtbm0FSPsRewjpdS0Oew7MPA==} + engines: {node: ^12 || ^14 || >=16} + peerDependencies: + postcss: ^8.2 + + postcss-discard-comments@5.1.2: + resolution: {integrity: sha512-+L8208OVbHVF2UQf1iDmRcbdjJkuBF6IS29yBDSiWUIzpYaAhtNl6JYnYm12FnkeCwQqF5LeklOu6rAqgfBZqQ==} + engines: {node: ^10 || ^12 || >=14.0} + peerDependencies: + postcss: ^8.2.15 + + postcss-discard-duplicates@5.1.0: + resolution: {integrity: sha512-zmX3IoSI2aoenxHV6C7plngHWWhUOV3sP1T8y2ifzxzbtnuhk1EdPwm0S1bIUNaJ2eNbWeGLEwzw8huPD67aQw==} + engines: {node: ^10 || ^12 || >=14.0} + peerDependencies: + postcss: ^8.2.15 + + postcss-discard-empty@5.1.1: + resolution: {integrity: sha512-zPz4WljiSuLWsI0ir4Mcnr4qQQ5e1Ukc3i7UfE2XcrwKK2LIPIqE5jxMRxO6GbI3cv//ztXDsXwEWT3BHOGh3A==} + engines: {node: ^10 || ^12 || >=14.0} + peerDependencies: + postcss: ^8.2.15 + + postcss-discard-overridden@5.1.0: + resolution: {integrity: sha512-21nOL7RqWR1kasIVdKs8HNqQJhFxLsyRfAnUDm4Fe4t4mCWL9OJiHvlHPjcd8zc5Myu89b/7wZDnOSjFgeWRtw==} + engines: {node: ^10 || ^12 || >=14.0} + peerDependencies: + postcss: ^8.2.15 + + postcss-double-position-gradients@3.1.2: + resolution: {integrity: sha512-GX+FuE/uBR6eskOK+4vkXgT6pDkexLokPaz/AbJna9s5Kzp/yl488pKPjhy0obB475ovfT1Wv8ho7U/cHNaRgQ==} + engines: {node: ^12 || ^14 || >=16} + peerDependencies: + postcss: ^8.2 + + postcss-env-function@4.0.6: + resolution: {integrity: sha512-kpA6FsLra+NqcFnL81TnsU+Z7orGtDTxcOhl6pwXeEq1yFPpRMkCDpHhrz8CFQDr/Wfm0jLiNQ1OsGGPjlqPwA==} + engines: {node: ^12 || ^14 || >=16} + peerDependencies: + postcss: ^8.4 + + postcss-flexbugs-fixes@5.0.2: + resolution: {integrity: sha512-18f9voByak7bTktR2QgDveglpn9DTbBWPUzSOe9g0N4WR/2eSt6Vrcbf0hmspvMI6YWGywz6B9f7jzpFNJJgnQ==} + peerDependencies: + postcss: ^8.1.4 + + postcss-focus-visible@6.0.4: + resolution: {integrity: sha512-QcKuUU/dgNsstIK6HELFRT5Y3lbrMLEOwG+A4s5cA+fx3A3y/JTq3X9LaOj3OC3ALH0XqyrgQIgey/MIZ8Wczw==} + engines: {node: ^12 || ^14 || >=16} + peerDependencies: + postcss: ^8.4 + + postcss-focus-within@5.0.4: + resolution: {integrity: sha512-vvjDN++C0mu8jz4af5d52CB184ogg/sSxAFS+oUJQq2SuCe7T5U2iIsVJtsCp2d6R4j0jr5+q3rPkBVZkXD9fQ==} + engines: {node: ^12 || ^14 || >=16} + peerDependencies: + postcss: ^8.4 + + postcss-font-variant@5.0.0: + resolution: {integrity: sha512-1fmkBaCALD72CK2a9i468mA/+tr9/1cBxRRMXOUaZqO43oWPR5imcyPjXwuv7PXbCid4ndlP5zWhidQVVa3hmA==} + peerDependencies: + postcss: ^8.1.0 + + postcss-gap-properties@3.0.5: + resolution: {integrity: sha512-IuE6gKSdoUNcvkGIqdtjtcMtZIFyXZhmFd5RUlg97iVEvp1BZKV5ngsAjCjrVy+14uhGBQl9tzmi1Qwq4kqVOg==} + engines: {node: ^12 || ^14 || >=16} + peerDependencies: + postcss: ^8.2 + + postcss-image-set-function@4.0.7: + resolution: {integrity: sha512-9T2r9rsvYzm5ndsBE8WgtrMlIT7VbtTfE7b3BQnudUqnBcBo7L758oc+o+pdj/dUV0l5wjwSdjeOH2DZtfv8qw==} + engines: {node: ^12 || ^14 || >=16} + peerDependencies: + postcss: ^8.2 + + postcss-import@15.1.0: + resolution: {integrity: sha512-hpr+J05B2FVYUAXHeK1YyI267J/dDDhMU6B6civm8hSY1jYJnBXxzKDKDswzJmtLHryrjhnDjqqp/49t8FALew==} + engines: {node: '>=14.0.0'} + peerDependencies: + postcss: ^8.0.0 + + postcss-initial@4.0.1: + resolution: {integrity: sha512-0ueD7rPqX8Pn1xJIjay0AZeIuDoF+V+VvMt/uOnn+4ezUKhZM/NokDeP6DwMNyIoYByuN/94IQnt5FEkaN59xQ==} + peerDependencies: + postcss: ^8.0.0 + + postcss-js@4.1.0: + resolution: {integrity: sha512-oIAOTqgIo7q2EOwbhb8UalYePMvYoIeRY2YKntdpFQXNosSu3vLrniGgmH9OKs/qAkfoj5oB3le/7mINW1LCfw==} + engines: {node: ^12 || ^14 || >= 16} + peerDependencies: + postcss: ^8.4.21 + + postcss-lab-function@4.2.1: + resolution: {integrity: sha512-xuXll4isR03CrQsmxyz92LJB2xX9n+pZJ5jE9JgcnmsCammLyKdlzrBin+25dy6wIjfhJpKBAN80gsTlCgRk2w==} + engines: {node: ^12 || ^14 || >=16} + peerDependencies: + postcss: ^8.2 + + postcss-load-config@6.0.1: + resolution: {integrity: sha512-oPtTM4oerL+UXmx+93ytZVN82RrlY/wPUV8IeDxFrzIjXOLF1pN+EmKPLbubvKHT2HC20xXsCAH2Z+CKV6Oz/g==} + engines: {node: '>= 18'} + peerDependencies: + jiti: '>=1.21.0' + postcss: '>=8.0.9' + tsx: ^4.8.1 + yaml: ^2.4.2 + peerDependenciesMeta: + jiti: + optional: true + postcss: + optional: true + tsx: + optional: true + yaml: + optional: true + + postcss-loader@6.2.1: + resolution: {integrity: sha512-WbbYpmAaKcux/P66bZ40bpWsBucjx/TTgVVzRZ9yUO8yQfVBlameJ0ZGVaPfH64hNSBh63a+ICP5nqOpBA0w+Q==} + engines: {node: '>= 12.13.0'} + peerDependencies: + postcss: ^7.0.0 || ^8.0.1 + webpack: ^5.0.0 + + postcss-logical@5.0.4: + resolution: {integrity: sha512-RHXxplCeLh9VjinvMrZONq7im4wjWGlRJAqmAVLXyZaXwfDWP73/oq4NdIp+OZwhQUMj0zjqDfM5Fj7qby+B4g==} + engines: {node: ^12 || ^14 || >=16} + peerDependencies: + postcss: ^8.4 + + postcss-media-minmax@5.0.0: + resolution: {integrity: sha512-yDUvFf9QdFZTuCUg0g0uNSHVlJ5X1lSzDZjPSFaiCWvjgsvu8vEVxtahPrLMinIDEEGnx6cBe6iqdx5YWz08wQ==} + engines: {node: '>=10.0.0'} + peerDependencies: + postcss: ^8.1.0 + + postcss-merge-longhand@5.1.7: + resolution: {integrity: sha512-YCI9gZB+PLNskrK0BB3/2OzPnGhPkBEwmwhfYk1ilBHYVAZB7/tkTHFBAnCrvBBOmeYyMYw3DMjT55SyxMBzjQ==} + engines: {node: ^10 || ^12 || >=14.0} + peerDependencies: + postcss: ^8.2.15 + + postcss-merge-rules@5.1.4: + resolution: {integrity: sha512-0R2IuYpgU93y9lhVbO/OylTtKMVcHb67zjWIfCiKR9rWL3GUk1677LAqD/BcHizukdZEjT8Ru3oHRoAYoJy44g==} + engines: {node: ^10 || ^12 || >=14.0} + peerDependencies: + postcss: ^8.2.15 + + postcss-minify-font-values@5.1.0: + resolution: {integrity: sha512-el3mYTgx13ZAPPirSVsHqFzl+BBBDrXvbySvPGFnQcTI4iNslrPaFq4muTkLZmKlGk4gyFAYUBMH30+HurREyA==} + engines: {node: ^10 || ^12 || >=14.0} + peerDependencies: + postcss: ^8.2.15 + + postcss-minify-gradients@5.1.1: + resolution: {integrity: sha512-VGvXMTpCEo4qHTNSa9A0a3D+dxGFZCYwR6Jokk+/3oB6flu2/PnPXAh2x7x52EkY5xlIHLm+Le8tJxe/7TNhzw==} + engines: {node: ^10 || ^12 || >=14.0} + peerDependencies: + postcss: ^8.2.15 + + postcss-minify-params@5.1.4: + resolution: {integrity: sha512-+mePA3MgdmVmv6g+30rn57USjOGSAyuxUmkfiWpzalZ8aiBkdPYjXWtHuwJGm1v5Ojy0Z0LaSYhHaLJQB0P8Jw==} + engines: {node: ^10 || ^12 || >=14.0} + peerDependencies: + postcss: ^8.2.15 + + postcss-minify-selectors@5.2.1: + resolution: {integrity: sha512-nPJu7OjZJTsVUmPdm2TcaiohIwxP+v8ha9NehQ2ye9szv4orirRU3SDdtUmKH+10nzn0bAyOXZ0UEr7OpvLehg==} + engines: {node: ^10 || ^12 || >=14.0} + peerDependencies: + postcss: ^8.2.15 + + postcss-modules-extract-imports@3.1.0: + resolution: {integrity: sha512-k3kNe0aNFQDAZGbin48pL2VNidTF0w4/eASDsxlyspobzU3wZQLOGj7L9gfRe0Jo9/4uud09DsjFNH7winGv8Q==} + engines: {node: ^10 || ^12 || >= 14} + peerDependencies: + postcss: ^8.1.0 + + postcss-modules-local-by-default@4.2.0: + resolution: {integrity: sha512-5kcJm/zk+GJDSfw+V/42fJ5fhjL5YbFDl8nVdXkJPLLW+Vf9mTD5Xe0wqIaDnLuL2U6cDNpTr+UQ+v2HWIBhzw==} + engines: {node: ^10 || ^12 || >= 14} + peerDependencies: + postcss: ^8.1.0 + + postcss-modules-scope@3.2.1: + resolution: {integrity: sha512-m9jZstCVaqGjTAuny8MdgE88scJnCiQSlSrOWcTQgM2t32UBe+MUmFSO5t7VMSfAf/FJKImAxBav8ooCHJXCJA==} + engines: {node: ^10 || ^12 || >= 14} + peerDependencies: + postcss: ^8.1.0 + + postcss-modules-values@4.0.0: + resolution: {integrity: sha512-RDxHkAiEGI78gS2ofyvCsu7iycRv7oqw5xMWn9iMoR0N/7mf9D50ecQqUo5BZ9Zh2vH4bCUR/ktCqbB9m8vJjQ==} + engines: {node: ^10 || ^12 || >= 14} + peerDependencies: + postcss: ^8.1.0 + + postcss-nested@6.2.0: + resolution: {integrity: sha512-HQbt28KulC5AJzG+cZtj9kvKB93CFCdLvog1WFLf1D+xmMvPGlBstkpTEZfK5+AN9hfJocyBFCNiqyS48bpgzQ==} + engines: {node: '>=12.0'} + peerDependencies: + postcss: ^8.2.14 + + postcss-nesting@10.2.0: + resolution: {integrity: sha512-EwMkYchxiDiKUhlJGzWsD9b2zvq/r2SSubcRrgP+jujMXFzqvANLt16lJANC+5uZ6hjI7lpRmI6O8JIl+8l1KA==} + engines: {node: ^12 || ^14 || >=16} + peerDependencies: + postcss: ^8.2 + + postcss-normalize-charset@5.1.0: + resolution: {integrity: sha512-mSgUJ+pd/ldRGVx26p2wz9dNZ7ji6Pn8VWBajMXFf8jk7vUoSrZ2lt/wZR7DtlZYKesmZI680qjr2CeFF2fbUg==} + engines: {node: ^10 || ^12 || >=14.0} + peerDependencies: + postcss: ^8.2.15 + + postcss-normalize-display-values@5.1.0: + resolution: {integrity: sha512-WP4KIM4o2dazQXWmFaqMmcvsKmhdINFblgSeRgn8BJ6vxaMyaJkwAzpPpuvSIoG/rmX3M+IrRZEz2H0glrQNEA==} + engines: {node: ^10 || ^12 || >=14.0} + peerDependencies: + postcss: ^8.2.15 + + postcss-normalize-positions@5.1.1: + resolution: {integrity: sha512-6UpCb0G4eofTCQLFVuI3EVNZzBNPiIKcA1AKVka+31fTVySphr3VUgAIULBhxZkKgwLImhzMR2Bw1ORK+37INg==} + engines: {node: ^10 || ^12 || >=14.0} + peerDependencies: + postcss: ^8.2.15 + + postcss-normalize-repeat-style@5.1.1: + resolution: {integrity: sha512-mFpLspGWkQtBcWIRFLmewo8aC3ImN2i/J3v8YCFUwDnPu3Xz4rLohDO26lGjwNsQxB3YF0KKRwspGzE2JEuS0g==} + engines: {node: ^10 || ^12 || >=14.0} + peerDependencies: + postcss: ^8.2.15 + + postcss-normalize-string@5.1.0: + resolution: {integrity: sha512-oYiIJOf4T9T1N4i+abeIc7Vgm/xPCGih4bZz5Nm0/ARVJ7K6xrDlLwvwqOydvyL3RHNf8qZk6vo3aatiw/go3w==} + engines: {node: ^10 || ^12 || >=14.0} + peerDependencies: + postcss: ^8.2.15 + + postcss-normalize-timing-functions@5.1.0: + resolution: {integrity: sha512-DOEkzJ4SAXv5xkHl0Wa9cZLF3WCBhF3o1SKVxKQAa+0pYKlueTpCgvkFAHfk+Y64ezX9+nITGrDZeVGgITJXjg==} + engines: {node: ^10 || ^12 || >=14.0} + peerDependencies: + postcss: ^8.2.15 + + postcss-normalize-unicode@5.1.1: + resolution: {integrity: sha512-qnCL5jzkNUmKVhZoENp1mJiGNPcsJCs1aaRmURmeJGES23Z/ajaln+EPTD+rBeNkSryI+2WTdW+lwcVdOikrpA==} + engines: {node: ^10 || ^12 || >=14.0} + peerDependencies: + postcss: ^8.2.15 + + postcss-normalize-url@5.1.0: + resolution: {integrity: sha512-5upGeDO+PVthOxSmds43ZeMeZfKH+/DKgGRD7TElkkyS46JXAUhMzIKiCa7BabPeIy3AQcTkXwVVN7DbqsiCew==} + engines: {node: ^10 || ^12 || >=14.0} + peerDependencies: + postcss: ^8.2.15 + + postcss-normalize-whitespace@5.1.1: + resolution: {integrity: sha512-83ZJ4t3NUDETIHTa3uEg6asWjSBYL5EdkVB0sDncx9ERzOKBVJIUeDO9RyA9Zwtig8El1d79HBp0JEi8wvGQnA==} + engines: {node: ^10 || ^12 || >=14.0} + peerDependencies: + postcss: ^8.2.15 + + postcss-normalize@10.0.1: + resolution: {integrity: sha512-+5w18/rDev5mqERcG3W5GZNMJa1eoYYNGo8gB7tEwaos0ajk3ZXAI4mHGcNT47NE+ZnZD1pEpUOFLvltIwmeJA==} + engines: {node: '>= 12'} + peerDependencies: + browserslist: '>= 4' + postcss: '>= 8' + + postcss-opacity-percentage@1.1.3: + resolution: {integrity: sha512-An6Ba4pHBiDtyVpSLymUUERMo2cU7s+Obz6BTrS+gxkbnSBNKSuD0AVUc+CpBMrpVPKKfoVz0WQCX+Tnst0i4A==} + engines: {node: ^12 || ^14 || >=16} + peerDependencies: + postcss: ^8.2 + + postcss-ordered-values@5.1.3: + resolution: {integrity: sha512-9UO79VUhPwEkzbb3RNpqqghc6lcYej1aveQteWY+4POIwlqkYE21HKWaLDF6lWNuqCobEAyTovVhtI32Rbv2RQ==} + engines: {node: ^10 || ^12 || >=14.0} + peerDependencies: + postcss: ^8.2.15 + + postcss-overflow-shorthand@3.0.4: + resolution: {integrity: sha512-otYl/ylHK8Y9bcBnPLo3foYFLL6a6Ak+3EQBPOTR7luMYCOsiVTUk1iLvNf6tVPNGXcoL9Hoz37kpfriRIFb4A==} + engines: {node: ^12 || ^14 || >=16} + peerDependencies: + postcss: ^8.2 + + postcss-page-break@3.0.4: + resolution: {integrity: sha512-1JGu8oCjVXLa9q9rFTo4MbeeA5FMe00/9C7lN4va606Rdb+HkxXtXsmEDrIraQ11fGz/WvKWa8gMuCKkrXpTsQ==} + peerDependencies: + postcss: ^8 + + postcss-place@7.0.5: + resolution: {integrity: sha512-wR8igaZROA6Z4pv0d+bvVrvGY4GVHihBCBQieXFY3kuSuMyOmEnnfFzHl/tQuqHZkfkIVBEbDvYcFfHmpSet9g==} + engines: {node: ^12 || ^14 || >=16} + peerDependencies: + postcss: ^8.2 + + postcss-preset-env@7.8.3: + resolution: {integrity: sha512-T1LgRm5uEVFSEF83vHZJV2z19lHg4yJuZ6gXZZkqVsqv63nlr6zabMH3l4Pc01FQCyfWVrh2GaUeCVy9Po+Aag==} + engines: {node: ^12 || ^14 || >=16} + peerDependencies: + postcss: ^8.2 + + postcss-pseudo-class-any-link@7.1.6: + resolution: {integrity: sha512-9sCtZkO6f/5ML9WcTLcIyV1yz9D1rf0tWc+ulKcvV30s0iZKS/ONyETvoWsr6vnrmW+X+KmuK3gV/w5EWnT37w==} + engines: {node: ^12 || ^14 || >=16} + peerDependencies: + postcss: ^8.2 + + postcss-reduce-initial@5.1.2: + resolution: {integrity: sha512-dE/y2XRaqAi6OvjzD22pjTUQ8eOfc6m/natGHgKFBK9DxFmIm69YmaRVQrGgFlEfc1HePIurY0TmDeROK05rIg==} + engines: {node: ^10 || ^12 || >=14.0} + peerDependencies: + postcss: ^8.2.15 + + postcss-reduce-transforms@5.1.0: + resolution: {integrity: sha512-2fbdbmgir5AvpW9RLtdONx1QoYG2/EtqpNQbFASDlixBbAYuTcJ0dECwlqNqH7VbaUnEnh8SrxOe2sRIn24XyQ==} + engines: {node: ^10 || ^12 || >=14.0} + peerDependencies: + postcss: ^8.2.15 + + postcss-replace-overflow-wrap@4.0.0: + resolution: {integrity: sha512-KmF7SBPphT4gPPcKZc7aDkweHiKEEO8cla/GjcBK+ckKxiZslIu3C4GCRW3DNfL0o7yW7kMQu9xlZ1kXRXLXtw==} + peerDependencies: + postcss: ^8.0.3 + + postcss-selector-not@6.0.1: + resolution: {integrity: sha512-1i9affjAe9xu/y9uqWH+tD4r6/hDaXJruk8xn2x1vzxC2U3J3LKO3zJW4CyxlNhA56pADJ/djpEwpH1RClI2rQ==} + engines: {node: ^12 || ^14 || >=16} + peerDependencies: + postcss: ^8.2 + + postcss-selector-parser@6.1.2: + resolution: {integrity: sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg==} + engines: {node: '>=4'} + + postcss-selector-parser@7.1.0: + resolution: {integrity: sha512-8sLjZwK0R+JlxlYcTuVnyT2v+htpdrjDOKuMcOVdYjt52Lh8hWRYpxBPoKx/Zg+bcjc3wx6fmQevMmUztS/ccA==} + engines: {node: '>=4'} + + postcss-svgo@5.1.0: + resolution: {integrity: sha512-D75KsH1zm5ZrHyxPakAxJWtkyXew5qwS70v56exwvw542d9CRtTo78K0WeFxZB4G7JXKKMbEZtZayTGdIky/eA==} + engines: {node: ^10 || ^12 || >=14.0} + peerDependencies: + postcss: ^8.2.15 + + postcss-unique-selectors@5.1.1: + resolution: {integrity: sha512-5JiODlELrz8L2HwxfPnhOWZYWDxVHWL83ufOv84NrcgipI7TaeRsatAhK4Tr2/ZiYldpK/wBvw5BD3qfaK96GA==} + engines: {node: ^10 || ^12 || >=14.0} + peerDependencies: + postcss: ^8.2.15 + + postcss-value-parser@4.2.0: + resolution: {integrity: sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==} + + postcss@7.0.39: + resolution: {integrity: sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==} + engines: {node: '>=6.0.0'} + + postcss@8.5.6: + resolution: {integrity: sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==} + engines: {node: ^10 || ^12 || >=14} + + postgres-array@2.0.0: + resolution: {integrity: sha512-VpZrUqU5A69eQyW2c5CA1jtLecCsN2U/bD6VilrFDWq5+5UIEVO7nazS3TEcHf1zuPYO/sqGvUvW62g86RXZuA==} + engines: {node: '>=4'} + + postgres-bytea@1.0.0: + resolution: {integrity: sha512-xy3pmLuQqRBZBXDULy7KbaitYqLcmxigw14Q5sj8QBVLqEwXfeybIKVWiqAXTlcvdvb0+xkOtDbfQMOf4lST1w==} + engines: {node: '>=0.10.0'} + + postgres-date@1.0.7: + resolution: {integrity: sha512-suDmjLVQg78nMK2UZ454hAG+OAW+HQPZ6n++TNDUX+L0+uUlLywnoxJKDou51Zm+zTCjrCl0Nq6J9C5hP9vK/Q==} + engines: {node: '>=0.10.0'} + + postgres-interval@1.2.0: + resolution: {integrity: sha512-9ZhXKM/rw350N1ovuWHbGxnGh/SNJ4cnxHiM0rxE4VN41wsg8P8zWn9hv/buK00RP4WvlOyr/RBDiptyxVbkZQ==} + engines: {node: '>=0.10.0'} + + prebuild-install@7.1.3: + resolution: {integrity: sha512-8Mf2cbV7x1cXPUILADGI3wuhfqWvtiLA1iclTDbFRZkgRQS0NqsPZphna9V+HyTEadheuPmjaJMsbzKQFOzLug==} + engines: {node: '>=10'} + deprecated: No longer maintained. Please contact the author of the relevant native addon; alternatives are available. + hasBin: true + + prelude-ls@1.1.2: + resolution: {integrity: sha512-ESF23V4SKG6lVSGZgYNpbsiaAkdab6ZgOxe52p7+Kid3W3u3bxR4Vfd/o21dmN7jSt0IwgZ4v5MUd26FEtXE9w==} + engines: {node: '>= 0.8.0'} + + prelude-ls@1.2.1: + resolution: {integrity: sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==} + engines: {node: '>= 0.8.0'} + + prettier-linter-helpers@1.0.0: + resolution: {integrity: sha512-GbK2cP9nraSSUF9N2XwUwqfzlAFlMNYYl+ShE/V+H8a9uNl/oUqB1w2EL54Jh0OlyRSd8RfWYJ3coVS4TROP2w==} + engines: {node: '>=6.0.0'} + + prettier@2.8.8: + resolution: {integrity: sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q==} + engines: {node: '>=10.13.0'} + hasBin: true + + prettier@3.6.2: + resolution: {integrity: sha512-I7AIg5boAr5R0FFtJ6rCfD+LFsWHp81dolrFD8S79U9tb8Az2nGrJncnMSnys+bpQJfRUzqs9hnA81OAA3hCuQ==} + engines: {node: '>=14'} + hasBin: true + + pretty-bytes@5.6.0: + resolution: {integrity: sha512-FFw039TmrBqFK8ma/7OL3sDz/VytdtJr044/QUJtH0wK9lb9jLq9tJyIxUwtQJHwar2BqtiA4iCWSwo9JLkzFg==} + engines: {node: '>=6'} + + pretty-bytes@6.1.1: + resolution: {integrity: sha512-mQUvGU6aUFQ+rNvTIAcZuWGRT9a6f6Yrg9bHs4ImKF+HZCEK+plBvnAZYSIQztknZF2qnzNtr6F8s0+IuptdlQ==} + engines: {node: ^14.13.1 || >=16.0.0} + + pretty-error@4.0.0: + resolution: {integrity: sha512-AoJ5YMAcXKYxKhuJGdcvse+Voc6v1RgnsR3nWcYU7q4t6z0Q6T86sv5Zq8VIRbOWWFpvdGE83LtdSMNd+6Y0xw==} + + pretty-format@27.5.1: + resolution: {integrity: sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ==} + engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0} + + pretty-format@28.1.3: + resolution: {integrity: sha512-8gFb/To0OmxHR9+ZTb14Df2vNxdGCX8g1xWGUTqUw5TiZvcQf5sHKObd5UcPyLLyowNwDAMTF3XWOG1B6mxl1Q==} + engines: {node: ^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0} + + pretty-format@30.2.0: + resolution: {integrity: sha512-9uBdv/B4EefsuAL+pWqueZyZS2Ba+LxfFeQ9DN14HU4bN8bhaxKdkpjpB6fs9+pSjIBu+FXQHImEg8j/Lw0+vA==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} + + pretty-quick@3.3.1: + resolution: {integrity: sha512-3b36UXfYQ+IXXqex6mCca89jC8u0mYLqFAN5eTQKoXO6oCQYcIVYZEB/5AlBHI7JPYygReM2Vv6Vom/Gln7fBg==} + engines: {node: '>=10.13'} + hasBin: true + peerDependencies: + prettier: ^2.0.0 + + prism-react-renderer@1.3.5: + resolution: {integrity: sha512-IJ+MSwBWKG+SM3b2SUfdrhC+gu01QkV2KmRQgREThBfSQRoufqRfxfHUxpG1WcaFjP+kojcFyO9Qqtpgt3qLCg==} + peerDependencies: + react: '>=0.14.9' + + prismjs@1.29.0: + resolution: {integrity: sha512-Kx/1w86q/epKcmte75LNrEoT+lX8pBpavuAbvJWRXar7Hz8jrtF+e3vY751p0R8H9HdArwaCTNDDzHg/ScJK1Q==} + engines: {node: '>=6'} + + process-nextick-args@2.0.1: + resolution: {integrity: sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==} + + process@0.11.10: + resolution: {integrity: sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==} + engines: {node: '>= 0.6.0'} + + promise-inflight@1.0.1: + resolution: {integrity: sha512-6zWPyEOFaQBJYcGMHBKTKJ3u6TBsnMFOIZSa6ce1e/ZrrsOlnHRHbabMjLiBYKp+n44X9eUI6VUPaukCXHuG4g==} + peerDependencies: + bluebird: '*' + peerDependenciesMeta: + bluebird: + optional: true + + promise-retry@2.0.1: + resolution: {integrity: sha512-y+WKFlBR8BGXnsNlIHFGPZmyDf3DFMoLhaflAnyZgV6rG6xu+JwesTo2Q9R6XwYmtmwAFCkAk3e35jEdoeh/3g==} + engines: {node: '>=10'} + + promise@7.3.1: + resolution: {integrity: sha512-nolQXZ/4L+bP/UGlkfaIujX9BKxGwmQ9OT4mOt5yvy8iK1h3wqTEJCijzGANTCCl9nWjY41juyAn2K3Q1hLLTg==} + + promise@8.3.0: + resolution: {integrity: sha512-rZPNPKTOYVNEEKFaq1HqTgOwZD+4/YHS5ukLzQCypkj+OkYx7iv0mA91lJlpPPZ8vMau3IIGj5Qlwrx+8iiSmg==} + + prompts@2.4.2: + resolution: {integrity: sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==} + engines: {node: '>= 6'} + + prop-types@15.8.1: + resolution: {integrity: sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==} + + property-expr@2.0.6: + resolution: {integrity: sha512-SVtmxhRE/CGkn3eZY1T6pC8Nln6Fr/lu1mKSgRud0eC73whjGfoAogbn78LkD8aFL0zz3bAFerKSnOl7NlErBA==} + + property-information@5.6.0: + resolution: {integrity: sha512-YUHSPk+A30YPv+0Qf8i9Mbfe/C0hdPXk1s1jPVToV8pk8BQtpw10ct89Eo7OWkutrwqvT0eicAxlOg3dOAu8JA==} + + property-information@6.5.0: + resolution: {integrity: sha512-PgTgs/BlvHxOu8QuEN7wi5A0OmXaBcHpmCSTehcs6Uuu9IkDIEo13Hy7n898RHfrQ49vKCoGeWZSaAK01nwVig==} + + protobufjs@7.4.0: + resolution: {integrity: sha512-mRUWCc3KUU4w1jU8sGxICXH/gNS94DvI1gxqDvBzhj1JpcsimQkYiOJfwsPUykUI5ZaspFbSgmBLER8IrQ3tqw==} + engines: {node: '>=12.0.0'} + + proxy-addr@2.0.7: + resolution: {integrity: sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==} + engines: {node: '>= 0.10'} + + proxy-from-env@1.0.0: + resolution: {integrity: sha512-F2JHgJQ1iqwnHDcQjVBsq3n/uoaFL+iPW/eAeL7kVxy/2RrWaN4WroKjjvbsoRtv0ftelNyC01bjRhn/bhcf4A==} + + proxy-from-env@1.1.0: + resolution: {integrity: sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==} + + ps-tree@1.2.0: + resolution: {integrity: sha512-0VnamPPYHl4uaU/nSFeZZpR21QAWRz+sRv4iW9+v/GS/J5U5iZB5BNN6J0RMoOvdx2gWM2+ZFMIm58q24e4UYA==} + engines: {node: '>= 0.10'} + hasBin: true + + pseudomap@1.0.2: + resolution: {integrity: sha512-b/YwNhb8lk1Zz2+bXXpS/LK9OisiZZ1SNsSLxN1x2OXVEhW2Ckr/7mWE5vrC1ZTiJlD9g19jWszTmJsB+oEpFQ==} + + psl@1.15.0: + resolution: {integrity: sha512-JZd3gMVBAVQkSs6HdNZo9Sdo0LNcQeMNP3CozBJb3JYC/QUYZTnKxP+f8oWRX4rHP5EurWxqAHTSwUCjlNKa1w==} + + pstree.remy@1.1.8: + resolution: {integrity: sha512-77DZwxQmxKnu3aR542U+X8FypNzbfJ+C5XQDk3uWjWxn6151aIMGthWYRXTqT1E5oJvg+ljaa2OJi+VfvCOQ8w==} + + pump@3.0.3: + resolution: {integrity: sha512-todwxLMY7/heScKmntwQG8CXVkWUOdYxIvY2s0VWAAMh/nd8SoYiRaKjlr7+iCs984f2P8zvrfWcDDYVb73NfA==} + + punycode@2.3.1: + resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==} + engines: {node: '>=6'} + + pure-color@1.3.0: + resolution: {integrity: sha512-QFADYnsVoBMw1srW7OVKEYjG+MbIa49s54w1MA1EDY6r2r/sTcKKYqRX1f4GYvnXP7eN/Pe9HFcX+hwzmrXRHA==} + + q@1.5.1: + resolution: {integrity: sha512-kV/CThkXo6xyFEZUugw/+pIOywXcDbFYgSct5cT3gqlbkBE1SJdwy6UQoZvodiWF/ckQLZyDE/Bu1M6gVu5lVw==} + engines: {node: '>=0.6.0', teleport: '>=0.2.0'} + deprecated: |- + You or someone you depend on is using Q, the JavaScript Promise library that gave JavaScript developers strong feelings about promises. They can almost certainly migrate to the native JavaScript promise now. Thank you literally everyone for joining me in this bet against the odds. Be excellent to each other. + + (For a CapTP with native promises, see @endo/eventual-send and @endo/captp) + + qs@6.13.0: + resolution: {integrity: sha512-+38qI9SOr8tfZ4QmJNplMUxqjbe7LKvvZgWdExBOmd+egZTtjLB67Gu0HRX3u/XOq7UU2Nx6nsjvS16Z9uwfpg==} + engines: {node: '>=0.6'} + + qs@6.14.0: + resolution: {integrity: sha512-YWWTjgABSKcvs/nWBi9PycY/JiPJqOD4JA6o9Sej2AtvSGarXxKC3OQSk4pAarbdQlKAh5D4FCQkJNkW+GAn3w==} + engines: {node: '>=0.6'} + + query-string@8.2.0: + resolution: {integrity: sha512-tUZIw8J0CawM5wyGBiDOAp7ObdRQh4uBor/fUR9ZjmbZVvw95OD9If4w3MQxr99rg0DJZ/9CIORcpEqU5hQG7g==} + engines: {node: '>=14.16'} + + querystringify@2.2.0: + resolution: {integrity: sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==} + + queue-microtask@1.2.3: + resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==} + + raf@3.4.1: + resolution: {integrity: sha512-Sq4CW4QhwOHE8ucn6J34MqtZCeWFP2aQSmrlroYgqAV1PjStIhJXxYuTgUIfkEk7zTLjmIjLmU5q+fbD1NnOJA==} + + randombytes@2.1.0: + resolution: {integrity: sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==} + + range-parser@1.2.1: + resolution: {integrity: sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==} + engines: {node: '>= 0.6'} + + raw-body@3.0.1: + resolution: {integrity: sha512-9G8cA+tuMS75+6G/TzW8OtLzmBDMo8p1JRxN5AZ+LAp8uxGA8V8GZm4GQ4/N5QNQEnLmg6SS7wyuSmbKepiKqA==} + engines: {node: '>= 0.10'} + + rc@1.2.8: + resolution: {integrity: sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==} + hasBin: true + + react-app-polyfill@3.0.0: + resolution: {integrity: sha512-sZ41cxiU5llIB003yxxQBYrARBqe0repqPTTYBTmMqTz9szeBbE37BehCE891NZsmdZqqP+xWKdT3eo3vOzN8w==} + engines: {node: '>=14'} + + react-base16-styling@0.6.0: + resolution: {integrity: sha512-yvh/7CArceR/jNATXOKDlvTnPKPmGZz7zsenQ3jUwLzHkNUR0CvY3yGYJbWJ/nnxsL8Sgmt5cO3/SILVuPO6TQ==} + + react-code-blocks@0.1.6: + resolution: {integrity: sha512-ENNuxG07yO+OuX1ChRje3ieefPRz6yrIpHmebQlaFQgzcAHbUfVeTINpOpoI9bSRSObeYo/OdHsporeToZ7fcg==} + engines: {node: '>=16'} + peerDependencies: + react: '>=16' + + react-color@2.19.3: + resolution: {integrity: sha512-LEeGE/ZzNLIsFWa1TMe8y5VYqr7bibneWmvJwm1pCn/eNmrabWDh659JSPn9BuaMpEfU83WTOJfnCcjDZwNQTA==} + peerDependencies: + react: '*' + + react-datepicker@4.25.0: + resolution: {integrity: sha512-zB7CSi44SJ0sqo8hUQ3BF1saE/knn7u25qEMTO1CQGofY1VAKahO8k9drZtp0cfW1DMfoYLR3uSY1/uMvbEzbg==} + peerDependencies: + react: ^16.9.0 || ^17 || ^18 + react-dom: ^16.9.0 || ^17 || ^18 + + react-dev-utils@12.0.1: + resolution: {integrity: sha512-84Ivxmr17KjUupyqzFode6xKhjwuEJDROWKJy/BthkL7Wn6NJ8h4WE6k/exAv6ImS+0oZLRRW5j/aINMHyeGeQ==} + engines: {node: '>=14'} + peerDependencies: + typescript: '>=2.7' + webpack: '>=4' + peerDependenciesMeta: + typescript: + optional: true + + react-device-detect@1.17.0: + resolution: {integrity: sha512-bBblIStwpHmoS281JFIVqeimcN3LhpoP5YKDWzxQdBIUP8S2xPvHDgizLDhUq2ScguLfVPmwfF5y268EEQR60w==} + peerDependencies: + react: '>= 0.14.0 < 18.0.0' + react-dom: '>= 0.14.0 < 18.0.0' + + react-dom@18.3.1: + resolution: {integrity: sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw==} + peerDependencies: + react: ^18.3.1 + + react-error-overlay@6.1.0: + resolution: {integrity: sha512-SN/U6Ytxf1QGkw/9ve5Y+NxBbZM6Ht95tuXNMKs8EJyFa/Vy/+Co3stop3KBHARfn/giv+Lj1uUnTfOJ3moFEQ==} + + react-fast-compare@2.0.4: + resolution: {integrity: sha512-suNP+J1VU1MWFKcyt7RtjiSWUjvidmQSlqu+eHslq+342xCbGTYmC0mEhPCOHxlW0CywylOC1u2DFAT+bv4dBw==} + + react-fast-compare@3.2.2: + resolution: {integrity: sha512-nsO+KSNgo1SbJqJEYRE9ERzo7YtYbou/OqjSQKxV7jcKox7+usiUVZOAC+XnDOABXggQTno0Y1CpVnuWEc1boQ==} + + react-frame-component@5.2.7: + resolution: {integrity: sha512-ROjHtSLoSVYUBfTieazj/nL8jIX9rZFmHC0yXEU+dx6Y82OcBEGgU9o7VyHMrBFUN9FuQ849MtIPNNLsb4krbg==} + peerDependencies: + prop-types: ^15.5.9 + react: '>= 16.3' + react-dom: '>= 16.3' + + react-inspector@6.0.2: + resolution: {integrity: sha512-x+b7LxhmHXjHoU/VrFAzw5iutsILRoYyDq97EDYdFpPLcvqtEzk4ZSZSQjnFPbr5T57tLXnHcqFYoN1pI6u8uQ==} + peerDependencies: + react: ^16.8.4 || ^17.0.0 || ^18.0.0 + + react-is@16.13.1: + resolution: {integrity: sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==} + + react-is@17.0.2: + resolution: {integrity: sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==} + + react-is@18.3.1: + resolution: {integrity: sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==} + + react-is@19.2.0: + resolution: {integrity: sha512-x3Ax3kNSMIIkyVYhWPyO09bu0uttcAIoecO/um/rKGQ4EltYWVYtyiGkS/3xMynrbVQdS69Jhlv8FXUEZehlzA==} + + react-lifecycles-compat@3.0.4: + resolution: {integrity: sha512-fBASbA6LnOU9dOU2eW7aQ8xmYBSXUIWr+UmF9b1efZBazGNO+rcXT/icdKnYm2pTwcRylVUYwW7H1PHfLekVzA==} + + react-markdown@8.0.7: + resolution: {integrity: sha512-bvWbzG4MtOU62XqBx3Xx+zB2raaFFsq4mYiAzfjXJMEz2sixgeAfraA3tvzULF02ZdOMUOKTBFFaZJDDrq+BJQ==} + peerDependencies: + '@types/react': '>=16' + react: '>=16' + + react-onclickoutside@6.13.2: + resolution: {integrity: sha512-h6Hbf1c8b7tIYY4u90mDdBLY4+AGQVMFtIE89HgC0DtVCh/JfKl477gYqUtGLmjZBKK3MJxomP/lFiLbz4sq9A==} + peerDependencies: + react: ^15.5.x || ^16.x || ^17.x || ^18.x + react-dom: ^15.5.x || ^16.x || ^17.x || ^18.x + + react-perfect-scrollbar@1.5.8: + resolution: {integrity: sha512-bQ46m70gp/HJtiBOF3gRzBISSZn8FFGNxznTdmTG8AAwpxG1bJCyn7shrgjEvGSQ5FJEafVEiosY+ccER11OSA==} + peerDependencies: + react: '>=16.3.3' + react-dom: '>=16.3.3' + + react-popper@2.3.0: + resolution: {integrity: sha512-e1hj8lL3uM+sgSR4Lxzn5h1GxBlpa4CQz0XLF8kx4MDrDRWY0Ena4c97PUeSX9i5W3UAfDP0z0FXCTQkoXUl3Q==} + peerDependencies: + '@popperjs/core': ^2.0.0 + react: ^16.8.0 || ^17 || ^18 + react-dom: ^16.8.0 || ^17 || ^18 + + react-property@2.0.0: + resolution: {integrity: sha512-kzmNjIgU32mO4mmH5+iUyrqlpFQhF8K2k7eZ4fdLSOPFrD1XgEuSBv9LDEgxRXTMBqMd8ppT0x6TIzqE5pdGdw==} + + react-redux@8.1.3: + resolution: {integrity: sha512-n0ZrutD7DaX/j9VscF+uTALI3oUPa/pO4Z3soOBIjuRn/FzVu6aehhysxZCLi6y7duMf52WNZGMl7CtuK5EnRw==} + peerDependencies: + '@types/react': ^16.8 || ^17.0 || ^18.0 + '@types/react-dom': ^16.8 || ^17.0 || ^18.0 + react: ^16.8 || ^17.0 || ^18.0 + react-dom: ^16.8 || ^17.0 || ^18.0 + react-native: '>=0.59' + redux: ^4 || ^5.0.0-beta.0 + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + react-dom: + optional: true + react-native: + optional: true + redux: + optional: true + + react-refresh@0.11.0: + resolution: {integrity: sha512-F27qZr8uUqwhWZboondsPx8tnC3Ct3SxZA3V5WyEvujRyyNv0VYPhoBg1gZ8/MV5tubQp76Trw8lTv9hzRBa+A==} + engines: {node: '>=0.10.0'} + + react-refresh@0.14.2: + resolution: {integrity: sha512-jCvmsr+1IUSMUyzOkRcvnVbX3ZYC6g9TDrDbFuFmRDq7PD4yaGbLKNQL6k2jnArV8hjYxh7hVhAZB6s9HDGpZA==} + engines: {node: '>=0.10.0'} + + react-refresh@0.17.0: + resolution: {integrity: sha512-z6F7K9bV85EfseRCp2bzrpyQ0Gkw1uLoCel9XBVWPg/TjRj94SkJzUTGfOa4bs7iJvBWtQG0Wq7wnI0syw3EBQ==} + engines: {node: '>=0.10.0'} + + react-router-dom@6.3.0: + resolution: {integrity: sha512-uaJj7LKytRxZNQV8+RbzJWnJ8K2nPsOOEuX7aQstlMZKQT0164C+X2w6bnkqU3sjtLvpd5ojrezAyfZ1+0sStw==} + peerDependencies: + react: '>=16.8' + react-dom: '>=16.8' + + react-router@6.3.0: + resolution: {integrity: sha512-7Wh1DzVQ+tlFjkeo+ujvjSqSJmkt1+8JO+T5xklPlgrh70y7ogx75ODRW0ThWhY7S+6yEDks8TYrtQe/aoboBQ==} + peerDependencies: + react: '>=16.8' + + react-scripts@5.0.1: + resolution: {integrity: sha512-8VAmEm/ZAwQzJ+GOMLbBsTdDKOpuZh7RPs0UymvBR2vRk4iZWCskjbFnxqjrzoIvlNNRZ3QJFx6/qDSi6zSnaQ==} + engines: {node: '>=14.0.0'} + hasBin: true + peerDependencies: + eslint: '*' + react: '>= 16' + typescript: ^3.2.1 || ^4 + peerDependenciesMeta: + typescript: + optional: true + + react-smooth@4.0.4: + resolution: {integrity: sha512-gnGKTpYwqL0Iii09gHobNolvX4Kiq4PKx6eWBCYYix+8cdw+cGo3do906l1NBPKkSWx1DghC1dlWG9L2uGd61Q==} + peerDependencies: + react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 + react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 + + react-syntax-highlighter@15.6.6: + resolution: {integrity: sha512-DgXrc+AZF47+HvAPEmn7Ua/1p10jNoVZVI/LoPiYdtY+OM+/nG5yefLHKJwdKqY1adMuHFbeyBaG9j64ML7vTw==} + peerDependencies: + react: '>= 0.14.0' + + react-textarea-autosize@8.5.9: + resolution: {integrity: sha512-U1DGlIQN5AwgjTyOEnI1oCcMuEr1pv1qOtklB2l4nyMGbHzWrI0eFsYK0zos2YWqAolJyG0IWJaqWmWj5ETh0A==} + engines: {node: '>=10'} + peerDependencies: + react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 + + react-toastify@11.0.5: + resolution: {integrity: sha512-EpqHBGvnSTtHYhCPLxML05NLY2ZX0JURbAdNYa6BUkk+amz4wbKBQvoKQAB0ardvSarUBuY4Q4s1sluAzZwkmA==} + peerDependencies: + react: ^18 || ^19 + react-dom: ^18 || ^19 + + react-transition-group@4.4.5: + resolution: {integrity: sha512-pZcd1MCJoiKiBR2NRxeCRg13uCXbydPnmB4EOeRrY7480qNWO8IIgQG6zlDkm6uRMsURXPuKq0GWtiM59a5Q6g==} + peerDependencies: + react: '>=16.6.0' + react-dom: '>=16.6.0' + + react@18.3.1: + resolution: {integrity: sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==} + engines: {node: '>=0.10.0'} + + reactcss@1.2.3: + resolution: {integrity: sha512-KiwVUcFu1RErkI97ywr8nvx8dNOpT03rbnma0SSalTYjkrPYaEajR4a/MRt6DZ46K6arDRbWMNHF+xH7G7n/8A==} + peerDependencies: + react: '*' + + reactflow@11.11.4: + resolution: {integrity: sha512-70FOtJkUWH3BAOsN+LU9lCrKoKbtOPnz2uq0CV2PLdNSwxTXOhCbsZr50GmZ+Rtw3jx8Uv7/vBFtCGixLfd4Og==} + peerDependencies: + react: '>=17' + react-dom: '>=17' + + read-cache@1.0.0: + resolution: {integrity: sha512-Owdv/Ft7IjOgm/i0xvNDZ1LrRANRfew4b2prF3OWMQLxLfu3bS8FVhCsrSCMK4lR56Y9ya+AThoTpDCTxCmpRA==} + + readable-stream@2.3.8: + resolution: {integrity: sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==} + + readable-stream@3.6.2: + resolution: {integrity: sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==} + engines: {node: '>= 6'} + + readdir-glob@1.1.3: + resolution: {integrity: sha512-v05I2k7xN8zXvPD9N+z/uhXPaj0sUFCe2rcWZIpBsqxfP7xXFQ0tipAd/wjj1YxWyWtUS5IDJpOG82JKt2EAVA==} + + readdirp@3.6.0: + resolution: {integrity: sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==} + engines: {node: '>=8.10.0'} + + readdirp@4.1.2: + resolution: {integrity: sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==} + engines: {node: '>= 14.18.0'} + + recharts-scale@0.4.5: + resolution: {integrity: sha512-kivNFO+0OcUNu7jQquLXAxz1FIwZj8nrj+YkOKc5694NbjCvcT6aSZiIzNzd2Kul4o4rTto8QVR9lMNtxD4G1w==} + + recharts@2.15.4: + resolution: {integrity: sha512-UT/q6fwS3c1dHbXv2uFgYJ9BMFHu3fwnd7AYZaEQhXuYQ4hgsxLvsUXzGdKeZrW5xopzDCvuA2N41WJ88I7zIw==} + engines: {node: '>=14'} + peerDependencies: + react: ^16.0.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 + react-dom: ^16.0.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 + + rechoir@0.6.2: + resolution: {integrity: sha512-HFM8rkZ+i3zrV+4LQjwQ0W+ez98pApMGM3HUrN04j3CqzPOzl9nmP15Y8YXNm8QHGv/eacOVEjqhmWpkRV0NAw==} + engines: {node: '>= 0.10'} + + recursive-readdir@2.2.3: + resolution: {integrity: sha512-8HrF5ZsXk5FAH9dgsx3BlUer73nIhuj+9OrQwEbLTPOBzGkL1lsFCR01am+v+0m2Cmbs1nP12hLDl5FA7EszKA==} + engines: {node: '>=6.0.0'} + + redent@3.0.0: + resolution: {integrity: sha512-6tDA8g98We0zd0GvVeMT9arEOnTw9qM03L9cJXaCjrip1OO764RDBLBfrB4cwzNGDj5OA5ioymC9GkizgWJDUg==} + engines: {node: '>=8'} + + redis-errors@1.2.0: + resolution: {integrity: sha512-1qny3OExCf0UvUV/5wpYKf2YwPcOqXzkwKKSmKHiE6ZMQs5heeE/c8eXK+PNllPvmjgAbfnsbpkGZWy8cBpn9w==} + engines: {node: '>=4'} + + redis-parser@3.0.0: + resolution: {integrity: sha512-DJnGAeenTdpMEH6uAJRK/uiyEIH9WVsUmoLwzudwGJUwZPp80PDBWPHXSAGNPwNvIXAbe7MSUB1zQFugFml66A==} + engines: {node: '>=4'} + + redis@4.7.1: + resolution: {integrity: sha512-S1bJDnqLftzHXHP8JsT5II/CtHWQrASX5K96REjWjlmWKrviSOLWmM7QnRLstAWsu1VBBV1ffV6DzCvxNP0UJQ==} + + redux-thunk@3.1.0: + resolution: {integrity: sha512-NW2r5T6ksUKXCabzhL9z+h206HQw/NJkcLm1GPImRQ8IzfXwRGqjVhKJGauHirT0DAuyy6hjdnMZaRoAcy0Klw==} + peerDependencies: + redux: ^5.0.0 + + redux@4.2.1: + resolution: {integrity: sha512-LAUYz4lc+Do8/g7aeRa8JkyDErK6ekstQaqWQrNRW//MY1TvCEpMtpTWvlQ+FPbWCx+Xixu/6SHt5N0HR+SB4w==} + + redux@5.0.1: + resolution: {integrity: sha512-M9/ELqF6fy8FwmkpnF0S3YKOqMyoWJ4+CS5Efg2ct3oY9daQvd/Pc71FpGZsVsbl3Cpb+IIcjBDUnnyBdQbq4w==} + + reflect-metadata@0.1.14: + resolution: {integrity: sha512-ZhYeb6nRaXCfhnndflDK8qI6ZQ/YcWZCISRAWICW9XYqMUwjZM9Z0DveWX/ABN01oxSHwVxKQmxeYZSsm0jh5A==} + + reflect.getprototypeof@1.0.10: + resolution: {integrity: sha512-00o4I+DVrefhv+nX0ulyi3biSHCPDe+yLv5o/p6d/UVlirijB8E16FtfwSAi4g3tcqrQ4lRAqQSoFEZJehYEcw==} + engines: {node: '>= 0.4'} + + refractor@3.6.0: + resolution: {integrity: sha512-MY9W41IOWxxk31o+YvFCNyNzdkc9M20NoZK5vq6jkv4I/uh2zkWcfudj0Q1fovjUQJrNewS9NMzeTtqPf+n5EA==} + + regenerate-unicode-properties@10.2.2: + resolution: {integrity: sha512-m03P+zhBeQd1RGnYxrGyDAPpWX/epKirLrp8e3qevZdVkKtnCrjjWczIbYc8+xd6vcTStVlqfycTx1KR4LOr0g==} + engines: {node: '>=4'} + + regenerate@1.4.2: + resolution: {integrity: sha512-zrceR/XhGYU/d/opr2EKO7aRHUeiBI8qjtfHqADTwZd6Szfy16la6kqD0MIUs5z5hx6AaKa+PixpPrR289+I0A==} + + regenerator-runtime@0.13.11: + resolution: {integrity: sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg==} + + regex-parser@2.3.1: + resolution: {integrity: sha512-yXLRqatcCuKtVHsWrNg0JL3l1zGfdXeEvDa0bdu4tCDQw0RpMDZsqbkyRTUnKMR0tXF627V2oEWjBEaEdqTwtQ==} + + regexp.prototype.flags@1.5.4: + resolution: {integrity: sha512-dYqgNSZbDwkaJ2ceRd9ojCGjBq+mOm9LmtXnAnEGyHhN/5R7iDW2TRw3h+o/jCFxus3P2LfWIIiwowAjANm7IA==} + engines: {node: '>= 0.4'} + + regexpu-core@6.4.0: + resolution: {integrity: sha512-0ghuzq67LI9bLXpOX/ISfve/Mq33a4aFRzoQYhnnok1JOFpmE/A2TBGkNVenOGEeSBCjIiWcc6MVOG5HEQv0sA==} + engines: {node: '>=4'} + + regjsgen@0.8.0: + resolution: {integrity: sha512-RvwtGe3d7LvWiDQXeQw8p5asZUmfU1G/l6WbUXeHta7Y2PEIvBTwH6E2EfmYUK8pxcxEdEmaomqyp0vZZ7C+3Q==} + + regjsparser@0.13.0: + resolution: {integrity: sha512-NZQZdC5wOE/H3UT28fVGL+ikOZcEzfMGk/c3iN9UGxzWHMa1op7274oyiUVrAG4B2EuFhus8SvkaYnhvW92p9Q==} + hasBin: true + + rehype-mathjax@4.0.3: + resolution: {integrity: sha512-QIwWH9U+r54nMQklVkT1qluxhKyzdPWz9dFwgel3BrseQsWZafRTDTUj8VR8/14nFuRIV2ChuCMz4zpACPoYvg==} + + relateurl@0.2.7: + resolution: {integrity: sha512-G08Dxvm4iDN3MLM0EsP62EDV9IuhXPR6blNz6Utcp7zyV3tr4HVNINt6MpaRWbxoOHT3Q7YN2P+jaHX8vUbgog==} + engines: {node: '>= 0.10'} + + remark-gfm@3.0.1: + resolution: {integrity: sha512-lEFDoi2PICJyNrACFOfDD3JlLkuSbOa5Wd8EPt06HUdptv8Gn0bxYTdbU/XXQ3swAPkEaGxxPN9cbnMHvVu1Ig==} + + remark-math@5.1.1: + resolution: {integrity: sha512-cE5T2R/xLVtfFI4cCePtiRn+e6jKMtFDR3P8V3qpv8wpKjwvHoBA4eJzvX+nVrnlNy0911bdGmuspCSwetfYHw==} + + remark-parse@10.0.2: + resolution: {integrity: sha512-3ydxgHa/ZQzG8LvC7jTXccARYDcRld3VfcgIIFs7bI6vbRSxJJmzgLEIIoYKyrfhaY+ujuWaf/PJiMZXoiCXgw==} + + remark-rehype@10.1.0: + resolution: {integrity: sha512-EFmR5zppdBp0WQeDVZ/b66CWJipB2q2VLNFMabzDSGR66Z2fQii83G5gTBbgGEnEEA0QRussvrFHxk1HWGJskw==} + + renderkid@3.0.0: + resolution: {integrity: sha512-q/7VIQA8lmM1hF+jn+sFSPWGlMkSAeNYcPLmDQx2zzuiDfaLrOmumR8iaUKlenFgh0XRPIUeSPlH3A+AW3Z5pg==} + + request-progress@3.0.0: + resolution: {integrity: sha512-MnWzEHHaxHO2iWiQuHrUPBi/1WeBf5PkxQqNyNvLl9VAYSdXkP8tQ3pBSeCPD+yw0v0Aq1zosWLz0BdeXpWwZg==} + + require-directory@2.1.1: + resolution: {integrity: sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==} + engines: {node: '>=0.10.0'} + + require-from-string@2.0.2: + resolution: {integrity: sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==} + engines: {node: '>=0.10.0'} + + requires-port@1.0.0: + resolution: {integrity: sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==} + + reselect@4.1.8: + resolution: {integrity: sha512-ab9EmR80F/zQTMNeneUr4cv+jSwPJgIlvEmVwLerwrWVbpLlBuls9XHzIeTFy4cegU2NHBp3va0LKOzU5qFEYQ==} + + reselect@5.1.1: + resolution: {integrity: sha512-K/BG6eIky/SBpzfHZv/dd+9JBFiS4SWV7FIujVyJRux6e45+73RaUHXLmIR1f7WOMaQ0U1km6qwklRQxpJJY0w==} + + resolve-cwd@3.0.0: + resolution: {integrity: sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg==} + engines: {node: '>=8'} + + resolve-from@4.0.0: + resolution: {integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==} + engines: {node: '>=4'} + + resolve-from@5.0.0: + resolution: {integrity: sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==} + engines: {node: '>=8'} + + resolve-url-loader@4.0.0: + resolution: {integrity: sha512-05VEMczVREcbtT7Bz+C+96eUO5HDNvdthIiMB34t7FcF8ehcu4wC0sSgPUubs3XW2Q3CNLJk/BJrCU9wVRymiA==} + engines: {node: '>=8.9'} + peerDependencies: + rework: 1.0.1 + rework-visit: 1.0.0 + peerDependenciesMeta: + rework: + optional: true + rework-visit: + optional: true + + resolve.exports@1.1.1: + resolution: {integrity: sha512-/NtpHNDN7jWhAaQ9BvBUYZ6YTXsRBgfqWFWP7BZBaoMJO/I3G5OFzvTuWNlZC3aPjins1F+TNrLKsGbH4rfsRQ==} + engines: {node: '>=10'} + + resolve@1.22.10: + resolution: {integrity: sha512-NPRy+/ncIMeDlTAsuqwKIiferiawhefFJtkNSW0qZJEqMEb+qBt/77B/jGeeek+F0uOeN05CDa6HXbbIgtVX4w==} + engines: {node: '>= 0.4'} + hasBin: true + + resolve@2.0.0-next.5: + resolution: {integrity: sha512-U7WjGVG9sH8tvjW5SmGbQuui75FiyjAX72HX15DwBBwF9dNiQZRQAg9nnPhYy+TUnE0+VcrttuvNI8oSxZcocA==} + hasBin: true + + restore-cursor@3.1.0: + resolution: {integrity: sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==} + engines: {node: '>=8'} + + restore-cursor@4.0.0: + resolution: {integrity: sha512-I9fPXU9geO9bHOt9pHHOhOkYerIMsmVaWB0rA2AI9ERh/+x/i7MV5HKBNrg+ljO5eoPVgCcnFuRjJ9uH6I/3eg==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + + retry@0.12.0: + resolution: {integrity: sha512-9LkiTwjUh6rT555DtE9rTX+BKByPfrMzEAtnlEtdEwr3Nkffwiihqe2bWADg+OQRjt9gl6ICdmB/ZFDCGAtSow==} + engines: {node: '>= 4'} + + retry@0.13.1: + resolution: {integrity: sha512-XQBQ3I8W1Cge0Seh+6gjj03LbmRFWuoszgK9ooCpwYIrhhoO80pfq4cUkU5DkknwfOfFteRwlZ56PYOGYyFWdg==} + engines: {node: '>= 4'} + + reusify@1.1.0: + resolution: {integrity: sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==} + engines: {iojs: '>=1.0.0', node: '>=0.10.0'} + + rfdc@1.4.1: + resolution: {integrity: sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA==} + + rimraf@3.0.2: + resolution: {integrity: sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==} + deprecated: Rimraf versions prior to v4 are no longer supported + hasBin: true + + rimraf@5.0.10: + resolution: {integrity: sha512-l0OE8wL34P4nJH/H2ffoaniAokM2qSmrtXHmlpvYr5AVVX8msAyW0l8NVJFDxlSK4u3Uh/f41cQheDVdnYijwQ==} + hasBin: true + + rollup-plugin-terser@7.0.2: + resolution: {integrity: sha512-w3iIaU4OxcF52UUXiZNsNeuXIMDvFrr+ZXK6bFZ0Q60qyVfq4uLptoS4bbq3paG3x216eQllFZX7zt6TIImguQ==} + deprecated: This package has been deprecated and is no longer maintained. Please use @rollup/plugin-terser + peerDependencies: + rollup: 4.45.0 + + rollup@4.45.0: + resolution: {integrity: sha512-WLjEcJRIo7i3WDDgOIJqVI2d+lAC3EwvOGy+Xfq6hs+GQuAA4Di/H72xmXkOhrIWFg2PFYSKZYfH0f4vfKXN4A==} + engines: {node: '>=18.0.0', npm: '>=8.0.0'} + hasBin: true + + run-applescript@5.0.0: + resolution: {integrity: sha512-XcT5rBksx1QdIhlFOCtgZkB99ZEouFZ1E2Kc2LHqNW13U3/74YGdkQRmThTwxy4QIyookibDKYZOPqX//6BlAg==} + engines: {node: '>=12'} + + run-parallel@1.2.0: + resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==} + + run-script-os@1.1.6: + resolution: {integrity: sha512-ql6P2LzhBTTDfzKts+Qo4H94VUKpxKDFz6QxxwaUZN0mwvi7L3lpOI7BqPCq7lgDh3XLl0dpeXwfcVIitlrYrw==} + hasBin: true + + rxjs@7.8.2: + resolution: {integrity: sha512-dhKf903U/PQZY6boNNtAGdWbG85WAbjT/1xYoZIC7FAY0yWapOBQVsVrDl58W86//e1VpMNBtRV4MaXfdMySFA==} + + sade@1.8.1: + resolution: {integrity: sha512-xal3CZX1Xlo/k4ApwCFrHVACi9fBqJ7V+mwhBsuf/1IOKbBy098Fex+Wa/5QMubw09pSZ/u8EY8PWgevJsXp1A==} + engines: {node: '>=6'} + + safe-array-concat@1.1.3: + resolution: {integrity: sha512-AURm5f0jYEOydBj7VQlVvDrjeFgthDdEF5H1dP+6mNpoXOMo1quQqJ4wvJDyRZ9+pO3kGWoOdmV08cSv2aJV6Q==} + engines: {node: '>=0.4'} + + safe-buffer@5.1.2: + resolution: {integrity: sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==} + + safe-buffer@5.2.1: + resolution: {integrity: sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==} + + safe-push-apply@1.0.0: + resolution: {integrity: sha512-iKE9w/Z7xCzUMIZqdBsp6pEQvwuEebH4vdpjcDWnyzaI6yl6O9FHvVpmGelvEHNsoY6wGblkxR6Zty/h00WiSA==} + engines: {node: '>= 0.4'} + + safe-regex-test@1.1.0: + resolution: {integrity: sha512-x/+Cz4YrimQxQccJf5mKEbIa1NzeCRNI5Ecl/ekmlYaampdNLPalVyIcCZNNH3MvmqBugV5TMYZXv0ljslUlaw==} + engines: {node: '>= 0.4'} + + safe-stable-stringify@2.5.0: + resolution: {integrity: sha512-b3rppTKm9T+PsVCBEOUR46GWI7fdOs00VKZ1+9c1EWDaDMvjQc6tUwuFyIprgGgTcWoVHSKrU8H31ZHA2e0RHA==} + engines: {node: '>=10'} + + safer-buffer@2.1.2: + resolution: {integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==} + + sanitize-html@2.17.0: + resolution: {integrity: sha512-dLAADUSS8rBwhaevT12yCezvioCA+bmUTPH/u57xKPT8d++voeYE6HeluA/bPbQ15TwDBG2ii+QZIEmYx8VdxA==} + + sanitize.css@13.0.0: + resolution: {integrity: sha512-ZRwKbh/eQ6w9vmTjkuG0Ioi3HBwPFce0O+v//ve+aOq1oeCy7jMV2qzzAlpsNuqpqCBjjriM1lbtZbF/Q8jVyA==} + + sass-loader@12.6.0: + resolution: {integrity: sha512-oLTaH0YCtX4cfnJZxKSLAyglED0naiYfNG1iXfU5w1LNZ+ukoA5DtyDIN5zmKVZwYNJP4KRc5Y3hkWga+7tYfA==} + engines: {node: '>= 12.13.0'} + peerDependencies: + fibers: '>= 3.1.0' + node-sass: ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 + sass: ^1.3.0 + sass-embedded: '*' + webpack: ^5.0.0 + peerDependenciesMeta: + fibers: + optional: true + node-sass: + optional: true + sass: + optional: true + sass-embedded: + optional: true + + sass@1.93.2: + resolution: {integrity: sha512-t+YPtOQHpGW1QWsh1CHQ5cPIr9lbbGZLZnbihP/D/qZj/yuV68m8qarcV17nvkOX81BCrvzAlq2klCQFZghyTg==} + engines: {node: '>=14.0.0'} + hasBin: true + + sax@1.2.4: + resolution: {integrity: sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==} + + saxes@5.0.1: + resolution: {integrity: sha512-5LBh1Tls8c9xgGjw3QrMwETmTMVk0oFgvrFSvWx62llR2hcEInrKNZ2GZCCuuy2lvWrdl5jhbpeqc5hRYKFOcw==} + engines: {node: '>=10'} + + saxes@6.0.0: + resolution: {integrity: sha512-xAg7SOnEhrm5zI3puOOKyy1OMcMlIJZYNJY7xLBwSze0UjhPLnWfj2GF2EpT0jmzaJKIWKHLsaSSajf35bcYnA==} + engines: {node: '>=v12.22.7'} + + scheduler@0.23.2: + resolution: {integrity: sha512-UOShsPwz7NrMUqhR6t0hWjFduvOzbtv7toDH1/hIrfRNIDBnnBWd0CwJTGvTpngVlmwGCdP9/Zl/tVrDqcuYzQ==} + + schema-utils@2.7.0: + resolution: {integrity: sha512-0ilKFI6QQF5nxDZLFn2dMjvc4hjg/Wkg7rHd3jK6/A4a1Hl9VFdQWvgB1UMGoU94pad1P/8N7fMcEnLnSiju8A==} + engines: {node: '>= 8.9.0'} + + schema-utils@2.7.1: + resolution: {integrity: sha512-SHiNtMOUGWBQJwzISiVYKu82GiV4QYGePp3odlY1tuKO7gPtphAT5R/py0fA6xtbgLL/RvtJZnU9b8s0F1q0Xg==} + engines: {node: '>= 8.9.0'} + + schema-utils@3.3.0: + resolution: {integrity: sha512-pN/yOAvcC+5rQ5nERGuwrjLlYvLTbCibnZ1I7B1LaiAz9BRBlE9GMgE/eqV30P7aJQUf7Ddimy/RsbYO/GrVGg==} + engines: {node: '>= 10.13.0'} + + schema-utils@4.3.3: + resolution: {integrity: sha512-eflK8wEtyOE6+hsaRVPxvUKYCpRgzLqDTb8krvAsRIwOGlHoSgYLgBXoubGgLd2fT41/OUYdb48v4k4WWHQurA==} + engines: {node: '>= 10.13.0'} + + select-hose@2.0.0: + resolution: {integrity: sha512-mEugaLK+YfkijB4fx0e6kImuJdCIt2LxCRcbEYPqRGCs4F2ogyfZU5IAZRdjCP8JPq2AtdNoC/Dux63d9Kiryg==} + + selfsigned@2.4.1: + resolution: {integrity: sha512-th5B4L2U+eGLq1TVh7zNRGBapioSORUeymIydxgFpwww9d2qyKvtuPU2jJuHvYAwwqi2Y596QBL3eEqcPEYL8Q==} + engines: {node: '>=10'} + + semver@7.7.1: + resolution: {integrity: sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA==} + engines: {node: '>=10'} + hasBin: true + + send@0.19.0: + resolution: {integrity: sha512-dW41u5VfLXu8SJh5bwRmyYUbAoSB3c9uQh6L8h/KtsFREPWpbX1lrljJo186Jc4nmci/sGUZ9a0a0J2zgfq2hw==} + engines: {node: '>= 0.8.0'} + + seq-queue@0.0.5: + resolution: {integrity: sha512-hr3Wtp/GZIc/6DAGPDcV4/9WoZhjrkXsi5B/07QgX8tsdc6ilr7BFM6PM6rbdAX1kFSDYeZGLipIZZKyQP0O5Q==} + + serialize-javascript@4.0.0: + resolution: {integrity: sha512-GaNA54380uFefWghODBWEGisLZFj00nS5ACs6yHa9nLqlLpVLO8ChDGeKRjZnV4Nh4n0Qi7nhYZD/9fCPzEqkw==} + + serialize-javascript@6.0.2: + resolution: {integrity: sha512-Saa1xPByTTq2gdeFZYLLo+RFE35NHZkAbqZeWNd3BpzppeVisAqpDjcp8dyf6uIvEqJRd46jemmyA4iFIeVk8g==} + + seroval-plugins@1.3.3: + resolution: {integrity: sha512-16OL3NnUBw8JG1jBLUoZJsLnQq0n5Ua6aHalhJK4fMQkz1lqR7Osz1sA30trBtd9VUDc2NgkuRCn8+/pBwqZ+w==} + engines: {node: '>=10'} + peerDependencies: + seroval: ^1.0 + + seroval@1.3.2: + resolution: {integrity: sha512-RbcPH1n5cfwKrru7v7+zrZvjLurgHhGyso3HTyGtRivGWgYjbOmGuivCQaORNELjNONoK35nj28EoWul9sb1zQ==} + engines: {node: '>=10'} + + serve-index@1.9.1: + resolution: {integrity: sha512-pXHfKNP4qujrtteMrSBb0rc8HJ9Ms/GrXwcUtUtD5s4ewDJI8bT3Cz2zTVRMKtri49pLx2e0Ya8ziP5Ya2pZZw==} + engines: {node: '>= 0.8.0'} + + serve-static@1.16.2: + resolution: {integrity: sha512-VqpjJZKadQB/PEbEwvFdO43Ax5dFBZ2UECszz8bQ7pi7wt//PWe1P6MN7eCnjsatYtBT6EuiClbjSWP2WrIoTw==} + engines: {node: '>= 0.8.0'} + + set-blocking@2.0.0: + resolution: {integrity: sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==} + + set-function-length@1.2.2: + resolution: {integrity: sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==} + engines: {node: '>= 0.4'} + + set-function-name@2.0.2: + resolution: {integrity: sha512-7PGFlmtwsEADb0WYyvCMa1t+yke6daIG4Wirafur5kcf+MhUnPms1UeR0CKQdTZD81yESwMHbtn+TR+dMviakQ==} + engines: {node: '>= 0.4'} + + set-proto@1.0.0: + resolution: {integrity: sha512-RJRdvCo6IAnPdsvP/7m6bsQqNnn1FCBX5ZNtFL98MmFF/4xAIJTIg1YbHW5DC2W5SKZanrC6i4HsJqlajw/dZw==} + engines: {node: '>= 0.4'} + + setimmediate@1.0.5: + resolution: {integrity: sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA==} + + setprototypeof@1.1.0: + resolution: {integrity: sha512-BvE/TwpZX4FXExxOxZyRGQQv651MSwmWKZGqvmPcRIjDqWub67kTKuIMx43cZZrS/cBBzwBcNDWoFxt2XEFIpQ==} + + setprototypeof@1.2.0: + resolution: {integrity: sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==} + + sha.js@2.4.12: + resolution: {integrity: sha512-8LzC5+bvI45BjpfXU8V5fdU2mfeKiQe1D1gIMn7XUlF3OTUrpdJpPPH4EMAnF0DsHHdSZqCdSss5qCmJKuiO3w==} + engines: {node: '>= 0.10'} + hasBin: true + + shebang-command@2.0.0: + resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==} + engines: {node: '>=8'} + + shebang-regex@3.0.0: + resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==} + engines: {node: '>=8'} + + shell-exec@1.0.2: + resolution: {integrity: sha512-jyVd+kU2X+mWKMmGhx4fpWbPsjvD53k9ivqetutVW/BQ+WIZoDoP4d8vUMGezV6saZsiNoW2f9GIhg9Dondohg==} + + shell-quote@1.8.3: + resolution: {integrity: sha512-ObmnIF4hXNg1BqhnHmgbDETF8dLPCggZWBjkQfhZpbszZnYur5DUljTcCHii5LC3J5E0yeO/1LIMyH+UvHQgyw==} + engines: {node: '>= 0.4'} + + shelljs@0.8.5: + resolution: {integrity: sha512-TiwcRcrkhHvbrZbnRcFYMLl30Dfov3HKqzp5tO5b4pt6G/SezKcYhmDg15zXVBswHmctSAQKznqNW2LO5tTDow==} + engines: {node: '>=4'} + hasBin: true + + shx@0.3.4: + resolution: {integrity: sha512-N6A9MLVqjxZYcVn8hLmtneQWIJtp8IKzMP4eMnx+nqkvXoqinUPCbUFLp2UcWTEIUONhlk0ewxr/jaVGlc+J+g==} + engines: {node: '>=6'} + hasBin: true + + side-channel-list@1.0.0: + resolution: {integrity: sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==} + engines: {node: '>= 0.4'} + + side-channel-map@1.0.1: + resolution: {integrity: sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==} + engines: {node: '>= 0.4'} + + side-channel-weakmap@1.0.2: + resolution: {integrity: sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==} + engines: {node: '>= 0.4'} + + side-channel@1.1.0: + resolution: {integrity: sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==} + engines: {node: '>= 0.4'} + + signal-exit@3.0.7: + resolution: {integrity: sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==} + + signal-exit@4.1.0: + resolution: {integrity: sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==} + engines: {node: '>=14'} + + simple-concat@1.0.1: + resolution: {integrity: sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q==} + + simple-get@3.1.1: + resolution: {integrity: sha512-CQ5LTKGfCpvE1K0n2us+kuMPbk/q0EKl82s4aheV9oXjFEz6W/Y7oQFVJuU6QG77hRT4Ghb5RURteF5vnWjupA==} + + simple-get@4.0.1: + resolution: {integrity: sha512-brv7p5WgH0jmQJr1ZDDfKDOSeWWg+OVypG99A/5vYGPqJ6pxiaHLy8nxtFjBA7oMa01ebA9gfh1uMCFqOuXxvA==} + + simple-update-notifier@1.1.0: + resolution: {integrity: sha512-VpsrsJSUcJEseSbMHkrsrAVSdvVS5I96Qo1QAQ4FxQ9wXFcB+pjj7FB7/us9+GcgfW4ziHtYMc1J0PLczb55mg==} + engines: {node: '>=8.10.0'} + + sisteransi@1.0.5: + resolution: {integrity: sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==} + + slash@3.0.0: + resolution: {integrity: sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==} + engines: {node: '>=8'} + + slash@4.0.0: + resolution: {integrity: sha512-3dOsAHXXUkQTpOYcoAxLIorMTp4gIQr5IW3iVb7A7lFIp0VHhnynm9izx6TssdrIcVIESAlVjtnO2K8bg+Coew==} + engines: {node: '>=12'} + + slice-ansi@3.0.0: + resolution: {integrity: sha512-pSyv7bSTC7ig9Dcgbw9AuRNUb5k5V6oDudjZoMBSr13qpLBG7tB+zgCkARjq7xIUgdz5P1Qe8u+rSGdouOOIyQ==} + engines: {node: '>=8'} + + slice-ansi@4.0.0: + resolution: {integrity: sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ==} + engines: {node: '>=10'} + + slice-ansi@5.0.0: + resolution: {integrity: sha512-FC+lgizVPfie0kkhqUScwRu1O/lF6NOgJmlCgK+/LYxDCTk8sGelYaHDhFcDN+Sn3Cv+3VSa4Byeo+IMCzpMgQ==} + engines: {node: '>=12'} + + smart-buffer@4.2.0: + resolution: {integrity: sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==} + engines: {node: '>= 6.0.0', npm: '>= 3.0.0'} + + socket.io-adapter@2.5.5: + resolution: {integrity: sha512-eLDQas5dzPgOWCk9GuuJC2lBqItuhKI4uxGgo9aIV7MYbk2h9Q6uULEh8WBzThoI7l+qU9Ast9fVUmkqPP9wYg==} + + socket.io-client@4.8.1: + resolution: {integrity: sha512-hJVXfu3E28NmzGk8o1sHhN3om52tRvwYeidbj7xKy2eIIse5IoKX3USlS6Tqt3BHAtflLIkCQBkzVrEEfWUyYQ==} + engines: {node: '>=10.0.0'} + + socket.io-parser@4.2.4: + resolution: {integrity: sha512-/GbIKmo8ioc+NIWIhwdecY0ge+qVBSMdgxGygevmdHj24bsfgtCmcUUcQ5ZzcylGFHsN3k4HB4Cgkl96KVnuew==} + engines: {node: '>=10.0.0'} + + socket.io@4.8.1: + resolution: {integrity: sha512-oZ7iUCxph8WYRHHcjBEc9unw3adt5CmSNlppj/5Q4k2RIrhl8Z5yY2Xr4j9zj0+wzVZ0bxmYoGSzKJnRl6A4yg==} + engines: {node: '>=10.2.0'} + + sockjs@0.3.24: + resolution: {integrity: sha512-GJgLTZ7vYb/JtPSSZ10hsOYIvEYsjbNU+zPdIHcUaWVNUEPivzxku31865sSSud0Da0W4lEeOPlmw93zLQchuQ==} + + socks-proxy-agent@6.2.1: + resolution: {integrity: sha512-a6KW9G+6B3nWZ1yB8G7pJwL3ggLy1uTzKAgCb7ttblwqdz9fMGJUuTy3uFzEP48FAs9FLILlmzDlE2JJhVQaXQ==} + engines: {node: '>= 10'} + + socks@2.8.7: + resolution: {integrity: sha512-HLpt+uLy/pxB+bum/9DzAgiKS8CX1EvbWxI4zlmgGCExImLdiad2iCwXT5Z4c9c3Eq8rP2318mPW2c+QbtjK8A==} + engines: {node: '>= 10.0.0', npm: '>= 3.0.0'} + + solid-element@1.7.0: + resolution: {integrity: sha512-VUMNqunL3acgtpqbiI9bbUwOyXyz9cAHvGy1Zki1znx+gZJYFgjzckpjpTb2u/Gxud8LSYL+LRTgMGIHdUY4bg==} + peerDependencies: + solid-js: 1.9.7 + + solid-js@1.9.7: + resolution: {integrity: sha512-/saTKi8iWEM233n5OSi1YHCCuh66ZIQ7aK2hsToPe4tqGm7qAejU1SwNuTPivbWAYq7SjuHVVYxxuZQNRbICiw==} + + source-list-map@2.0.1: + resolution: {integrity: sha512-qnQ7gVMxGNxsiL4lEuJwe/To8UnK7fAnmbGEEH8RpLouuKbeEm0lhbQVFIrNSuB+G7tVrAlVsZgETT5nljf+Iw==} + + source-map-js@1.2.1: + resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==} + engines: {node: '>=0.10.0'} + + source-map-loader@3.0.2: + resolution: {integrity: sha512-BokxPoLjyl3iOrgkWaakaxqnelAJSS+0V+De0kKIq6lyWrXuiPgYTGp6z3iHmqljKAaLXwZa+ctD8GccRJeVvg==} + engines: {node: '>= 12.13.0'} + peerDependencies: + webpack: ^5.0.0 + + source-map-support@0.5.21: + resolution: {integrity: sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==} + + source-map@0.5.7: + resolution: {integrity: sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ==} + engines: {node: '>=0.10.0'} + + source-map@0.6.1: + resolution: {integrity: sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==} + engines: {node: '>=0.10.0'} + + source-map@0.7.6: + resolution: {integrity: sha512-i5uvt8C3ikiWeNZSVZNWcfZPItFQOsYTUAOkcUPGd8DqDy1uOUikjt5dG+uRlwyvR108Fb9DOd4GvXfT0N2/uQ==} + engines: {node: '>= 12'} + + source-map@0.8.0-beta.0: + resolution: {integrity: sha512-2ymg6oRBpebeZi9UUNsgQ89bhx01TcTkmNTGnNO88imTmbSgy4nfujrgVEFKWpMTEGA11EDkTt7mqObTPdigIA==} + engines: {node: '>= 8'} + deprecated: The work that was done in this beta branch won't be included in future versions + + sourcemap-codec@1.4.8: + resolution: {integrity: sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA==} + deprecated: Please use @jridgewell/sourcemap-codec instead + + space-separated-tokens@1.1.5: + resolution: {integrity: sha512-q/JSVd1Lptzhf5bkYm4ob4iWPjx0KiRe3sRFBNrVqbJkFaBm5vbbowy1mymoPNLRa52+oadOhJ+K49wsSeSjTA==} + + space-separated-tokens@2.0.2: + resolution: {integrity: sha512-PEGlAwrG8yXGXRjW32fGbg66JAlOAwbObuqVoJpv/mRgoWDQfgH1wDPvtzWyUSNAXBGSk8h755YDbbcEy3SH2Q==} + + sparse-bitfield@3.0.3: + resolution: {integrity: sha512-kvzhi7vqKTfkh0PZU+2D2PIllw2ymqJKujUcyPMd9Y75Nv4nPbGJZXNhxsgdQab2BmlDct1YnfQCguEvHr7VsQ==} + + spawn-command@0.0.2: + resolution: {integrity: sha512-zC8zGoGkmc8J9ndvml8Xksr1Amk9qBujgbF0JAIWO7kXr43w0h/0GJNM/Vustixu+YE8N/MTrQ7N31FvHUACxQ==} + + spdy-transport@3.0.0: + resolution: {integrity: sha512-hsLVFE5SjA6TCisWeJXFKniGGOpBgMLmerfO2aCyCU5s7nJ/rpAepqmFifv/GCbSbueEeAJJnmSQ2rKC/g8Fcw==} + + spdy@4.0.2: + resolution: {integrity: sha512-r46gZQZQV+Kl9oItvl1JZZqJKGr+oEkB08A6BzkiR7593/7IbtuncXHd2YoYeTsG4157ZssMu9KYvUHLcjcDoA==} + engines: {node: '>=6.0.0'} + + speech-rule-engine@4.1.2: + resolution: {integrity: sha512-S6ji+flMEga+1QU79NDbwZ8Ivf0S/MpupQQiIC0rTpU/ZTKgcajijJJb1OcByBQDjrXCN1/DJtGz4ZJeBMPGJw==} + hasBin: true + + split-on-first@3.0.0: + resolution: {integrity: sha512-qxQJTx2ryR0Dw0ITYyekNQWpz6f8dGd7vffGNflQQ3Iqj9NJ6qiZ7ELpZsJ/QBhIVAiDfXdag3+Gp8RvWa62AA==} + engines: {node: '>=12'} + + split2@4.2.0: + resolution: {integrity: sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==} + engines: {node: '>= 10.x'} + + split@0.3.3: + resolution: {integrity: sha512-wD2AeVmxXRBoX44wAycgjVpMhvbwdI2aZjCkvfNcH1YqHQvJVa1duWc73OyVGJUc05fhFaTZeQ/PYsrmyH0JVA==} + + sprintf-js@1.0.3: + resolution: {integrity: sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==} + + sql-highlight@6.1.0: + resolution: {integrity: sha512-ed7OK4e9ywpE7pgRMkMQmZDPKSVdm0oX5IEtZiKnFucSF0zu6c80GZBe38UqHuVhTWJ9xsKgSMjCG2bml86KvA==} + engines: {node: '>=14'} + + sqlite3@5.1.7: + resolution: {integrity: sha512-GGIyOiFaG+TUra3JIfkI/zGP8yZYLPQ0pl1bH+ODjiX57sPhrLU5sQJn1y9bDKZUFYkX1crlrPfSYt0BKKdkog==} + + sqlstring@2.3.3: + resolution: {integrity: sha512-qC9iz2FlN7DQl3+wjwn3802RTyjCx7sDvfQEXchwa6CWOx07/WVfh91gBmQ9fahw8snwGEWU3xGzOt4tFyHLxg==} + engines: {node: '>= 0.6'} + + sshpk@1.18.0: + resolution: {integrity: sha512-2p2KJZTSqQ/I3+HX42EpYOa2l3f8Erv8MWKsy2I9uf4wA7yFIkXRffYdsx86y6z4vHtV8u7g+pPlr8/4ouAxsQ==} + engines: {node: '>=0.10.0'} + hasBin: true + + ssri@8.0.1: + resolution: {integrity: sha512-97qShzy1AiyxvPNIkLWoGua7xoQzzPjQ0HAH4B0rWKo7SZ6USuPcrUiAFrws0UH8RrbWmgq3LMTObhPIHbbBeQ==} + engines: {node: '>= 8'} + + stable@0.1.8: + resolution: {integrity: sha512-ji9qxRnOVfcuLDySj9qzhGSEFVobyt1kIOSkj1qZzYLzq7Tos/oUUWvotUPQLlrsidqsK6tBH89Bc9kL5zHA6w==} + deprecated: 'Modern JS already guarantees Array#sort() is a stable sort, so this library is deprecated. See the compatibility table on MDN: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/sort#browser_compatibility' + + stack-trace@0.0.10: + resolution: {integrity: sha512-KGzahc7puUKkzyMt+IqAep+TVNbKP+k2Lmwhub39m1AsTSkaDutx56aDCo+HLDzf/D26BIHTJWNiTG1KAJiQCg==} + + stack-utils@2.0.6: + resolution: {integrity: sha512-XlkWvfIm6RmsWtNJx+uqtKLS8eqFbxUg0ZzLXqY0caEy9l7hruX8IpiDnjsLavoBgqCCR71TqWO8MaXYheJ3RQ==} + engines: {node: '>=10'} + + stackframe@1.3.4: + resolution: {integrity: sha512-oeVtt7eWQS+Na6F//S4kJ2K2VbRlS9D43mAlMyVpVWovy9o+jfgH8O9agzANzaiLjclA0oYzUXEM4PurhSUChw==} + + standard-as-callback@2.1.0: + resolution: {integrity: sha512-qoRRSyROncaz1z0mvYqIE4lCd9p2R90i6GxW3uZv5ucSu8tU7B5HXUP1gG8pVZsYNVaXjk8ClXHPttLyxAL48A==} + + start-server-and-test@2.1.2: + resolution: {integrity: sha512-OIjfo3G6QV9Sh6IlMqj58oZwVhPVuU/l6uVACG7YNE9kAfDvcYoPThtb0NNT3tZMMC3wOYbXnC15yiCSNFkdRg==} + engines: {node: '>=16'} + hasBin: true + + static-eval@2.0.2: + resolution: {integrity: sha512-N/D219Hcr2bPjLxPiV+TQE++Tsmrady7TqAJugLy7Xk1EumfDWS/f5dtBbkRCGE7wKKXuYockQoj8Rm2/pVKyg==} + + statuses@1.5.0: + resolution: {integrity: sha512-OpZ3zP+jT1PI7I8nemJX4AKmAX070ZkYPVWV/AaKTJl+tXCTGyVdC1a4SL8RUQYEwk/f34ZX8UTykN68FwrqAA==} + engines: {node: '>= 0.6'} + + statuses@2.0.1: + resolution: {integrity: sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==} + engines: {node: '>= 0.8'} + + stop-iteration-iterator@1.1.0: + resolution: {integrity: sha512-eLoXW/DHyl62zxY4SCaIgnRhuMr6ri4juEYARS8E6sCEqzKpOiE521Ucofdx+KnDZl5xmvGYaaKCk5FEOxJCoQ==} + engines: {node: '>= 0.4'} + + stream-combiner@0.0.4: + resolution: {integrity: sha512-rT00SPnTVyRsaSz5zgSPma/aHSOic5U1prhYdRy5HS2kTZviFpmDgzilbtsJsxiroqACmayynDN/9VzIbX5DOw==} + + streamsearch@1.1.0: + resolution: {integrity: sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg==} + engines: {node: '>=10.0.0'} + + streamx@2.23.0: + resolution: {integrity: sha512-kn+e44esVfn2Fa/O0CPFcex27fjIL6MkVae0Mm6q+E6f0hWv578YCERbv+4m02cjxvDsPKLnmxral/rR6lBMAg==} + + string-argv@0.3.2: + resolution: {integrity: sha512-aqD2Q0144Z+/RqG52NeHEkZauTAUWJO8c6yTftGJKO3Tja5tUgIfmIl6kExvhtxSDP7fXB6DvzkfMpCd/F3G+Q==} + engines: {node: '>=0.6.19'} + + string-length@4.0.2: + resolution: {integrity: sha512-+l6rNN5fYHNhZZy41RXsYptCjA2Igmq4EG7kZAYFQI1E1VTXarr6ZPXBg6eq7Y6eK4FEhY6AJlyuFIb/v/S0VQ==} + engines: {node: '>=10'} + + string-length@5.0.1: + resolution: {integrity: sha512-9Ep08KAMUn0OadnVaBuRdE2l615CQ508kr0XMadjClfYpdCyvrbFp6Taebo8yyxokQ4viUd/xPPUA4FGgUa0ow==} + engines: {node: '>=12.20'} + + string-natural-compare@3.0.1: + resolution: {integrity: sha512-n3sPwynL1nwKi3WJ6AIsClwBMa0zTi54fn2oLU6ndfTSIO05xaznjSf15PcBZU6FNWbmN5Q6cxT4V5hGvB4taw==} + + string-width@4.2.3: + resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==} + engines: {node: '>=8'} + + string-width@5.1.2: + resolution: {integrity: sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==} + engines: {node: '>=12'} + + string.prototype.includes@2.0.1: + resolution: {integrity: sha512-o7+c9bW6zpAdJHTtujeePODAhkuicdAryFsfVKwA+wGw89wJ4GTY484WTucM9hLtDEOpOvI+aHnzqnC5lHp4Rg==} + engines: {node: '>= 0.4'} + + string.prototype.matchall@4.0.12: + resolution: {integrity: sha512-6CC9uyBL+/48dYizRf7H7VAYCMCNTBeM78x/VTUe9bFEaxBepPJDa1Ow99LqI/1yF7kuy7Q3cQsYMrcjGUcskA==} + engines: {node: '>= 0.4'} + + string.prototype.repeat@1.0.0: + resolution: {integrity: sha512-0u/TldDbKD8bFCQ/4f5+mNRrXwZ8hg2w7ZR8wa16e8z9XpePWl3eGEcUD0OXpEH/VJH/2G3gjUtR3ZOiBe2S/w==} + + string.prototype.trim@1.2.10: + resolution: {integrity: sha512-Rs66F0P/1kedk5lyYyH9uBzuiI/kNRmwJAR9quK6VOtIpZ2G+hMZd+HQbbv25MgCA6gEffoMZYxlTod4WcdrKA==} + engines: {node: '>= 0.4'} + + string.prototype.trimend@1.0.9: + resolution: {integrity: sha512-G7Ok5C6E/j4SGfyLCloXTrngQIQU3PWtXGst3yM7Bea9FRURf1S42ZHlZZtsNque2FN2PoUhfZXYLNWwEr4dLQ==} + engines: {node: '>= 0.4'} + + string.prototype.trimstart@1.0.8: + resolution: {integrity: sha512-UXSH262CSZY1tfu3G3Secr6uGLCFVPMhIqHjlgCUtCCcgihYc/xKs9djMTMUOb2j1mVSeU8EU6NWc/iQKU6Gfg==} + engines: {node: '>= 0.4'} + + string_decoder@1.1.1: + resolution: {integrity: sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==} + + string_decoder@1.3.0: + resolution: {integrity: sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==} + + stringify-object@3.3.0: + resolution: {integrity: sha512-rHqiFh1elqCQ9WPLIC8I0Q/g/wj5J1eMkyoiD6eoQApWHP0FtlK7rqnhmabL5VUY9JQCcqwwvlOaSuutekgyrw==} + engines: {node: '>=4'} + + strip-ansi@6.0.1: + resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==} + engines: {node: '>=8'} + + strip-ansi@7.1.2: + resolution: {integrity: sha512-gmBGslpoQJtgnMAvOVqGZpEz9dyoKTCzy2nfz/n8aIFhN/jCE/rCmcxabB6jOOHV+0WNnylOxaxBQPSvcWklhA==} + engines: {node: '>=12'} + + strip-bom@3.0.0: + resolution: {integrity: sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==} + engines: {node: '>=4'} + + strip-bom@4.0.0: + resolution: {integrity: sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w==} + engines: {node: '>=8'} + + strip-comments@2.0.1: + resolution: {integrity: sha512-ZprKx+bBLXv067WTCALv8SSz5l2+XhpYCsVtSqlMnkAXMWDq+/ekVbl1ghqP9rUHTzv6sm/DwCOiYutU/yp1fw==} + engines: {node: '>=10'} + + strip-eof@1.0.0: + resolution: {integrity: sha512-7FCwGGmx8mD5xQd3RPUvnSpUXHM3BWuzjtpD4TXsfcZ9EL4azvVVUscFYwD9nx8Kh+uCBC00XBtAykoMHwTh8Q==} + engines: {node: '>=0.10.0'} + + strip-final-newline@2.0.0: + resolution: {integrity: sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==} + engines: {node: '>=6'} + + strip-final-newline@3.0.0: + resolution: {integrity: sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw==} + engines: {node: '>=12'} + + strip-indent@3.0.0: + resolution: {integrity: sha512-laJTa3Jb+VQpaC6DseHhF7dXVqHTfJPCRDaEbid/drOhgitgYku/letMUqOXFoWV0zIIUbjpdH2t+tYj4bQMRQ==} + engines: {node: '>=8'} + + strip-json-comments@2.0.1: + resolution: {integrity: sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==} + engines: {node: '>=0.10.0'} + + strip-json-comments@3.1.1: + resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==} + engines: {node: '>=8'} + + strnum@2.2.3: + resolution: {integrity: sha512-oKx6RUCuHfT3oyVjtnrmn19H1SiCqgJSg+54XqURKp5aCMbrXrhLjRN9TjuwMjiYstZ0MzDrHqkGZ5dFTKd+zg==} + + style-loader@3.3.4: + resolution: {integrity: sha512-0WqXzrsMTyb8yjZJHDqwmnwRJvhALK9LfRtRc6B4UTWe8AijYLZYZ9thuJTZc2VfQWINADW/j+LiJnfy2RoC1w==} + engines: {node: '>= 12.13.0'} + peerDependencies: + webpack: ^5.0.0 + + style-mod@4.1.2: + resolution: {integrity: sha512-wnD1HyVqpJUI2+eKZ+eo1UwghftP6yuFheBqqe+bWCotBjC2K1YnteJILRMs3SM4V/0dLEW1SC27MWP5y+mwmw==} + + style-to-js@1.1.3: + resolution: {integrity: sha512-zKI5gN/zb7LS/Vm0eUwjmjrXWw8IMtyA8aPBJZdYiQTXj4+wQ3IucOLIOnF7zCHxvW8UhIGh/uZh/t9zEHXNTQ==} + + style-to-object@0.4.1: + resolution: {integrity: sha512-HFpbb5gr2ypci7Qw+IOhnP2zOU7e77b+rzM+wTzXzfi1PrtBCX0E7Pk4wL4iTLnhzZ+JgEGAhX81ebTg/aYjQw==} + + style-to-object@0.4.4: + resolution: {integrity: sha512-HYNoHZa2GorYNyqiCaBgsxvcJIn7OHq6inEga+E6Ke3m5JkoqpQbnFssk4jwe+K7AhGa2fcha4wSOf1Kn01dMg==} + + style-value-types@4.1.4: + resolution: {integrity: sha512-LCJL6tB+vPSUoxgUBt9juXIlNJHtBMy8jkXzUJSBzeHWdBu6lhzHqCvLVkXFGsFIlNa2ln1sQHya/gzaFmB2Lg==} + + styled-components@6.4.0: + resolution: {integrity: sha512-BL1EDFpt+q10eAeZB0q9ps6pSlPejaBQWBkiuM16pyoVTG4NhZrPrZK0cqNbrozxSsYwUsJ9SQYN6NyeKJYX9A==} + engines: {node: '>= 16'} + peerDependencies: + css-to-react-native: '>= 3.2.0' + react: '>= 16.8.0' + react-dom: '>= 16.8.0' + react-native: '>= 0.68.0' + peerDependenciesMeta: + css-to-react-native: + optional: true + react-dom: + optional: true + react-native: + optional: true + + stylehacks@5.1.1: + resolution: {integrity: sha512-sBpcd5Hx7G6seo7b1LkpttvTz7ikD0LlH5RmdcBNb6fFR0Fl7LQwHDFr300q4cwUqi+IYrFGmsIHieMBfnN/Bw==} + engines: {node: ^10 || ^12 || >=14.0} + peerDependencies: + postcss: ^8.2.15 + + stylis@4.2.0: + resolution: {integrity: sha512-Orov6g6BB1sDfYgzWfTHDOxamtX1bE/zo104Dh9e6fqJ3PooipYyfJ0pUmrZO2wAvO8YbEyeFrkV91XTsGMSrw==} + + stylis@4.3.6: + resolution: {integrity: sha512-yQ3rwFWRfwNUY7H5vpU0wfdkNSnvnJinhF9830Swlaxl03zsOjCfmX0ugac+3LtK0lYSgwL/KXc8oYL3mG4YFQ==} + + sucrase@3.35.0: + resolution: {integrity: sha512-8EbVDiu9iN/nESwxeSxDKe0dunta1GOlHufmSSXxMD2z2/tMZpDMpvXQGsc+ajGo8y2uYUmixaSRUc/QPoQ0GA==} + engines: {node: '>=16 || 14 >=14.17'} + hasBin: true + + supports-color@5.5.0: + resolution: {integrity: sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==} + engines: {node: '>=4'} + + supports-color@7.2.0: + resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==} + engines: {node: '>=8'} + + supports-color@8.1.1: + resolution: {integrity: sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==} + engines: {node: '>=10'} + + supports-hyperlinks@2.3.0: + resolution: {integrity: sha512-RpsAZlpWcDwOPQA22aCH4J0t7L8JmAvsCxfOSEwm7cQs3LshN36QaTkwd70DnBOXDWGssw2eUoc8CaRWT0XunA==} + engines: {node: '>=8'} + + supports-preserve-symlinks-flag@1.0.0: + resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==} + engines: {node: '>= 0.4'} + + svg-parser@2.0.4: + resolution: {integrity: sha512-e4hG1hRwoOdRb37cIMSgzNsxyzKfayW6VOflrwvR+/bzrkyxY/31WkbgnQpgtrNp1SdpJvpUAGTa/ZoiPNDuRQ==} + + svgo@1.3.2: + resolution: {integrity: sha512-yhy/sQYxR5BkC98CY7o31VGsg014AKLEPxdfhora76l36hD9Rdy5NZA/Ocn6yayNPgSamYdtX2rFJdcv07AYVw==} + engines: {node: '>=4.0.0'} + deprecated: This SVGO version is no longer supported. Upgrade to v2.x.x. + hasBin: true + + svgo@2.8.0: + resolution: {integrity: sha512-+N/Q9kV1+F+UeWYoSiULYo4xYSDQlTgb+ayMobAXPwMnLvop7oxKMo9OzIrX5x3eS4L4f2UHhc9axXwY8DpChg==} + engines: {node: '>=10.13.0'} + hasBin: true + + symbol-tree@3.2.4: + resolution: {integrity: sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==} + + tailwindcss@3.4.18: + resolution: {integrity: sha512-6A2rnmW5xZMdw11LYjhcI5846rt9pbLSabY5XPxo+XWdxwZaFEn47Go4NzFiHu9sNNmr/kXivP1vStfvMaK1GQ==} + engines: {node: '>=14.0.0'} + hasBin: true + + tapable@1.1.3: + resolution: {integrity: sha512-4WK/bYZmj8xLr+HUCODHGF1ZFzsYffasLUgEiMBY4fgtltdO6B4WJtlSbPaDTLpYTcGVwM2qLnFTICEcNxs3kA==} + engines: {node: '>=6'} + + tapable@2.3.0: + resolution: {integrity: sha512-g9ljZiwki/LfxmQADO3dEY1CbpmXT5Hm2fJ+QaGKwSXUylMybePR7/67YW7jOrrvjEgL1Fmz5kzyAjWVWLlucg==} + engines: {node: '>=6'} + + tar-fs@3.1.1: + resolution: {integrity: sha512-LZA0oaPOc2fVo82Txf3gw+AkEd38szODlptMYejQUhndHMLQ9M059uXR+AfS7DNo0NpINvSqDsvyaCrBVkptWg==} + + tar-stream@3.1.7: + resolution: {integrity: sha512-qJj60CXt7IU1Ffyc3NJMjh6EkuCFej46zUqJ4J7pqYlThyd9bO0XBTmcOIhSzZJVWfsLks0+nle/j538YAW9RQ==} + + tar@6.2.1: + resolution: {integrity: sha512-DZ4yORTwrbTj/7MZYq2w+/ZFdI6OZ/f9SFHR+71gIVUZhOQPHzVCLpvRnPgyaMpfWxxk/4ONva3GQSyNIKRv6A==} + engines: {node: '>=10'} + deprecated: Old versions of tar are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me + + temp-dir@2.0.0: + resolution: {integrity: sha512-aoBAniQmmwtcKp/7BzsH8Cxzv8OL736p7v1ihGb5e9DJ9kTwGWHrQrVB5+lfVDzfGrdRzXch+ig7LHaY1JTOrg==} + engines: {node: '>=8'} + + tempy@0.6.0: + resolution: {integrity: sha512-G13vtMYPT/J8A4X2SjdtBTphZlrp1gKv6hZiOjw14RCWg6GbHuQBGtjlx75xLbYV/wEc0D7G5K4rxKP/cXk8Bw==} + engines: {node: '>=10'} + + terminal-link@2.1.1: + resolution: {integrity: sha512-un0FmiRUQNr5PJqy9kP7c40F5BOfpGlYTrxonDChEZB7pzZxRNp/bt+ymiy9/npwXya9KH99nJ/GXFIiUkYGFQ==} + engines: {node: '>=8'} + + terser-webpack-plugin@5.3.14: + resolution: {integrity: sha512-vkZjpUjb6OMS7dhV+tILUW6BhpDR7P2L/aQSAv+Uwk+m8KATX9EccViHTJR2qDtACKPIYndLGCyl3FMo+r2LMw==} + engines: {node: '>= 10.13.0'} + peerDependencies: + '@swc/core': '*' + esbuild: '*' + uglify-js: '*' + webpack: ^5.1.0 + peerDependenciesMeta: + '@swc/core': + optional: true + esbuild: + optional: true + uglify-js: + optional: true + + terser@5.44.0: + resolution: {integrity: sha512-nIVck8DK+GM/0Frwd+nIhZ84pR/BX7rmXMfYwyg+Sri5oGVE99/E3KvXqpC2xHFxyqXyGHTKBSioxxplrO4I4w==} + engines: {node: '>=10'} + hasBin: true + + test-exclude@6.0.0: + resolution: {integrity: sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==} + engines: {node: '>=8'} + + text-decoder@1.2.3: + resolution: {integrity: sha512-3/o9z3X0X0fTupwsYvR03pJ/DjWuqqrfwBgTQzdWDiQSm9KitAyz/9WqsT2JQW7KV2m+bC2ol/zqpW37NHxLaA==} + + text-hex@1.0.0: + resolution: {integrity: sha512-uuVGNWzgJ4yhRaNSiubPY7OjISw4sw4E5Uv0wbjp+OzcbmVU/rsT8ujgcXJhn9ypzsgr5vlzpPqP+MBBKcGvbg==} + + text-table@0.2.0: + resolution: {integrity: sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==} + + thenify-all@1.6.0: + resolution: {integrity: sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA==} + engines: {node: '>=0.8'} + + thenify@3.3.1: + resolution: {integrity: sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==} + + thingies@2.6.0: + resolution: {integrity: sha512-rMHRjmlFLM1R96UYPvpmnc3LYtdFrT33JIB7L9hetGue1qAPfn1N2LJeEjxUSidu1Iku+haLZXDuEXUHNGO/lg==} + engines: {node: '>=10.18'} + peerDependencies: + tslib: ^2 + + throat@6.0.2: + resolution: {integrity: sha512-WKexMoJj3vEuK0yFEapj8y64V0A6xcuPuK9Gt1d0R+dzCSJc0lHqQytAbSB4cDAK0dWh4T0E2ETkoLE2WZ41OQ==} + + throttleit@1.0.1: + resolution: {integrity: sha512-vDZpf9Chs9mAdfY046mcPt8fg5QSZr37hEH4TXYBnDF+izxgrbRGUAAaBvIk/fJm9aOFCGFd1EsNg5AZCbnQCQ==} + + through@2.3.8: + resolution: {integrity: sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==} + + thunky@1.1.0: + resolution: {integrity: sha512-eHY7nBftgThBqOyHGVN+l8gF0BucP09fMo0oO/Lb0w1OF80dJv+lDVpXG60WMQvkcxAkNybKsrEIE3ZtKGmPrA==} + + tiny-invariant@1.3.3: + resolution: {integrity: sha512-+FbBPE1o9QAYvviau/qC5SE3caw21q3xkvWKBtja5vgqOWIHHJ3ioaq1VPfn/Szqctz2bU/oYeKd9/z5BL+PVg==} + + tiny-warning@1.0.3: + resolution: {integrity: sha512-lBN9zLN/oAf68o3zNXYrdCt1kP8WsiGW8Oo2ka41b2IM5JL/S1CTyX1rW0mb/zSuJun0ZUrDxx4sqvYS2FWzPA==} + + tinycolor2@1.6.0: + resolution: {integrity: sha512-XPaBkWQJdsf3pLKJV9p4qN/S+fm2Oj8AIPo1BTUhg5oxkvm9+SVEGFdhyOz7tTdUTfvxMiAs4sp6/eZO2Ew+pw==} + + titleize@1.0.1: + resolution: {integrity: sha512-rUwGDruKq1gX+FFHbTl5qjI7teVO7eOe+C8IcQ7QT+1BK3eEUXJqbZcBOeaRP4FwSC/C1A5jDoIVta0nIQ9yew==} + engines: {node: '>=0.10.0'} + + tldts-core@6.1.86: + resolution: {integrity: sha512-Je6p7pkk+KMzMv2XXKmAE3McmolOQFdxkKw0R8EYNr7sELW46JqnNeTX8ybPiQgvg1ymCoF8LXs5fzFaZvJPTA==} + + tldts@6.1.86: + resolution: {integrity: sha512-WMi/OQ2axVTf/ykqCQgXiIct+mSQDFdH2fkwhPwgEwvJ1kSzZRiinb0zF2Xb8u4+OqPChmyI6MEu4EezNJz+FQ==} + hasBin: true + + tmp@0.2.5: + resolution: {integrity: sha512-voyz6MApa1rQGUxT3E+BK7/ROe8itEx7vD8/HEvt4xwXucvQ5G5oeEiHkmHZJuBO21RpOf+YYm9MOivj709jow==} + engines: {node: '>=14.14'} + + tmpl@1.0.5: + resolution: {integrity: sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw==} + + to-buffer@1.2.2: + resolution: {integrity: sha512-db0E3UJjcFhpDhAF4tLo03oli3pwl3dbnzXOUIlRKrp+ldk/VUxzpWYZENsw2SZiuBjHAk7DfB0VU7NKdpb6sw==} + engines: {node: '>= 0.4'} + + to-regex-range@5.0.1: + resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==} + engines: {node: '>=8.0'} + + toidentifier@1.0.1: + resolution: {integrity: sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==} + engines: {node: '>=0.6'} + + toposort@2.0.2: + resolution: {integrity: sha512-0a5EOkAUp8D4moMi2W8ZF8jcga7BgZd91O/yabJCFY8az+XSzeGyTKs0Aoo897iV1Nj6guFq8orWDS96z91oGg==} + + touch@3.1.1: + resolution: {integrity: sha512-r0eojU4bI8MnHr8c5bNo7lJDdI2qXlWWJk6a9EAFG7vbhTjElYhBVS3/miuE0uOuoLdb8Mc/rVfsmm6eo5o9GA==} + hasBin: true + + tough-cookie@4.1.4: + resolution: {integrity: sha512-Loo5UUvLD9ScZ6jh8beX1T6sO1w2/MpCRpEP7V280GKMVUQ0Jzar2U3UJPsrdbziLEMMhu3Ujnq//rhiFuIeag==} + engines: {node: '>=6'} + + tough-cookie@5.1.2: + resolution: {integrity: sha512-FVDYdxtnj0G6Qm/DhNPSb8Ju59ULcup3tuJxkFb5K8Bv2pUXILbf0xZWU8PX8Ov19OXljbUyveOFwRMwkXzO+A==} + engines: {node: '>=16'} + + tr46@0.0.3: + resolution: {integrity: sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==} + + tr46@1.0.1: + resolution: {integrity: sha512-dTpowEjclQ7Kgx5SdBkqRzVhERQXov8/l9Ft9dVM9fmg0W0KQSVaXX9T4i6twCPNtYiZM53lpSSUAwJbFPOHxA==} + + tr46@2.1.0: + resolution: {integrity: sha512-15Ih7phfcdP5YxqiB+iDtLoaTz4Nd35+IiAv0kQ5FNKHzXgdWqPoTIqEDDJmXceQt4JZk6lVPT8lnDlPpGDppw==} + engines: {node: '>=8'} + + tr46@3.0.0: + resolution: {integrity: sha512-l7FvfAHlcmulp8kr+flpQZmVwtu7nfRV7NZujtN0OqES8EL4O4e0qqzL0DC5gAvx/ZC/9lk6rhcUwYvkBnBnYA==} + engines: {node: '>=12'} + + tr46@5.1.1: + resolution: {integrity: sha512-hdF5ZgjTqgAntKkklYw0R03MG2x/bSzTtkxmIRw/sTNV8YXsCJ1tfLAX23lhxhHJlEf3CRCOCGGWw3vI3GaSPw==} + engines: {node: '>=18'} + + tree-dump@1.1.0: + resolution: {integrity: sha512-rMuvhU4MCDbcbnleZTFezWsaZXRFemSqAM+7jPnzUl1fo9w3YEKOxAeui0fz3OI4EU4hf23iyA7uQRVko+UaBA==} + engines: {node: '>=10.0'} + peerDependencies: + tslib: '2' + + tree-kill@1.2.2: + resolution: {integrity: sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==} + hasBin: true + + trim-lines@3.0.1: + resolution: {integrity: sha512-kRj8B+YHZCc9kQYdWfJB2/oUl9rA99qbowYYBtr4ui4mZyAQ2JpvVBd/6U2YloATfqBhBTSMhTpgBHtU0Mf3Rg==} + + triple-beam@1.4.1: + resolution: {integrity: sha512-aZbgViZrg1QNcG+LULa7nhZpJTZSLm/mXnHXnbAbjmN5aSa0y7V+wvv6+4WaBtpISJzThKy+PIPxc1Nq1EJ9mg==} + engines: {node: '>= 14.0.0'} + + trough@2.2.0: + resolution: {integrity: sha512-tmMpK00BjZiUyVyvrBK7knerNgmgvcV/KLVyuma/SC+TQN167GrMRciANTz09+k3zW8L8t60jWO1GpfkZdjTaw==} + + tryer@1.0.1: + resolution: {integrity: sha512-c3zayb8/kWWpycWYg87P71E1S1ZL6b6IJxfb5fvsUgsf0S2MVGaDhDXXjDMpdCpfWXqptc+4mXwmiy1ypXqRAA==} + + ts-api-utils@1.4.3: + resolution: {integrity: sha512-i3eMG77UTMD0hZhgRS562pv83RC6ukSAC2GMNWc+9dieh/+jDM5u5YG+NHX6VNDRHQcHwmsTHctP9LhbC3WxVw==} + engines: {node: '>=16'} + peerDependencies: + typescript: '>=4.2.0' + + ts-interface-checker@0.1.13: + resolution: {integrity: sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==} + + ts-node@10.9.2: + resolution: {integrity: sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ==} + hasBin: true + peerDependencies: + '@swc/core': '>=1.2.50' + '@swc/wasm': '>=1.2.50' + '@types/node': '*' + typescript: '>=2.7' + peerDependenciesMeta: + '@swc/core': + optional: true + '@swc/wasm': + optional: true + + tsc-watch@6.3.1: + resolution: {integrity: sha512-x1hNJ/m1Cln2OvNJJLPsnWI1pObm+Jq9jF6kyz0flEy/ym7Y9TvU9edg6lXLuGVkhDClwBWi8aWAFE0qgzEkzw==} + engines: {node: '>=12.12.0'} + hasBin: true + peerDependencies: + typescript: '*' + + tsconfck@3.1.6: + resolution: {integrity: sha512-ks6Vjr/jEw0P1gmOVwutM3B7fWxoWBL2KRDb1JfqGVawBmO5UsvmWOQFGHBPl5yxYz4eERr19E6L7NMv+Fej4w==} + engines: {node: ^18 || >=20} + hasBin: true + peerDependencies: + typescript: ^5.0.0 + peerDependenciesMeta: + typescript: + optional: true + + tsconfig-paths@3.15.0: + resolution: {integrity: sha512-2Ac2RgzDe/cn48GvOe3M+o82pEFewD3UPbyoUHHdKasHwJKjds4fLXWf/Ux5kATBKN20oaFGu+jbElp1pos0mg==} + + tslib@1.14.1: + resolution: {integrity: sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==} + + tslib@2.8.1: + resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==} + + tsutils@3.21.0: + resolution: {integrity: sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==} + engines: {node: '>= 6'} + peerDependencies: + typescript: '>=2.8.0 || >= 3.2.0-dev || >= 3.3.0-dev || >= 3.4.0-dev || >= 3.5.0-dev || >= 3.6.0-dev || >= 3.6.0-beta || >= 3.7.0-dev || >= 3.7.0-beta' + + tunnel-agent@0.6.0: + resolution: {integrity: sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w==} + + turbo-darwin-64@2.5.8: + resolution: {integrity: sha512-Dh5bCACiHO8rUXZLpKw+m3FiHtAp2CkanSyJre+SInEvEr5kIxjGvCK/8MFX8SFRjQuhjtvpIvYYZJB4AGCxNQ==} + cpu: [x64] + os: [darwin] + + turbo-darwin-arm64@2.5.8: + resolution: {integrity: sha512-f1H/tQC9px7+hmXn6Kx/w8Jd/FneIUnvLlcI/7RGHunxfOkKJKvsoiNzySkoHQ8uq1pJnhJ0xNGTlYM48ZaJOQ==} + cpu: [arm64] + os: [darwin] + + turbo-linux-64@2.5.8: + resolution: {integrity: sha512-hMyvc7w7yadBlZBGl/bnR6O+dJTx3XkTeyTTH4zEjERO6ChEs0SrN8jTFj1lueNXKIHh1SnALmy6VctKMGnWfw==} + cpu: [x64] + os: [linux] + + turbo-linux-arm64@2.5.8: + resolution: {integrity: sha512-LQELGa7bAqV2f+3rTMRPnj5G/OHAe2U+0N9BwsZvfMvHSUbsQ3bBMWdSQaYNicok7wOZcHjz2TkESn1hYK6xIQ==} + cpu: [arm64] + os: [linux] + + turbo-windows-64@2.5.8: + resolution: {integrity: sha512-3YdcaW34TrN1AWwqgYL9gUqmZsMT4T7g8Y5Azz+uwwEJW+4sgcJkIi9pYFyU4ZBSjBvkfuPZkGgfStir5BBDJQ==} + cpu: [x64] + os: [win32] + + turbo-windows-arm64@2.5.8: + resolution: {integrity: sha512-eFC5XzLmgXJfnAK3UMTmVECCwuBcORrWdewoiXBnUm934DY6QN8YowC/srhNnROMpaKaqNeRpoB5FxCww3eteQ==} + cpu: [arm64] + os: [win32] + + turbo@2.5.8: + resolution: {integrity: sha512-5c9Fdsr9qfpT3hA0EyYSFRZj1dVVsb6KIWubA9JBYZ/9ZEAijgUEae0BBR/Xl/wekt4w65/lYLTFaP3JmwSO8w==} + hasBin: true + + tweetnacl@0.14.5: + resolution: {integrity: sha512-KXXFFdAbFXY4geFIwoyNK+f5Z1b7swfXABfL7HXCmoIWMKU3dmS26672A4EeQtDzLKy7SXmfBu51JolvEKwtGA==} + + type-check@0.3.2: + resolution: {integrity: sha512-ZCmOJdvOWDBYJlzAoFkC+Q0+bUyEOS1ltgp1MGU03fqHG+dbi9tBFU2Rd9QKiDZFAYrhPh2JUf7rZRIuHRKtOg==} + engines: {node: '>= 0.8.0'} + + type-check@0.4.0: + resolution: {integrity: sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==} + engines: {node: '>= 0.8.0'} + + type-detect@4.0.8: + resolution: {integrity: sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==} + engines: {node: '>=4'} + + type-fest@0.16.0: + resolution: {integrity: sha512-eaBzG6MxNzEn9kiwvtre90cXaNLkmadMWa1zQMs3XORCXNbsH/OewwbxC5ia9dCxIxnTAsSxXJaa/p5y8DlvJg==} + engines: {node: '>=10'} + + type-fest@0.20.2: + resolution: {integrity: sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==} + engines: {node: '>=10'} + + type-fest@0.21.3: + resolution: {integrity: sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==} + engines: {node: '>=10'} + + type-fest@1.4.0: + resolution: {integrity: sha512-yGSza74xk0UG8k+pLh5oeoYirvIiWo5t0/o3zHHAO2tRDiZcxWP7fywNlXhqb6/r6sWvwi+RsyQMWhVLe4BVuA==} + engines: {node: '>=10'} + + type-fest@2.19.0: + resolution: {integrity: sha512-RAH822pAdBgcNMAfWnCBU3CFZcfZ/i1eZjwFU/dsLKumyuuP3niueg2UAukXYF0E2AAoc82ZSSf9J0WQBinzHA==} + engines: {node: '>=12.20'} + + type-fest@4.41.0: + resolution: {integrity: sha512-TeTSQ6H5YHvpqVwBRcnLDCBnDOHWYu7IvGbHT6N8AOymcr9PJGjc1GTtiWZTYg0NCgYwvnYWEkVChQAr9bjfwA==} + engines: {node: '>=16'} + + type-is@1.6.18: + resolution: {integrity: sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==} + engines: {node: '>= 0.6'} + + typed-array-buffer@1.0.3: + resolution: {integrity: sha512-nAYYwfY3qnzX30IkA6AQZjVbtK6duGontcQm1WSG1MD94YLqK0515GNApXkoxKOWMusVssAHWLh9SeaoefYFGw==} + engines: {node: '>= 0.4'} + + typed-array-byte-length@1.0.3: + resolution: {integrity: sha512-BaXgOuIxz8n8pIq3e7Atg/7s+DpiYrxn4vdot3w9KbnBhcRQq6o3xemQdIfynqSeXeDrF32x+WvfzmOjPiY9lg==} + engines: {node: '>= 0.4'} + + typed-array-byte-offset@1.0.4: + resolution: {integrity: sha512-bTlAFB/FBYMcuX81gbL4OcpH5PmlFHqlCCpAl8AlEzMz5k53oNDvN8p1PNOWLEmI2x4orp3raOFB51tv9X+MFQ==} + engines: {node: '>= 0.4'} + + typed-array-length@1.0.7: + resolution: {integrity: sha512-3KS2b+kL7fsuk/eJZ7EQdnEmQoaho/r6KUef7hxvltNA5DR8NAUM+8wJMbJyZ4G9/7i3v5zPBIMN5aybAh2/Jg==} + engines: {node: '>= 0.4'} + + typedarray-to-buffer@3.1.5: + resolution: {integrity: sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q==} + + typedarray@0.0.6: + resolution: {integrity: sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA==} + + typeorm@0.3.27: + resolution: {integrity: sha512-pNV1bn+1n8qEe8tUNsNdD8ejuPcMAg47u2lUGnbsajiNUr3p2Js1XLKQjBMH0yMRMDfdX8T+fIRejFmIwy9x4A==} + engines: {node: '>=16.13.0'} + hasBin: true + peerDependencies: + '@google-cloud/spanner': ^5.18.0 || ^6.0.0 || ^7.0.0 + '@sap/hana-client': ^2.14.22 + better-sqlite3: ^8.0.0 || ^9.0.0 || ^10.0.0 || ^11.0.0 || ^12.0.0 + ioredis: ^5.0.4 + mongodb: ^5.8.0 || ^6.0.0 + mssql: ^9.1.1 || ^10.0.1 || ^11.0.1 + mysql2: ^2.2.5 || ^3.0.1 + oracledb: ^6.3.0 + pg: ^8.5.1 + pg-native: ^3.0.0 + pg-query-stream: ^4.0.0 + redis: ^3.1.1 || ^4.0.0 || ^5.0.14 + reflect-metadata: ^0.1.14 || ^0.2.0 + sql.js: ^1.4.0 + sqlite3: ^5.0.3 + ts-node: ^10.7.0 + typeorm-aurora-data-api-driver: ^2.0.0 || ^3.0.0 + peerDependenciesMeta: + '@google-cloud/spanner': + optional: true + '@sap/hana-client': + optional: true + better-sqlite3: + optional: true + ioredis: + optional: true + mongodb: + optional: true + mssql: + optional: true + mysql2: + optional: true + oracledb: + optional: true + pg: + optional: true + pg-native: + optional: true + pg-query-stream: + optional: true + redis: + optional: true + sql.js: + optional: true + sqlite3: + optional: true + ts-node: + optional: true + typeorm-aurora-data-api-driver: + optional: true + + typescript@5.9.3: + resolution: {integrity: sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==} + engines: {node: '>=14.17'} + hasBin: true + + ua-parser-js@0.7.41: + resolution: {integrity: sha512-O3oYyCMPYgNNHuO7Jjk3uacJWZF8loBgwrfd/5LE/HyZ3lUIOdniQ7DNXJcIgZbwioZxk0fLfI4EVnetdiX5jg==} + hasBin: true + + ua-parser-js@1.0.41: + resolution: {integrity: sha512-LbBDqdIC5s8iROCUjMbW1f5dJQTEFB1+KO9ogbvlb3nm9n4YHa5p4KTvFPWvh2Hs8gZMBuiB1/8+pdfe/tDPug==} + hasBin: true + + unbox-primitive@1.1.0: + resolution: {integrity: sha512-nWJ91DjeOkej/TA8pXQ3myruKpKEYgqvpw9lz4OPHj/NWFNluYrjbz9j01CJ8yKQd2g4jFoOkINCTW2I5LEEyw==} + engines: {node: '>= 0.4'} + + undefsafe@2.0.5: + resolution: {integrity: sha512-WxONCrssBM8TSPRqN5EmsjVrsv4A8X12J4ArBiiayv3DyyG3ZlIg6yysuuSYdZsVz3TKcTg2fd//Ujd4CHV1iA==} + + underscore@1.12.1: + resolution: {integrity: sha512-hEQt0+ZLDVUMhebKxL4x1BTtDY7bavVofhZ9KZ4aI26X9SRaE+Y3m83XUL1UP2jn8ynjndwCCpEHdUG+9pP1Tw==} + + undici-types@7.14.0: + resolution: {integrity: sha512-QQiYxHuyZ9gQUIrmPo3IA+hUl4KYk8uSA7cHrcKd/l3p1OTpZcM0Tbp9x7FAtXdAYhlasd60ncPpgu6ihG6TOA==} + + unicode-canonical-property-names-ecmascript@2.0.1: + resolution: {integrity: sha512-dA8WbNeb2a6oQzAQ55YlT5vQAWGV9WXOsi3SskE3bcCdM0P4SDd+24zS/OCacdRq5BkdsRj9q3Pg6YyQoxIGqg==} + engines: {node: '>=4'} + + unicode-match-property-ecmascript@2.0.0: + resolution: {integrity: sha512-5kaZCrbp5mmbz5ulBkDkbY0SsPOjKqVS35VpL9ulMPfSl0J0Xsm+9Evphv9CoIZFwre7aJoa94AY6seMKGVN5Q==} + engines: {node: '>=4'} + + unicode-match-property-value-ecmascript@2.2.1: + resolution: {integrity: sha512-JQ84qTuMg4nVkx8ga4A16a1epI9H6uTXAknqxkGF/aFfRLw1xC/Bp24HNLaZhHSkWd3+84t8iXnp1J0kYcZHhg==} + engines: {node: '>=4'} + + unicode-property-aliases-ecmascript@2.2.0: + resolution: {integrity: sha512-hpbDzxUY9BFwX+UeBnxv3Sh1q7HFxj48DTmXchNgRa46lO8uj3/1iEn3MiNUYTg1g9ctIqXCCERn8gYZhHC5lQ==} + engines: {node: '>=4'} + + unified@10.1.2: + resolution: {integrity: sha512-pUSWAi/RAnVy1Pif2kAoeWNBa3JVrx0MId2LASj8G+7AiHWoKZNTomq6LG326T68U7/e263X6fTdcXIy7XnF7Q==} + + unique-filename@1.1.1: + resolution: {integrity: sha512-Vmp0jIp2ln35UTXuryvjzkjGdRyf9b2lTXuSYUiPmzRcl3FDtYqAwOnTJkAngD9SWhnoJzDbTKwaOrZ+STtxNQ==} + + unique-slug@2.0.2: + resolution: {integrity: sha512-zoWr9ObaxALD3DOPfjPSqxt4fnZiWblxHIgeWqW8x7UqDzEtHEQLzji2cuJYQFCU6KmoJikOYAZlrTHHebjx2w==} + + unique-string@2.0.0: + resolution: {integrity: sha512-uNaeirEPvpZWSgzwsPGtU2zVSTrn/8L5q/IexZmH0eH6SA73CmAA5U4GwORTxQAZs95TAXLNqeLoPPNO5gZfWg==} + engines: {node: '>=8'} + + unist-util-find-after@4.0.1: + resolution: {integrity: sha512-QO/PuPMm2ERxC6vFXEPtmAutOopy5PknD+Oq64gGwxKtk4xwo9Z97t9Av1obPmGU0IyTa6EKYUfTrK2QJS3Ozw==} + + unist-util-generated@2.0.1: + resolution: {integrity: sha512-qF72kLmPxAw0oN2fwpWIqbXAVyEqUzDHMsbtPvOudIlUzXYFIeQIuxXQCRCFh22B7cixvU0MG7m3MW8FTq/S+A==} + + unist-util-is@5.2.1: + resolution: {integrity: sha512-u9njyyfEh43npf1M+yGKDGVPbY/JWEemg5nH05ncKPfi+kBbKBJoTdsogMu33uhytuLlv9y0O7GH7fEdwLdLQw==} + + unist-util-position@4.0.4: + resolution: {integrity: sha512-kUBE91efOWfIVBo8xzh/uZQ7p9ffYRtUbMRZBNFYwf0RK8koUMx6dGUfwylLOKmaT2cs4wSW96QoYUSXAyEtpg==} + + unist-util-stringify-position@2.0.3: + resolution: {integrity: sha512-3faScn5I+hy9VleOq/qNbAd6pAx7iH5jYBMS9I1HgQVijz/4mv5Bvw5iw1sC/90CODiKo81G/ps8AJrISn687g==} + + unist-util-stringify-position@3.0.3: + resolution: {integrity: sha512-k5GzIBZ/QatR8N5X2y+drfpWG8IDBzdnVj6OInRNWm1oXrzydiaAT2OQiA8DPRRZyAKb9b6I2a6PxYklZD0gKg==} + + unist-util-visit-parents@5.1.3: + resolution: {integrity: sha512-x6+y8g7wWMyQhL1iZfhIPhDAs7Xwbn9nRosDXl7qoPTSCy0yNxnKc+hWokFifWQIDGi154rdUqKvbCa4+1kLhg==} + + unist-util-visit@4.1.2: + resolution: {integrity: sha512-MSd8OUGISqHdVvfY9TPhyK2VdUrPgxkUtWSuMHF6XAAFuL4LokseigBnZtPnJMu+FbynTkFNnFlyjxpVKujMRg==} + + universalify@0.2.0: + resolution: {integrity: sha512-CJ1QgKmNg3CwvAv/kOFmtnEN05f0D/cn9QntgNOQlQF9dgvVTHj3t+8JPdjqawCHk7V/KA+fbUqzZ9XWhcqPUg==} + engines: {node: '>= 4.0.0'} + + universalify@2.0.1: + resolution: {integrity: sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==} + engines: {node: '>= 10.0.0'} + + unpipe@1.0.0: + resolution: {integrity: sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==} + engines: {node: '>= 0.8'} + + unquote@1.1.1: + resolution: {integrity: sha512-vRCqFv6UhXpWxZPyGDh/F3ZpNv8/qo7w6iufLpQg9aKnQ71qM4B5KiI7Mia9COcjEhrO9LueHpMYjYzsWH3OIg==} + + untildify@4.0.0: + resolution: {integrity: sha512-KK8xQ1mkzZeg9inewmFVDNkg3l5LUhoq9kN6iWYB/CC9YMG8HA+c1Q8HwDe6dEX7kErrEVNVBO3fWsVq5iDgtw==} + engines: {node: '>=8'} + + upath@1.2.0: + resolution: {integrity: sha512-aZwGpamFO61g3OlfT7OQCHqhGnW43ieH9WZeP7QxN/G/jS4jfqUkZxoryvJgVPEcrl5NL/ggHsSmLMHuH64Lhg==} + engines: {node: '>=4'} + + update-browserslist-db@1.1.3: + resolution: {integrity: sha512-UxhIZQ+QInVdunkDAaiazvvT/+fXL5Osr0JZlJulepYu6Jd7qJtDZjlur0emRlT71EN3ScPoE7gvsuIKKNavKw==} + hasBin: true + peerDependencies: + browserslist: '>= 4.21.0' + + uri-js@4.4.1: + resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==} + + url-parse@1.5.10: + resolution: {integrity: sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ==} + + use-composed-ref@1.4.0: + resolution: {integrity: sha512-djviaxuOOh7wkj0paeO1Q/4wMZ8Zrnag5H6yBvzN7AKKe8beOaED9SF5/ByLqsku8NP4zQqsvM2u3ew/tJK8/w==} + peerDependencies: + '@types/react': '*' + react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 + peerDependenciesMeta: + '@types/react': + optional: true + + use-isomorphic-layout-effect@1.2.1: + resolution: {integrity: sha512-tpZZ+EX0gaghDAiFR37hj5MgY6ZN55kLiPkJsKxBMZ6GZdOSPJXiOzPM984oPYZ5AnehYx5WQp1+ME8I/P/pRA==} + peerDependencies: + '@types/react': '*' + react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 + peerDependenciesMeta: + '@types/react': + optional: true + + use-latest@1.3.0: + resolution: {integrity: sha512-mhg3xdm9NaM8q+gLT8KryJPnRFOz1/5XPBhmDEVZK1webPzDjrPk7f/mbpeLqTgB9msytYWANxgALOCJKnLvcQ==} + peerDependencies: + '@types/react': '*' + react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 + peerDependenciesMeta: + '@types/react': + optional: true + + use-sync-external-store@1.6.0: + resolution: {integrity: sha512-Pp6GSwGP/NrPIrxVFAIkOQeyw8lFenOHijQWkUTrDvrF4ALqylP2C/KCkeS9dpUM3KvYRQhna5vt7IL95+ZQ9w==} + peerDependencies: + react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 + + utf-8-validate@6.0.5: + resolution: {integrity: sha512-EYZR+OpIXp9Y1eG1iueg8KRsY8TuT8VNgnanZ0uA3STqhHQTLwbl+WX76/9X5OY12yQubymBpaBSmMPkSTQcKA==} + engines: {node: '>=6.14.2'} + + util-deprecate@1.0.2: + resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==} + + util.promisify@1.0.1: + resolution: {integrity: sha512-g9JpC/3He3bm38zsLupWryXHoEcS22YHthuPQSJdMy6KNrzIRzWqcsHzD/WUnqe45whVou4VIsPew37DoXWNrA==} + + utila@0.4.0: + resolution: {integrity: sha512-Z0DbgELS9/L/75wZbro8xAnT50pBVFQZ+hUEueGDU5FN51YSCYM+jdxsfCiHjwNP/4LCDD0i/graKpeBnOXKRA==} + + utils-merge@1.0.1: + resolution: {integrity: sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==} + engines: {node: '>= 0.4.0'} + + uuid@11.1.0: + resolution: {integrity: sha512-0/A9rDy9P7cJ+8w1c9WD9V//9Wj15Ce2MPz8Ri6032usz+NfePxx5AcN3bN+r6ZL6jEo066/yNYB3tn4pQEx+A==} + hasBin: true + + uuid@8.3.2: + resolution: {integrity: sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==} + hasBin: true + + uuid@9.0.1: + resolution: {integrity: sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==} + hasBin: true + + uvu@0.5.6: + resolution: {integrity: sha512-+g8ENReyr8YsOc6fv/NVJs2vFdHBnBNdfE49rshrTzDWOlUx4Gq7KOS2GD8eqhy2j+Ejq29+SbKH8yjkAqXqoA==} + engines: {node: '>=8'} + hasBin: true + + v8-compile-cache-lib@3.0.1: + resolution: {integrity: sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==} + + v8-to-istanbul@8.1.1: + resolution: {integrity: sha512-FGtKtv3xIpR6BYhvgH8MI/y78oT7d8Au3ww4QIxymrCtZEh5b8gCw2siywE+puhEmuWKDtmfrvF5UlB298ut3w==} + engines: {node: '>=10.12.0'} + + vary@1.1.2: + resolution: {integrity: sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==} + engines: {node: '>= 0.8'} + + verror@1.10.0: + resolution: {integrity: sha512-ZZKSmDAEFOijERBLkmYfJ+vmk3w+7hOLYDNkRCuRuMJGEmqYNCNLyBBFwWKVMhfwaEF3WOd0Zlw86U/WC/+nYw==} + engines: {'0': node >=0.6.0} + + vfile-message@3.1.4: + resolution: {integrity: sha512-fa0Z6P8HUrQN4BZaX05SIVXic+7kE3b05PWAtPuYP9QLHsLKYR7/AlLW3NtOrpXRLeawpDLMsVkmk5DG0NXgWw==} + + vfile@5.3.7: + resolution: {integrity: sha512-r7qlzkgErKjobAmyNIkkSpizsFPYiUPuJb5pNW1RB4JcYVZhs4lIbVqk8XPk033CV/1z8ss5pkax8SuhGpcG8g==} + + victory-vendor@36.9.2: + resolution: {integrity: sha512-PnpQQMuxlwYdocC8fIJqVXvkeViHYzotI+NJrCuav0ZYFoq912ZHBk3mCeuj+5/VpodOjPe1z0Fk2ihgzlXqjQ==} + + vite-plugin-pwa@0.17.5: + resolution: {integrity: sha512-UxRNPiJBzh4tqU/vc8G2TxmrUTzT6BqvSzhszLk62uKsf+npXdvLxGDz9C675f4BJi6MbD2tPnJhi5txlMzxbQ==} + engines: {node: '>=16.0.0'} + peerDependencies: + vite: ^3.1.0 || ^4.0.0 || ^5.0.0 + workbox-build: ^7.0.0 + workbox-window: ^7.0.0 + + vite-plugin-react-js-support@1.0.7: + resolution: {integrity: sha512-yo9513wqA1Ba9MbgqdBXJr4RB3A4mFAhUe867A2te2TEcN30nb0iDpmQvFH+NiiMZFp85fJGD6+ZOKZbOLZD4A==} + + vite-tsconfig-paths@4.3.2: + resolution: {integrity: sha512-0Vd/a6po6Q+86rPlntHye7F31zA2URZMbH8M3saAZ/xR9QoGN/L21bxEGfXdWmFdNkqPpRdxFT7nmNe12e9/uA==} + peerDependencies: + vite: '*' + peerDependenciesMeta: + vite: + optional: true + + vite@4.5.14: + resolution: {integrity: sha512-+v57oAaoYNnO3hIu5Z/tJRZjq5aHM2zDve9YZ8HngVHbhk66RStobhb1sqPMIPEleV6cNKYK4eGrAbE9Ulbl2g==} + engines: {node: ^14.18.0 || >=16.0.0} + hasBin: true + peerDependencies: + '@types/node': '>= 14' + less: '*' + lightningcss: ^1.21.0 + sass: '*' + stylus: '*' + sugarss: '*' + terser: ^5.4.0 + peerDependenciesMeta: + '@types/node': + optional: true + less: + optional: true + lightningcss: + optional: true + sass: + optional: true + stylus: + optional: true + sugarss: + optional: true + terser: + optional: true + + vite@5.4.20: + resolution: {integrity: sha512-j3lYzGC3P+B5Yfy/pfKNgVEg4+UtcIJcVRt2cDjIOmhLourAqPqf8P7acgxeiSgUB7E3p2P8/3gNIgDLpwzs4g==} + engines: {node: ^18.0.0 || >=20.0.0} + hasBin: true + peerDependencies: + '@types/node': ^18.0.0 || >=20.0.0 + less: '*' + lightningcss: ^1.21.0 + sass: '*' + sass-embedded: '*' + stylus: '*' + sugarss: '*' + terser: ^5.4.0 + peerDependenciesMeta: + '@types/node': + optional: true + less: + optional: true + lightningcss: + optional: true + sass: + optional: true + sass-embedded: + optional: true + stylus: + optional: true + sugarss: + optional: true + terser: + optional: true + + w3c-hr-time@1.0.2: + resolution: {integrity: sha512-z8P5DvDNjKDoFIHK7q8r8lackT6l+jo/Ye3HOle7l9nICP9lf1Ci25fy9vHd0JOWewkIFzXIEig3TdKT7JQ5fQ==} + deprecated: Use your platform's native performance.now() and performance.timeOrigin. + + w3c-keyname@2.2.8: + resolution: {integrity: sha512-dpojBhNsCNN7T82Tm7k26A6G9ML3NkhDsnw9n/eoxSRlVBB4CEtIQ/KTCLI2Fwf3ataSXRhYFkQi3SlnFwPvPQ==} + + w3c-xmlserializer@2.0.0: + resolution: {integrity: sha512-4tzD0mF8iSiMiNs30BiLO3EpfGLZUT2MSX/G+o7ZywDzliWQ3OPtTZ0PTC3B3ca1UAf4cJMHB+2Bf56EriJuRA==} + engines: {node: '>=10'} + + w3c-xmlserializer@4.0.0: + resolution: {integrity: sha512-d+BFHzbiCx6zGfz0HyQ6Rg69w9k19nviJspaj4yNscGjrHu94sVP+aRm75yEbCh+r2/yR+7q6hux9LVtbuTGBw==} + engines: {node: '>=14'} + + wait-on@8.0.5: + resolution: {integrity: sha512-J3WlS0txVHkhLRb2FsmRg3dkMTCV1+M6Xra3Ho7HzZDHpE7DCOnoSoCJsZotrmW3uRMhvIJGSKUKrh/MeF4iag==} + engines: {node: '>=12.0.0'} + hasBin: true + + walker@1.0.8: + resolution: {integrity: sha512-ts/8E8l5b7kY0vlWLewOkDXMmPdLcVV4GmOQLyxuSswIJsweeFZtAsMF7k1Nszz+TYBQrlYRmzOnr398y1JemQ==} + + warning@4.0.3: + resolution: {integrity: sha512-rpJyN222KWIvHJ/F53XSZv0Zl/accqHR8et1kpaMTD/fLCRxtV8iX8czMzY7sVZupTI3zcUTg8eycS2kNF9l6w==} + + watchpack@2.4.4: + resolution: {integrity: sha512-c5EGNOiyxxV5qmTtAB7rbiXxi1ooX1pQKMLX/MIabJjRA0SJBQOjKF+KSVfHkr9U1cADPon0mRiVe/riyaiDUA==} + engines: {node: '>=10.13.0'} + + wbuf@1.7.3: + resolution: {integrity: sha512-O84QOnr0icsbFGLS0O3bI5FswxzRr8/gHwWkDlQFskhSPryQXvrTMxjxGP4+iWYoauLoBvfDpkrOauZ+0iZpDA==} + + web-namespaces@2.0.1: + resolution: {integrity: sha512-bKr1DkiNa2krS7qxNtdrtHAmzuYGFQLiQ13TsorsdT6ULTkPLKuu5+GsFpDlg6JFjUTwX2DyhMPG2be8uPrqsQ==} + + web-streams-polyfill@3.3.3: + resolution: {integrity: sha512-d2JWLCivmZYTSIoge9MsgFCZrt571BikcWGYkjC1khllbTeDlGqZ2D8vD8E/lJa8WGWbb7Plm8/XJYV7IJHZZw==} + engines: {node: '>= 8'} + + webidl-conversions@3.0.1: + resolution: {integrity: sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==} + + webidl-conversions@4.0.2: + resolution: {integrity: sha512-YQ+BmxuTgd6UXZW3+ICGfyqRyHXVlD5GtQr5+qjiNW7bF0cqrzX500HVXPBOvgXb5YnzDd+h0zqyv61KUD7+Sg==} + + webidl-conversions@5.0.0: + resolution: {integrity: sha512-VlZwKPCkYKxQgeSbH5EyngOmRp7Ww7I9rQLERETtf5ofd9pGeswWiOtogpEO850jziPRarreGxn5QIiTqpb2wA==} + engines: {node: '>=8'} + + webidl-conversions@6.1.0: + resolution: {integrity: sha512-qBIvFLGiBpLjfwmYAaHPXsn+ho5xZnGvyGvsarywGNc8VyQJUMHJ8OBKGGrPER0okBeMDaan4mNBlgBROxuI8w==} + engines: {node: '>=10.4'} + + webidl-conversions@7.0.0: + resolution: {integrity: sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==} + engines: {node: '>=12'} + + webpack-dev-middleware@7.4.2: + resolution: {integrity: sha512-xOO8n6eggxnwYpy1NlzUKpvrjfJTvae5/D6WOK0S2LSo7vjmo5gCM1DbLUmFqrMTJP+W/0YZNctm7jasWvLuBA==} + engines: {node: '>= 18.12.0'} + peerDependencies: + webpack: ^5.0.0 + peerDependenciesMeta: + webpack: + optional: true + + webpack-dev-server@4.15.2: + resolution: {integrity: sha512-0XavAZbNJ5sDrCbkpWL8mia0o5WPOd2YGtxrEiZkBK9FjLppIUK2TgxK6qGD2P3hUXTJNNPVibrerKcx5WkR1g==} + engines: {node: '>= 12.13.0'} + hasBin: true + peerDependencies: + webpack: ^4.37.0 || ^5.0.0 + webpack-cli: '*' + peerDependenciesMeta: + webpack: + optional: true + webpack-cli: + optional: true + + webpack-manifest-plugin@4.1.1: + resolution: {integrity: sha512-YXUAwxtfKIJIKkhg03MKuiFAD72PlrqCiwdwO4VEXdRO5V0ORCNwaOwAZawPZalCbmH9kBDmXnNeQOw+BIEiow==} + engines: {node: '>=12.22.0'} + peerDependencies: + webpack: ^4.44.2 || ^5.47.0 + + webpack-sources@1.4.3: + resolution: {integrity: sha512-lgTS3Xhv1lCOKo7SA5TjKXMjpSM4sBjNV5+q2bqesbSPs5FjGmU6jjtBSkX9b4qW87vDIsCIlUPOEhbZrMdjeQ==} + + webpack-sources@2.3.1: + resolution: {integrity: sha512-y9EI9AO42JjEcrTJFOYmVywVZdKVUfOvDUPsJea5GIr1JOEGFVqwlY2K098fFoIjOkDzHn2AjRvM8dsBZu+gCA==} + engines: {node: '>=10.13.0'} + + webpack-sources@3.3.3: + resolution: {integrity: sha512-yd1RBzSGanHkitROoPFd6qsrxt+oFhg/129YzheDGqeustzX0vTZJZsSsQjVQC4yzBQ56K55XU8gaNCtIzOnTg==} + engines: {node: '>=10.13.0'} + + webpack@5.102.1: + resolution: {integrity: sha512-7h/weGm9d/ywQ6qzJ+Xy+r9n/3qgp/thalBbpOi5i223dPXKi04IBtqPN9nTd+jBc7QKfvDbaBnFipYp4sJAUQ==} + engines: {node: '>=10.13.0'} + hasBin: true + peerDependencies: + webpack-cli: '*' + peerDependenciesMeta: + webpack-cli: + optional: true + + websocket-driver@0.7.4: + resolution: {integrity: sha512-b17KeDIQVjvb0ssuSDF2cYXSg2iztliJ4B9WdsuB6J952qCPKmnVq4DyW5motImXHDC1cBT/1UezrJVsKw5zjg==} + engines: {node: '>=0.8.0'} + + websocket-extensions@0.1.4: + resolution: {integrity: sha512-OqedPIGOfsDlo31UNwYbCFMSaO9m9G/0faIHj5/dZFDMFqPTcx6UwqyOy3COEaEOg/9VsGIpdqn62W5KhoKSpg==} + engines: {node: '>=0.8.0'} + + whatwg-encoding@1.0.5: + resolution: {integrity: sha512-b5lim54JOPN9HtzvK9HFXvBma/rnfFeqsic0hSpjtDbVxR3dJKLc+KB4V6GgiGOvl7CY/KNh8rxSo9DKQrnUEw==} + deprecated: Use @exodus/bytes instead for a more spec-conformant and faster implementation + + whatwg-encoding@2.0.0: + resolution: {integrity: sha512-p41ogyeMUrw3jWclHWTQg1k05DSVXPLcVxRTYsXUk+ZooOCZLcoYgPZ/HL/D/N+uQPOtcp1me1WhBEaX02mhWg==} + engines: {node: '>=12'} + deprecated: Use @exodus/bytes instead for a more spec-conformant and faster implementation + + whatwg-fetch@3.6.20: + resolution: {integrity: sha512-EqhiFU6daOA8kpjOWTL0olhVOF3i7OrFzSYiGsEMB8GcXS+RrzauAERX65xMeNWVqxA6HXH2m69Z9LaKKdisfg==} + + whatwg-mimetype@2.3.0: + resolution: {integrity: sha512-M4yMwr6mAnQz76TbJm914+gPpB/nCwvZbJU28cUD6dR004SAxDLOOSUaB1JDRqLtaOV/vi0IC5lEAGFgrjGv/g==} + + whatwg-mimetype@3.0.0: + resolution: {integrity: sha512-nt+N2dzIutVRxARx1nghPKGv1xHikU7HKdfafKkLNLindmPU/ch3U31NOCGGA/dmPcmb1VlofO0vnKAcsm0o/Q==} + engines: {node: '>=12'} + + whatwg-url@11.0.0: + resolution: {integrity: sha512-RKT8HExMpoYx4igMiVMY83lN6UeITKJlBQ+vR/8ZJ8OCdSiN3RwCq+9gH0+Xzj0+5IrM6i4j/6LuvzbZIQgEcQ==} + engines: {node: '>=12'} + + whatwg-url@14.2.0: + resolution: {integrity: sha512-De72GdQZzNTUBBChsXueQUnPKDkg/5A5zp7pFDuQAj5UFoENpiACU0wlCvzpAGnTkj++ihpKwKyYewn/XNUbKw==} + engines: {node: '>=18'} + + whatwg-url@5.0.0: + resolution: {integrity: sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==} + + whatwg-url@7.1.0: + resolution: {integrity: sha512-WUu7Rg1DroM7oQvGWfOiAK21n74Gg+T4elXEQYkOhtyLeWiJFoOGLXPKI/9gzIie9CtwVLm8wtw6YJdKyxSjeg==} + + whatwg-url@8.7.0: + resolution: {integrity: sha512-gAojqb/m9Q8a5IV96E3fHJM70AzCkgt4uXYX2O7EmuyOnLrViCQlsEBmF9UQIu3/aeAIp2U17rtbpZWNntQqdg==} + engines: {node: '>=10'} + + which-boxed-primitive@1.1.1: + resolution: {integrity: sha512-TbX3mj8n0odCBFVlY8AxkqcHASw3L60jIuF8jFP78az3C2YhmGvqbHBpAjTRH2/xqYunrJ9g1jSyjCjpoWzIAA==} + engines: {node: '>= 0.4'} + + which-builtin-type@1.2.1: + resolution: {integrity: sha512-6iBczoX+kDQ7a3+YJBnh3T+KZRxM/iYNPXicqk66/Qfm1b93iu+yOImkg0zHbj5LNOcNv1TEADiZ0xa34B4q6Q==} + engines: {node: '>= 0.4'} + + which-collection@1.0.2: + resolution: {integrity: sha512-K4jVyjnBdgvc86Y6BkaLZEN933SwYOuBFkdmBu9ZfkcAbdVbpITnDmjvZ/aQjRXQrv5EPkTnD1s39GiiqbngCw==} + engines: {node: '>= 0.4'} + + which-typed-array@1.1.19: + resolution: {integrity: sha512-rEvr90Bck4WZt9HHFC4DJMsjvu7x+r6bImz0/BrbWb7A2djJ8hnZMrWnHo9F8ssv0OMErasDhftrfROTyqSDrw==} + engines: {node: '>= 0.4'} + + which@1.3.1: + resolution: {integrity: sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==} + hasBin: true + + which@2.0.2: + resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==} + engines: {node: '>= 8'} + hasBin: true + + wicked-good-xpath@1.3.0: + resolution: {integrity: sha512-Gd9+TUn5nXdwj/hFsPVx5cuHHiF5Bwuc30jZ4+ronF1qHK5O7HD0sgmXWSEgwKquT3ClLoKPVbO6qGwVwLzvAw==} + + wide-align@1.1.5: + resolution: {integrity: sha512-eDMORYaPNZ4sQIuuYPDHdQvf4gyCF9rEEV/yPxGfwPkRodwEgiMUUXTx/dex+Me0wxx53S+NgUHaP7y3MGlDmg==} + + widest-line@4.0.1: + resolution: {integrity: sha512-o0cyEG0e8GPzT4iGHphIOh0cJOV8fivsXxddQasHPHfoZf1ZexrfeA21w2NaEN1RHE+fXlfISmOE8R9N3u3Qig==} + engines: {node: '>=12'} + + winston-daily-rotate-file@5.0.0: + resolution: {integrity: sha512-JDjiXXkM5qvwY06733vf09I2wnMXpZEhxEVOSPenZMii+g7pcDcTBt2MRugnoi8BwVSuCT2jfRXBUy+n1Zz/Yw==} + engines: {node: '>=8'} + peerDependencies: + winston: ^3 + + winston-transport@4.9.0: + resolution: {integrity: sha512-8drMJ4rkgaPo1Me4zD/3WLfI/zPdA9o2IipKODunnGDcuqbHwjsbB79ylv04LCGGzU0xQ6vTznOMpQGaLhhm6A==} + engines: {node: '>= 12.0.0'} + + winston@3.18.3: + resolution: {integrity: sha512-NoBZauFNNWENgsnC9YpgyYwOVrl2m58PpQ8lNHjV3kosGs7KJ7Npk9pCUE+WJlawVSe8mykWDKWFSVfs3QO9ww==} + engines: {node: '>= 12.0.0'} + + word-wrap@1.2.5: + resolution: {integrity: sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==} + engines: {node: '>=0.10.0'} + + workbox-background-sync@6.6.0: + resolution: {integrity: sha512-jkf4ZdgOJxC9u2vztxLuPT/UjlH7m/nWRQ/MgGL0v8BJHoZdVGJd18Kck+a0e55wGXdqyHO+4IQTk0685g4MUw==} + + workbox-broadcast-update@6.6.0: + resolution: {integrity: sha512-nm+v6QmrIFaB/yokJmQ/93qIJ7n72NICxIwQwe5xsZiV2aI93MGGyEyzOzDPVz5THEr5rC3FJSsO3346cId64Q==} + + workbox-build@6.6.0: + resolution: {integrity: sha512-Tjf+gBwOTuGyZwMz2Nk/B13Fuyeo0Q84W++bebbVsfr9iLkDSo6j6PST8tET9HYA58mlRXwlMGpyWO8ETJiXdQ==} + engines: {node: '>=10.0.0'} + + workbox-cacheable-response@6.6.0: + resolution: {integrity: sha512-JfhJUSQDwsF1Xv3EV1vWzSsCOZn4mQ38bWEBR3LdvOxSPgB65gAM6cS2CX8rkkKHRgiLrN7Wxoyu+TuH67kHrw==} + deprecated: workbox-background-sync@6.6.0 + + workbox-core@6.6.0: + resolution: {integrity: sha512-GDtFRF7Yg3DD859PMbPAYPeJyg5gJYXuBQAC+wyrWuuXgpfoOrIQIvFRZnQ7+czTIQjIr1DhLEGFzZanAT/3bQ==} + + workbox-expiration@6.6.0: + resolution: {integrity: sha512-baplYXcDHbe8vAo7GYvyAmlS4f6998Jff513L4XvlzAOxcl8F620O91guoJ5EOf5qeXG4cGdNZHkkVAPouFCpw==} + + workbox-google-analytics@6.6.0: + resolution: {integrity: sha512-p4DJa6OldXWd6M9zRl0H6vB9lkrmqYFkRQ2xEiNdBFp9U0LhsGO7hsBscVEyH9H2/3eZZt8c97NB2FD9U2NJ+Q==} + deprecated: It is not compatible with newer versions of GA starting with v4, as long as you are using GAv3 it should be ok, but the package is not longer being maintained + + workbox-navigation-preload@6.6.0: + resolution: {integrity: sha512-utNEWG+uOfXdaZmvhshrh7KzhDu/1iMHyQOV6Aqup8Mm78D286ugu5k9MFD9SzBT5TcwgwSORVvInaXWbvKz9Q==} + + workbox-precaching@6.6.0: + resolution: {integrity: sha512-eYu/7MqtRZN1IDttl/UQcSZFkHP7dnvr/X3Vn6Iw6OsPMruQHiVjjomDFCNtd8k2RdjLs0xiz9nq+t3YVBcWPw==} + + workbox-range-requests@6.6.0: + resolution: {integrity: sha512-V3aICz5fLGq5DpSYEU8LxeXvsT//mRWzKrfBOIxzIdQnV/Wj7R+LyJVTczi4CQ4NwKhAaBVaSujI1cEjXW+hTw==} + + workbox-recipes@6.6.0: + resolution: {integrity: sha512-TFi3kTgYw73t5tg73yPVqQC8QQjxJSeqjXRO4ouE/CeypmP2O/xqmB/ZFBBQazLTPxILUQ0b8aeh0IuxVn9a6A==} + + workbox-routing@6.6.0: + resolution: {integrity: sha512-x8gdN7VDBiLC03izAZRfU+WKUXJnbqt6PG9Uh0XuPRzJPpZGLKce/FkOX95dWHRpOHWLEq8RXzjW0O+POSkKvw==} + + workbox-strategies@6.6.0: + resolution: {integrity: sha512-eC07XGuINAKUWDnZeIPdRdVja4JQtTuc35TZ8SwMb1ztjp7Ddq2CJ4yqLvWzFWGlYI7CG/YGqaETntTxBGdKgQ==} + + workbox-streams@6.6.0: + resolution: {integrity: sha512-rfMJLVvwuED09CnH1RnIep7L9+mj4ufkTyDPVaXPKlhi9+0czCu+SJggWCIFbPpJaAZmp2iyVGLqS3RUmY3fxg==} + + workbox-sw@6.6.0: + resolution: {integrity: sha512-R2IkwDokbtHUE4Kus8pKO5+VkPHD2oqTgl+XJwh4zbF1HyjAbgNmK/FneZHVU7p03XUt9ICfuGDYISWG9qV/CQ==} + + workbox-webpack-plugin@6.6.0: + resolution: {integrity: sha512-xNZIZHalboZU66Wa7x1YkjIqEy1gTR+zPM+kjrYJzqN7iurYZBctBLISyScjhkJKYuRrZUP0iqViZTh8rS0+3A==} + engines: {node: '>=10.0.0'} + peerDependencies: + webpack: ^4.4.0 || ^5.9.0 + + workbox-window@6.6.0: + resolution: {integrity: sha512-L4N9+vka17d16geaJXXRjENLFldvkWy7JyGxElRD0JvBxvFEd8LOhr+uXCcar/NzAmIBRv9EZ+M+Qr4mOoBITw==} + + wrap-ansi@6.2.0: + resolution: {integrity: sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==} + engines: {node: '>=8'} + + wrap-ansi@7.0.0: + resolution: {integrity: sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==} + engines: {node: '>=10'} + + wrap-ansi@8.1.0: + resolution: {integrity: sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==} + engines: {node: '>=12'} + + wrappy@1.0.2: + resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==} + + write-file-atomic@3.0.3: + resolution: {integrity: sha512-AvHcyZ5JnSfq3ioSyjrBkH9yW4m7Ayk8/9My/DD9onKeu/94fwrMocemO2QAJFAlnnDN+ZDS+ZjAR5ua1/PV/Q==} + + ws@8.18.3: + resolution: {integrity: sha512-PEIGCY5tSlUt50cqyMXfCzX+oOPqN0vuGqWzbcJ2xvnkzkq46oOpz7dQaTDBdfICb4N14+GARUDw2XV2N4tvzg==} + engines: {node: '>=10.0.0'} + peerDependencies: + bufferutil: ^4.0.1 + utf-8-validate: '>=5.0.2' + peerDependenciesMeta: + bufferutil: + optional: true + utf-8-validate: + optional: true + + xdg-default-browser@2.1.0: + resolution: {integrity: sha512-HY4G725+IDQr16N8XOjAms5qJGArdJaWIuC7Q7A8UXIwj2mifqnPXephazyL7sIkQPvmEoPX3E0v2yFv6hQUNg==} + engines: {node: '>=4'} + + xml-name-validator@3.0.0: + resolution: {integrity: sha512-A5CUptxDsvxKJEU3yO6DuWBSJz/qizqzJKOMIfUJHETbBw/sFaDxgd6fxm1ewUaM0jZ444Fc5vC5ROYurg/4Pw==} + + xml-name-validator@4.0.0: + resolution: {integrity: sha512-ICP2e+jsHvAj2E2lIHxa5tjXRlKDJo4IdvPvCXbXQGdzSfmSpNVyIKMvoZHjDY9DP0zV17iI85o90vRFXNccRw==} + engines: {node: '>=12'} + + xmlchars@2.2.0: + resolution: {integrity: sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==} + + xmlhttprequest-ssl@2.1.2: + resolution: {integrity: sha512-TEU+nJVUUnA4CYJFLvK5X9AOeH4KvDvhIfm0vV1GaQRtchnG0hgK5p8hw/xjv8cunWYCsiPCSDzObPyhEwq3KQ==} + engines: {node: '>=0.4.0'} + + xtend@4.0.2: + resolution: {integrity: sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==} + engines: {node: '>=0.4'} + + y18n@5.0.8: + resolution: {integrity: sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==} + engines: {node: '>=10'} + + yallist@2.1.2: + resolution: {integrity: sha512-ncTzHV7NvsQZkYe1DW7cbDLm0YpzHmZF5r/iyP3ZnQtMiJ+pjzisCiMNI+Sj+xQF5pXhSHxSB3uDbsBTzY/c2A==} + + yallist@3.1.1: + resolution: {integrity: sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==} + + yallist@4.0.0: + resolution: {integrity: sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==} + + yaml@1.10.2: + resolution: {integrity: sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==} + engines: {node: '>= 6'} + + yaml@2.3.1: + resolution: {integrity: sha512-2eHWfjaoXgTBC2jNM1LRef62VQa0umtvRiDSk6HSzW7RvS5YtkabJrwYLLEKWBc8a5U2PTSCs+dJjUTJdlHsWQ==} + engines: {node: '>= 14'} + + yaml@2.8.1: + resolution: {integrity: sha512-lcYcMxX2PO9XMGvAJkJ3OsNMw+/7FKes7/hgerGUYWIoWu5j/+YQqcZr5JnPZWzOsEBgMbSbiSTn/dv/69Mkpw==} + engines: {node: '>= 14.6'} + hasBin: true + + yargs-parser@20.2.9: + resolution: {integrity: sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==} + engines: {node: '>=10'} + + yargs-parser@21.1.1: + resolution: {integrity: sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==} + engines: {node: '>=12'} + + yargs@16.2.0: + resolution: {integrity: sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==} + engines: {node: '>=10'} + + yargs@17.7.2: + resolution: {integrity: sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==} + engines: {node: '>=12'} + + yauzl@2.10.0: + resolution: {integrity: sha512-p4a9I6X6nu6IhoGmBqAcbJy1mlC4j27vEPZX9F4L4/vZT3Lyq1VkFHw/V/PUcB9Buo+DG3iHkT0x3Qya58zc3g==} + + yn@3.1.1: + resolution: {integrity: sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==} + engines: {node: '>=6'} + + yocto-queue@0.1.0: + resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==} + engines: {node: '>=10'} + + yup@0.32.11: + resolution: {integrity: sha512-Z2Fe1bn+eLstG8DRR6FTavGD+MeAwyfmouhHsIUgaADz8jvFKbO/fXc2trJKZg+5EBjh4gGm3iU/t3onKlXHIg==} + engines: {node: '>=10'} + + zip-stream@5.0.2: + resolution: {integrity: sha512-LfOdrUvPB8ZoXtvOBz6DlNClfvi//b5d56mSWyJi7XbH/HfhOHfUhOqxhT/rUiR7yiktlunqRo+jY6y/cWC/5g==} + engines: {node: '>= 12.0.0'} + + zod@3.25.76: + resolution: {integrity: sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==} + + zustand@4.5.7: + resolution: {integrity: sha512-CHOUy7mu3lbD6o6LJLfllpjkzhHXSBlX8B9+qPddUsIfeF5S/UZ5q0kmCsnRqT1UHFQZchNFDDzMbQsuesHWlw==} + engines: {node: '>=12.7.0'} + peerDependencies: + '@types/react': '>=16.8' + immer: '>=9.0.6' + react: '>=16.8' + peerDependenciesMeta: + '@types/react': + optional: true + immer: + optional: true + react: + optional: true + + zwitch@2.0.4: + resolution: {integrity: sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A==} + +snapshots: + + '@adobe/css-tools@4.4.4': {} + + '@alloc/quick-lru@5.2.0': {} + + '@apideck/better-ajv-errors@0.3.6(ajv@8.17.1)': + dependencies: + ajv: 8.17.1 + json-schema: 0.4.0 + jsonpointer: 5.0.1 + leven: 3.1.0 + + '@aws-crypto/sha256-browser@5.2.0': + dependencies: + '@aws-crypto/sha256-js': 5.2.0 + '@aws-crypto/supports-web-crypto': 5.2.0 + '@aws-crypto/util': 5.2.0 + '@aws-sdk/types': 3.973.7 + '@aws-sdk/util-locate-window': 3.893.0 + '@smithy/util-utf8': 2.3.0 + tslib: 2.8.1 + optional: true + + '@aws-crypto/sha256-js@5.2.0': + dependencies: + '@aws-crypto/util': 5.2.0 + '@aws-sdk/types': 3.973.7 + tslib: 2.8.1 + optional: true + + '@aws-crypto/supports-web-crypto@5.2.0': + dependencies: + tslib: 2.8.1 + optional: true + + '@aws-crypto/util@5.2.0': + dependencies: + '@aws-sdk/types': 3.973.7 + '@smithy/util-utf8': 2.3.0 + tslib: 2.8.1 + optional: true + + '@aws-sdk/client-cognito-identity@3.908.0': + dependencies: + '@aws-crypto/sha256-browser': 5.2.0 + '@aws-crypto/sha256-js': 5.2.0 + '@aws-sdk/core': 3.908.0 + '@aws-sdk/credential-provider-node': 3.908.0 + '@aws-sdk/middleware-host-header': 3.901.0 + '@aws-sdk/middleware-logger': 3.901.0 + '@aws-sdk/middleware-recursion-detection': 3.901.0 + '@aws-sdk/middleware-user-agent': 3.908.0 + '@aws-sdk/region-config-resolver': 3.901.0 + '@aws-sdk/types': 3.901.0 + '@aws-sdk/util-endpoints': 3.901.0 + '@aws-sdk/util-user-agent-browser': 3.907.0 + '@aws-sdk/util-user-agent-node': 3.908.0 + '@smithy/config-resolver': 4.4.14 + '@smithy/core': 3.23.14 + '@smithy/fetch-http-handler': 5.3.16 + '@smithy/hash-node': 4.2.13 + '@smithy/invalid-dependency': 4.2.13 + '@smithy/middleware-content-length': 4.2.13 + '@smithy/middleware-endpoint': 4.4.29 + '@smithy/middleware-retry': 4.5.1 + '@smithy/middleware-serde': 4.2.17 + '@smithy/middleware-stack': 4.2.13 + '@smithy/node-config-provider': 4.3.13 + '@smithy/node-http-handler': 4.5.2 + '@smithy/protocol-http': 5.3.13 + '@smithy/smithy-client': 4.12.9 + '@smithy/types': 4.14.0 + '@smithy/url-parser': 4.2.13 + '@smithy/util-base64': 4.3.2 + '@smithy/util-body-length-browser': 4.2.2 + '@smithy/util-body-length-node': 4.2.3 + '@smithy/util-defaults-mode-browser': 4.3.45 + '@smithy/util-defaults-mode-node': 4.2.49 + '@smithy/util-endpoints': 3.3.4 + '@smithy/util-middleware': 4.2.13 + '@smithy/util-retry': 4.3.1 + '@smithy/util-utf8': 4.2.2 + tslib: 2.8.1 + transitivePeerDependencies: + - aws-crt + optional: true + + '@aws-sdk/client-sso@3.908.0': + dependencies: + '@aws-crypto/sha256-browser': 5.2.0 + '@aws-crypto/sha256-js': 5.2.0 + '@aws-sdk/core': 3.908.0 + '@aws-sdk/middleware-host-header': 3.901.0 + '@aws-sdk/middleware-logger': 3.901.0 + '@aws-sdk/middleware-recursion-detection': 3.901.0 + '@aws-sdk/middleware-user-agent': 3.908.0 + '@aws-sdk/region-config-resolver': 3.901.0 + '@aws-sdk/types': 3.901.0 + '@aws-sdk/util-endpoints': 3.901.0 + '@aws-sdk/util-user-agent-browser': 3.907.0 + '@aws-sdk/util-user-agent-node': 3.908.0 + '@smithy/config-resolver': 4.4.14 + '@smithy/core': 3.23.14 + '@smithy/fetch-http-handler': 5.3.16 + '@smithy/hash-node': 4.2.13 + '@smithy/invalid-dependency': 4.2.13 + '@smithy/middleware-content-length': 4.2.13 + '@smithy/middleware-endpoint': 4.4.29 + '@smithy/middleware-retry': 4.5.1 + '@smithy/middleware-serde': 4.2.17 + '@smithy/middleware-stack': 4.2.13 + '@smithy/node-config-provider': 4.3.13 + '@smithy/node-http-handler': 4.5.2 + '@smithy/protocol-http': 5.3.13 + '@smithy/smithy-client': 4.12.9 + '@smithy/types': 4.14.0 + '@smithy/url-parser': 4.2.13 + '@smithy/util-base64': 4.3.2 + '@smithy/util-body-length-browser': 4.2.2 + '@smithy/util-body-length-node': 4.2.3 + '@smithy/util-defaults-mode-browser': 4.3.45 + '@smithy/util-defaults-mode-node': 4.2.49 + '@smithy/util-endpoints': 3.3.4 + '@smithy/util-middleware': 4.2.13 + '@smithy/util-retry': 4.3.1 + '@smithy/util-utf8': 4.2.2 + tslib: 2.8.1 + transitivePeerDependencies: + - aws-crt + optional: true + + '@aws-sdk/core@3.908.0': + dependencies: + '@aws-sdk/types': 3.901.0 + '@aws-sdk/xml-builder': 3.901.0 + '@smithy/core': 3.23.14 + '@smithy/node-config-provider': 4.3.13 + '@smithy/property-provider': 4.2.13 + '@smithy/protocol-http': 5.3.13 + '@smithy/signature-v4': 5.3.13 + '@smithy/smithy-client': 4.12.9 + '@smithy/types': 4.14.0 + '@smithy/util-base64': 4.3.2 + '@smithy/util-middleware': 4.2.13 + '@smithy/util-utf8': 4.2.2 + tslib: 2.8.1 + optional: true + + '@aws-sdk/credential-provider-cognito-identity@3.908.0': + dependencies: + '@aws-sdk/client-cognito-identity': 3.908.0 + '@aws-sdk/types': 3.901.0 + '@smithy/property-provider': 4.2.13 + '@smithy/types': 4.14.0 + tslib: 2.8.1 + transitivePeerDependencies: + - aws-crt + optional: true + + '@aws-sdk/credential-provider-env@3.908.0': + dependencies: + '@aws-sdk/core': 3.908.0 + '@aws-sdk/types': 3.901.0 + '@smithy/property-provider': 4.2.13 + '@smithy/types': 4.14.0 + tslib: 2.8.1 + optional: true + + '@aws-sdk/credential-provider-http@3.908.0': + dependencies: + '@aws-sdk/core': 3.908.0 + '@aws-sdk/types': 3.901.0 + '@smithy/fetch-http-handler': 5.3.16 + '@smithy/node-http-handler': 4.5.2 + '@smithy/property-provider': 4.2.13 + '@smithy/protocol-http': 5.3.13 + '@smithy/smithy-client': 4.12.9 + '@smithy/types': 4.14.0 + '@smithy/util-stream': 4.5.22 + tslib: 2.8.1 + optional: true + + '@aws-sdk/credential-provider-ini@3.908.0': + dependencies: + '@aws-sdk/core': 3.908.0 + '@aws-sdk/credential-provider-env': 3.908.0 + '@aws-sdk/credential-provider-http': 3.908.0 + '@aws-sdk/credential-provider-process': 3.908.0 + '@aws-sdk/credential-provider-sso': 3.908.0 + '@aws-sdk/credential-provider-web-identity': 3.908.0 + '@aws-sdk/nested-clients': 3.908.0 + '@aws-sdk/types': 3.901.0 + '@smithy/credential-provider-imds': 4.2.13 + '@smithy/property-provider': 4.2.13 + '@smithy/shared-ini-file-loader': 4.4.8 + '@smithy/types': 4.14.0 + tslib: 2.8.1 + transitivePeerDependencies: + - aws-crt + optional: true + + '@aws-sdk/credential-provider-node@3.908.0': + dependencies: + '@aws-sdk/credential-provider-env': 3.908.0 + '@aws-sdk/credential-provider-http': 3.908.0 + '@aws-sdk/credential-provider-ini': 3.908.0 + '@aws-sdk/credential-provider-process': 3.908.0 + '@aws-sdk/credential-provider-sso': 3.908.0 + '@aws-sdk/credential-provider-web-identity': 3.908.0 + '@aws-sdk/types': 3.901.0 + '@smithy/credential-provider-imds': 4.2.13 + '@smithy/property-provider': 4.2.13 + '@smithy/shared-ini-file-loader': 4.4.8 + '@smithy/types': 4.14.0 + tslib: 2.8.1 + transitivePeerDependencies: + - aws-crt + optional: true + + '@aws-sdk/credential-provider-process@3.908.0': + dependencies: + '@aws-sdk/core': 3.908.0 + '@aws-sdk/types': 3.901.0 + '@smithy/property-provider': 4.2.13 + '@smithy/shared-ini-file-loader': 4.4.8 + '@smithy/types': 4.14.0 + tslib: 2.8.1 + optional: true + + '@aws-sdk/credential-provider-sso@3.908.0': + dependencies: + '@aws-sdk/client-sso': 3.908.0 + '@aws-sdk/core': 3.908.0 + '@aws-sdk/token-providers': 3.908.0 + '@aws-sdk/types': 3.901.0 + '@smithy/property-provider': 4.2.13 + '@smithy/shared-ini-file-loader': 4.4.8 + '@smithy/types': 4.14.0 + tslib: 2.8.1 + transitivePeerDependencies: + - aws-crt + optional: true + + '@aws-sdk/credential-provider-web-identity@3.908.0': + dependencies: + '@aws-sdk/core': 3.908.0 + '@aws-sdk/nested-clients': 3.908.0 + '@aws-sdk/types': 3.901.0 + '@smithy/property-provider': 4.2.13 + '@smithy/shared-ini-file-loader': 4.4.8 + '@smithy/types': 4.14.0 + tslib: 2.8.1 + transitivePeerDependencies: + - aws-crt + optional: true + + '@aws-sdk/credential-providers@3.908.0': + dependencies: + '@aws-sdk/client-cognito-identity': 3.908.0 + '@aws-sdk/core': 3.908.0 + '@aws-sdk/credential-provider-cognito-identity': 3.908.0 + '@aws-sdk/credential-provider-env': 3.908.0 + '@aws-sdk/credential-provider-http': 3.908.0 + '@aws-sdk/credential-provider-ini': 3.908.0 + '@aws-sdk/credential-provider-node': 3.908.0 + '@aws-sdk/credential-provider-process': 3.908.0 + '@aws-sdk/credential-provider-sso': 3.908.0 + '@aws-sdk/credential-provider-web-identity': 3.908.0 + '@aws-sdk/nested-clients': 3.908.0 + '@aws-sdk/types': 3.901.0 + '@smithy/config-resolver': 4.4.14 + '@smithy/core': 3.23.14 + '@smithy/credential-provider-imds': 4.2.13 + '@smithy/node-config-provider': 4.3.13 + '@smithy/property-provider': 4.2.13 + '@smithy/types': 4.14.0 + tslib: 2.8.1 + transitivePeerDependencies: + - aws-crt + optional: true + + '@aws-sdk/middleware-host-header@3.901.0': + dependencies: + '@aws-sdk/types': 3.901.0 + '@smithy/protocol-http': 5.3.13 + '@smithy/types': 4.14.0 + tslib: 2.8.1 + optional: true + + '@aws-sdk/middleware-logger@3.901.0': + dependencies: + '@aws-sdk/types': 3.901.0 + '@smithy/types': 4.14.0 + tslib: 2.8.1 + optional: true + + '@aws-sdk/middleware-recursion-detection@3.901.0': + dependencies: + '@aws-sdk/types': 3.901.0 + '@aws/lambda-invoke-store': 0.0.1 + '@smithy/protocol-http': 5.3.13 + '@smithy/types': 4.14.0 + tslib: 2.8.1 + optional: true + + '@aws-sdk/middleware-user-agent@3.908.0': + dependencies: + '@aws-sdk/core': 3.908.0 + '@aws-sdk/types': 3.901.0 + '@aws-sdk/util-endpoints': 3.901.0 + '@smithy/core': 3.23.14 + '@smithy/protocol-http': 5.3.13 + '@smithy/types': 4.14.0 + tslib: 2.8.1 + optional: true + + '@aws-sdk/nested-clients@3.908.0': + dependencies: + '@aws-crypto/sha256-browser': 5.2.0 + '@aws-crypto/sha256-js': 5.2.0 + '@aws-sdk/core': 3.908.0 + '@aws-sdk/middleware-host-header': 3.901.0 + '@aws-sdk/middleware-logger': 3.901.0 + '@aws-sdk/middleware-recursion-detection': 3.901.0 + '@aws-sdk/middleware-user-agent': 3.908.0 + '@aws-sdk/region-config-resolver': 3.901.0 + '@aws-sdk/types': 3.901.0 + '@aws-sdk/util-endpoints': 3.901.0 + '@aws-sdk/util-user-agent-browser': 3.907.0 + '@aws-sdk/util-user-agent-node': 3.908.0 + '@smithy/config-resolver': 4.4.14 + '@smithy/core': 3.23.14 + '@smithy/fetch-http-handler': 5.3.16 + '@smithy/hash-node': 4.2.13 + '@smithy/invalid-dependency': 4.2.13 + '@smithy/middleware-content-length': 4.2.13 + '@smithy/middleware-endpoint': 4.4.29 + '@smithy/middleware-retry': 4.5.1 + '@smithy/middleware-serde': 4.2.17 + '@smithy/middleware-stack': 4.2.13 + '@smithy/node-config-provider': 4.3.13 + '@smithy/node-http-handler': 4.5.2 + '@smithy/protocol-http': 5.3.13 + '@smithy/smithy-client': 4.12.9 + '@smithy/types': 4.14.0 + '@smithy/url-parser': 4.2.13 + '@smithy/util-base64': 4.3.2 + '@smithy/util-body-length-browser': 4.2.2 + '@smithy/util-body-length-node': 4.2.3 + '@smithy/util-defaults-mode-browser': 4.3.45 + '@smithy/util-defaults-mode-node': 4.2.49 + '@smithy/util-endpoints': 3.3.4 + '@smithy/util-middleware': 4.2.13 + '@smithy/util-retry': 4.3.1 + '@smithy/util-utf8': 4.2.2 + tslib: 2.8.1 + transitivePeerDependencies: + - aws-crt + optional: true + + '@aws-sdk/region-config-resolver@3.901.0': + dependencies: + '@aws-sdk/types': 3.901.0 + '@smithy/node-config-provider': 4.3.13 + '@smithy/types': 4.14.0 + '@smithy/util-config-provider': 4.2.2 + '@smithy/util-middleware': 4.2.13 + tslib: 2.8.1 + optional: true + + '@aws-sdk/token-providers@3.908.0': + dependencies: + '@aws-sdk/core': 3.908.0 + '@aws-sdk/nested-clients': 3.908.0 + '@aws-sdk/types': 3.901.0 + '@smithy/property-provider': 4.2.13 + '@smithy/shared-ini-file-loader': 4.4.8 + '@smithy/types': 4.14.0 + tslib: 2.8.1 + transitivePeerDependencies: + - aws-crt + optional: true + + '@aws-sdk/types@3.901.0': + dependencies: + '@smithy/types': 4.14.0 + tslib: 2.8.1 + optional: true + + '@aws-sdk/types@3.973.7': + dependencies: + '@smithy/types': 4.14.0 + tslib: 2.8.1 + optional: true + + '@aws-sdk/util-endpoints@3.901.0': + dependencies: + '@aws-sdk/types': 3.901.0 + '@smithy/types': 4.14.0 + '@smithy/url-parser': 4.2.13 + '@smithy/util-endpoints': 3.3.4 + tslib: 2.8.1 + optional: true + + '@aws-sdk/util-locate-window@3.893.0': + dependencies: + tslib: 2.8.1 + optional: true + + '@aws-sdk/util-user-agent-browser@3.907.0': + dependencies: + '@aws-sdk/types': 3.901.0 + '@smithy/types': 4.14.0 + bowser: 2.12.1 + tslib: 2.8.1 + optional: true + + '@aws-sdk/util-user-agent-node@3.908.0': + dependencies: + '@aws-sdk/middleware-user-agent': 3.908.0 + '@aws-sdk/types': 3.901.0 + '@smithy/node-config-provider': 4.3.13 + '@smithy/types': 4.14.0 + tslib: 2.8.1 + optional: true + + '@aws-sdk/xml-builder@3.901.0': + dependencies: + '@smithy/types': 4.14.0 + fast-xml-parser: 5.2.5 + tslib: 2.8.1 + optional: true + + '@aws/lambda-invoke-store@0.0.1': + optional: true + + '@babel/code-frame@7.27.1': + dependencies: + '@babel/helper-validator-identifier': 7.27.1 + js-tokens: 4.0.0 + picocolors: 1.1.1 + + '@babel/compat-data@7.28.4': {} + + '@babel/core@7.28.4': + dependencies: + '@babel/code-frame': 7.27.1 + '@babel/generator': 7.28.3 + '@babel/helper-compilation-targets': 7.27.2 + '@babel/helper-module-transforms': 7.28.3(@babel/core@7.28.4) + '@babel/helpers': 7.28.4 + '@babel/parser': 7.28.4 + '@babel/template': 7.27.2 + '@babel/traverse': 7.28.4 + '@babel/types': 7.28.4 + '@jridgewell/remapping': 2.3.5 + convert-source-map: 2.0.0 + debug: 4.4.3(supports-color@8.1.1) + gensync: 1.0.0-beta.2 + json5: 2.2.3 + semver: 7.7.1 + transitivePeerDependencies: + - supports-color + + '@babel/eslint-parser@7.28.4(@babel/core@7.28.4)(eslint@8.57.1)': + dependencies: + '@babel/core': 7.28.4 + '@nicolo-ribaudo/eslint-scope-5-internals': 5.1.1-v1 + eslint: 8.57.1 + eslint-visitor-keys: 2.1.0 + semver: 7.7.1 + + '@babel/generator@7.28.3': + dependencies: + '@babel/parser': 7.28.4 + '@babel/types': 7.28.4 + '@jridgewell/gen-mapping': 0.3.13 + '@jridgewell/trace-mapping': 0.3.31 + jsesc: 3.1.0 + + '@babel/helper-annotate-as-pure@7.27.3': + dependencies: + '@babel/types': 7.28.4 + + '@babel/helper-compilation-targets@7.27.2': + dependencies: + '@babel/compat-data': 7.28.4 + '@babel/helper-validator-option': 7.27.1 + browserslist: 4.26.3 + lru-cache: 5.1.1 + semver: 7.7.1 + + '@babel/helper-create-class-features-plugin@7.28.3(@babel/core@7.28.4)': + dependencies: + '@babel/core': 7.28.4 + '@babel/helper-annotate-as-pure': 7.27.3 + '@babel/helper-member-expression-to-functions': 7.27.1 + '@babel/helper-optimise-call-expression': 7.27.1 + '@babel/helper-replace-supers': 7.27.1(@babel/core@7.28.4) + '@babel/helper-skip-transparent-expression-wrappers': 7.27.1 + '@babel/traverse': 7.28.4 + semver: 7.7.1 + transitivePeerDependencies: + - supports-color + + '@babel/helper-create-regexp-features-plugin@7.27.1(@babel/core@7.28.4)': + dependencies: + '@babel/core': 7.28.4 + '@babel/helper-annotate-as-pure': 7.27.3 + regexpu-core: 6.4.0 + semver: 7.7.1 + + '@babel/helper-define-polyfill-provider@0.6.5(@babel/core@7.28.4)': + dependencies: + '@babel/core': 7.28.4 + '@babel/helper-compilation-targets': 7.27.2 + '@babel/helper-plugin-utils': 7.27.1 + debug: 4.4.3(supports-color@8.1.1) + lodash.debounce: 4.0.8 + resolve: 1.22.10 + transitivePeerDependencies: + - supports-color + + '@babel/helper-globals@7.28.0': {} + + '@babel/helper-member-expression-to-functions@7.27.1': + dependencies: + '@babel/traverse': 7.28.4 + '@babel/types': 7.28.4 + transitivePeerDependencies: + - supports-color + + '@babel/helper-module-imports@7.27.1': + dependencies: + '@babel/traverse': 7.28.4 + '@babel/types': 7.28.4 + transitivePeerDependencies: + - supports-color + + '@babel/helper-module-transforms@7.28.3(@babel/core@7.28.4)': + dependencies: + '@babel/core': 7.28.4 + '@babel/helper-module-imports': 7.27.1 + '@babel/helper-validator-identifier': 7.27.1 + '@babel/traverse': 7.28.4 + transitivePeerDependencies: + - supports-color + + '@babel/helper-optimise-call-expression@7.27.1': + dependencies: + '@babel/types': 7.28.4 + + '@babel/helper-plugin-utils@7.27.1': {} + + '@babel/helper-remap-async-to-generator@7.27.1(@babel/core@7.28.4)': + dependencies: + '@babel/core': 7.28.4 + '@babel/helper-annotate-as-pure': 7.27.3 + '@babel/helper-wrap-function': 7.28.3 + '@babel/traverse': 7.28.4 + transitivePeerDependencies: + - supports-color + + '@babel/helper-replace-supers@7.27.1(@babel/core@7.28.4)': + dependencies: + '@babel/core': 7.28.4 + '@babel/helper-member-expression-to-functions': 7.27.1 + '@babel/helper-optimise-call-expression': 7.27.1 + '@babel/traverse': 7.28.4 + transitivePeerDependencies: + - supports-color + + '@babel/helper-skip-transparent-expression-wrappers@7.27.1': + dependencies: + '@babel/traverse': 7.28.4 + '@babel/types': 7.28.4 + transitivePeerDependencies: + - supports-color + + '@babel/helper-string-parser@7.27.1': {} + + '@babel/helper-validator-identifier@7.27.1': {} + + '@babel/helper-validator-option@7.27.1': {} + + '@babel/helper-wrap-function@7.28.3': + dependencies: + '@babel/template': 7.27.2 + '@babel/traverse': 7.28.4 + '@babel/types': 7.28.4 + transitivePeerDependencies: + - supports-color + + '@babel/helpers@7.28.4': + dependencies: + '@babel/template': 7.27.2 + '@babel/types': 7.28.4 + + '@babel/parser@7.28.4': + dependencies: + '@babel/types': 7.28.4 + + '@babel/plugin-bugfix-firefox-class-in-computed-class-key@7.27.1(@babel/core@7.28.4)': + dependencies: + '@babel/core': 7.28.4 + '@babel/helper-plugin-utils': 7.27.1 + '@babel/traverse': 7.28.4 + transitivePeerDependencies: + - supports-color + + '@babel/plugin-bugfix-safari-class-field-initializer-scope@7.27.1(@babel/core@7.28.4)': + dependencies: + '@babel/core': 7.28.4 + '@babel/helper-plugin-utils': 7.27.1 + + '@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@7.27.1(@babel/core@7.28.4)': + dependencies: + '@babel/core': 7.28.4 + '@babel/helper-plugin-utils': 7.27.1 + + '@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining@7.27.1(@babel/core@7.28.4)': + dependencies: + '@babel/core': 7.28.4 + '@babel/helper-plugin-utils': 7.27.1 + '@babel/helper-skip-transparent-expression-wrappers': 7.27.1 + '@babel/plugin-transform-optional-chaining': 7.27.1(@babel/core@7.28.4) + transitivePeerDependencies: + - supports-color + + '@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly@7.28.3(@babel/core@7.28.4)': + dependencies: + '@babel/core': 7.28.4 + '@babel/helper-plugin-utils': 7.27.1 + '@babel/traverse': 7.28.4 + transitivePeerDependencies: + - supports-color + + '@babel/plugin-proposal-class-properties@7.18.6(@babel/core@7.28.4)': + dependencies: + '@babel/core': 7.28.4 + '@babel/helper-create-class-features-plugin': 7.28.3(@babel/core@7.28.4) + '@babel/helper-plugin-utils': 7.27.1 + transitivePeerDependencies: + - supports-color + + '@babel/plugin-proposal-decorators@7.28.0(@babel/core@7.28.4)': + dependencies: + '@babel/core': 7.28.4 + '@babel/helper-create-class-features-plugin': 7.28.3(@babel/core@7.28.4) + '@babel/helper-plugin-utils': 7.27.1 + '@babel/plugin-syntax-decorators': 7.27.1(@babel/core@7.28.4) + transitivePeerDependencies: + - supports-color + + '@babel/plugin-proposal-nullish-coalescing-operator@7.18.6(@babel/core@7.28.4)': + dependencies: + '@babel/core': 7.28.4 + '@babel/helper-plugin-utils': 7.27.1 + '@babel/plugin-syntax-nullish-coalescing-operator': 7.8.3(@babel/core@7.28.4) + + '@babel/plugin-proposal-numeric-separator@7.18.6(@babel/core@7.28.4)': + dependencies: + '@babel/core': 7.28.4 + '@babel/helper-plugin-utils': 7.27.1 + '@babel/plugin-syntax-numeric-separator': 7.10.4(@babel/core@7.28.4) + + '@babel/plugin-proposal-optional-chaining@7.21.0(@babel/core@7.28.4)': + dependencies: + '@babel/core': 7.28.4 + '@babel/helper-plugin-utils': 7.27.1 + '@babel/helper-skip-transparent-expression-wrappers': 7.27.1 + '@babel/plugin-syntax-optional-chaining': 7.8.3(@babel/core@7.28.4) + transitivePeerDependencies: + - supports-color + + '@babel/plugin-proposal-private-methods@7.18.6(@babel/core@7.28.4)': + dependencies: + '@babel/core': 7.28.4 + '@babel/helper-create-class-features-plugin': 7.28.3(@babel/core@7.28.4) + '@babel/helper-plugin-utils': 7.27.1 + transitivePeerDependencies: + - supports-color + + '@babel/plugin-proposal-private-property-in-object@7.21.0-placeholder-for-preset-env.2(@babel/core@7.28.4)': + dependencies: + '@babel/core': 7.28.4 + + '@babel/plugin-proposal-private-property-in-object@7.21.11(@babel/core@7.28.4)': + dependencies: + '@babel/core': 7.28.4 + '@babel/helper-annotate-as-pure': 7.27.3 + '@babel/helper-create-class-features-plugin': 7.28.3(@babel/core@7.28.4) + '@babel/helper-plugin-utils': 7.27.1 + '@babel/plugin-syntax-private-property-in-object': 7.14.5(@babel/core@7.28.4) + transitivePeerDependencies: + - supports-color + + '@babel/plugin-syntax-async-generators@7.8.4(@babel/core@7.28.4)': + dependencies: + '@babel/core': 7.28.4 + '@babel/helper-plugin-utils': 7.27.1 + + '@babel/plugin-syntax-bigint@7.8.3(@babel/core@7.28.4)': + dependencies: + '@babel/core': 7.28.4 + '@babel/helper-plugin-utils': 7.27.1 + + '@babel/plugin-syntax-class-properties@7.12.13(@babel/core@7.28.4)': + dependencies: + '@babel/core': 7.28.4 + '@babel/helper-plugin-utils': 7.27.1 + + '@babel/plugin-syntax-class-static-block@7.14.5(@babel/core@7.28.4)': + dependencies: + '@babel/core': 7.28.4 + '@babel/helper-plugin-utils': 7.27.1 + + '@babel/plugin-syntax-decorators@7.27.1(@babel/core@7.28.4)': + dependencies: + '@babel/core': 7.28.4 + '@babel/helper-plugin-utils': 7.27.1 + + '@babel/plugin-syntax-flow@7.27.1(@babel/core@7.28.4)': + dependencies: + '@babel/core': 7.28.4 + '@babel/helper-plugin-utils': 7.27.1 + + '@babel/plugin-syntax-import-assertions@7.27.1(@babel/core@7.28.4)': + dependencies: + '@babel/core': 7.28.4 + '@babel/helper-plugin-utils': 7.27.1 + + '@babel/plugin-syntax-import-attributes@7.27.1(@babel/core@7.28.4)': + dependencies: + '@babel/core': 7.28.4 + '@babel/helper-plugin-utils': 7.27.1 + + '@babel/plugin-syntax-import-meta@7.10.4(@babel/core@7.28.4)': + dependencies: + '@babel/core': 7.28.4 + '@babel/helper-plugin-utils': 7.27.1 + + '@babel/plugin-syntax-json-strings@7.8.3(@babel/core@7.28.4)': + dependencies: + '@babel/core': 7.28.4 + '@babel/helper-plugin-utils': 7.27.1 + + '@babel/plugin-syntax-jsx@7.27.1(@babel/core@7.28.4)': + dependencies: + '@babel/core': 7.28.4 + '@babel/helper-plugin-utils': 7.27.1 + + '@babel/plugin-syntax-logical-assignment-operators@7.10.4(@babel/core@7.28.4)': + dependencies: + '@babel/core': 7.28.4 + '@babel/helper-plugin-utils': 7.27.1 + + '@babel/plugin-syntax-nullish-coalescing-operator@7.8.3(@babel/core@7.28.4)': + dependencies: + '@babel/core': 7.28.4 + '@babel/helper-plugin-utils': 7.27.1 + + '@babel/plugin-syntax-numeric-separator@7.10.4(@babel/core@7.28.4)': + dependencies: + '@babel/core': 7.28.4 + '@babel/helper-plugin-utils': 7.27.1 + + '@babel/plugin-syntax-object-rest-spread@7.8.3(@babel/core@7.28.4)': + dependencies: + '@babel/core': 7.28.4 + '@babel/helper-plugin-utils': 7.27.1 + + '@babel/plugin-syntax-optional-catch-binding@7.8.3(@babel/core@7.28.4)': + dependencies: + '@babel/core': 7.28.4 + '@babel/helper-plugin-utils': 7.27.1 + + '@babel/plugin-syntax-optional-chaining@7.8.3(@babel/core@7.28.4)': + dependencies: + '@babel/core': 7.28.4 + '@babel/helper-plugin-utils': 7.27.1 + + '@babel/plugin-syntax-private-property-in-object@7.14.5(@babel/core@7.28.4)': + dependencies: + '@babel/core': 7.28.4 + '@babel/helper-plugin-utils': 7.27.1 + + '@babel/plugin-syntax-top-level-await@7.14.5(@babel/core@7.28.4)': + dependencies: + '@babel/core': 7.28.4 + '@babel/helper-plugin-utils': 7.27.1 + + '@babel/plugin-syntax-typescript@7.27.1(@babel/core@7.28.4)': + dependencies: + '@babel/core': 7.28.4 + '@babel/helper-plugin-utils': 7.27.1 + + '@babel/plugin-syntax-unicode-sets-regex@7.18.6(@babel/core@7.28.4)': + dependencies: + '@babel/core': 7.28.4 + '@babel/helper-create-regexp-features-plugin': 7.27.1(@babel/core@7.28.4) + '@babel/helper-plugin-utils': 7.27.1 + + '@babel/plugin-transform-arrow-functions@7.27.1(@babel/core@7.28.4)': + dependencies: + '@babel/core': 7.28.4 + '@babel/helper-plugin-utils': 7.27.1 + + '@babel/plugin-transform-async-generator-functions@7.28.0(@babel/core@7.28.4)': + dependencies: + '@babel/core': 7.28.4 + '@babel/helper-plugin-utils': 7.27.1 + '@babel/helper-remap-async-to-generator': 7.27.1(@babel/core@7.28.4) + '@babel/traverse': 7.28.4 + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-async-to-generator@7.27.1(@babel/core@7.28.4)': + dependencies: + '@babel/core': 7.28.4 + '@babel/helper-module-imports': 7.27.1 + '@babel/helper-plugin-utils': 7.27.1 + '@babel/helper-remap-async-to-generator': 7.27.1(@babel/core@7.28.4) + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-block-scoped-functions@7.27.1(@babel/core@7.28.4)': + dependencies: + '@babel/core': 7.28.4 + '@babel/helper-plugin-utils': 7.27.1 + + '@babel/plugin-transform-block-scoping@7.28.4(@babel/core@7.28.4)': + dependencies: + '@babel/core': 7.28.4 + '@babel/helper-plugin-utils': 7.27.1 + + '@babel/plugin-transform-class-properties@7.27.1(@babel/core@7.28.4)': + dependencies: + '@babel/core': 7.28.4 + '@babel/helper-create-class-features-plugin': 7.28.3(@babel/core@7.28.4) + '@babel/helper-plugin-utils': 7.27.1 + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-class-static-block@7.28.3(@babel/core@7.28.4)': + dependencies: + '@babel/core': 7.28.4 + '@babel/helper-create-class-features-plugin': 7.28.3(@babel/core@7.28.4) + '@babel/helper-plugin-utils': 7.27.1 + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-classes@7.28.4(@babel/core@7.28.4)': + dependencies: + '@babel/core': 7.28.4 + '@babel/helper-annotate-as-pure': 7.27.3 + '@babel/helper-compilation-targets': 7.27.2 + '@babel/helper-globals': 7.28.0 + '@babel/helper-plugin-utils': 7.27.1 + '@babel/helper-replace-supers': 7.27.1(@babel/core@7.28.4) + '@babel/traverse': 7.28.4 + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-computed-properties@7.27.1(@babel/core@7.28.4)': + dependencies: + '@babel/core': 7.28.4 + '@babel/helper-plugin-utils': 7.27.1 + '@babel/template': 7.27.2 + + '@babel/plugin-transform-destructuring@7.28.0(@babel/core@7.28.4)': + dependencies: + '@babel/core': 7.28.4 + '@babel/helper-plugin-utils': 7.27.1 + '@babel/traverse': 7.28.4 + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-dotall-regex@7.27.1(@babel/core@7.28.4)': + dependencies: + '@babel/core': 7.28.4 + '@babel/helper-create-regexp-features-plugin': 7.27.1(@babel/core@7.28.4) + '@babel/helper-plugin-utils': 7.27.1 + + '@babel/plugin-transform-duplicate-keys@7.27.1(@babel/core@7.28.4)': + dependencies: + '@babel/core': 7.28.4 + '@babel/helper-plugin-utils': 7.27.1 + + '@babel/plugin-transform-duplicate-named-capturing-groups-regex@7.27.1(@babel/core@7.28.4)': + dependencies: + '@babel/core': 7.28.4 + '@babel/helper-create-regexp-features-plugin': 7.27.1(@babel/core@7.28.4) + '@babel/helper-plugin-utils': 7.27.1 + + '@babel/plugin-transform-dynamic-import@7.27.1(@babel/core@7.28.4)': + dependencies: + '@babel/core': 7.28.4 + '@babel/helper-plugin-utils': 7.27.1 + + '@babel/plugin-transform-explicit-resource-management@7.28.0(@babel/core@7.28.4)': + dependencies: + '@babel/core': 7.28.4 + '@babel/helper-plugin-utils': 7.27.1 + '@babel/plugin-transform-destructuring': 7.28.0(@babel/core@7.28.4) + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-exponentiation-operator@7.27.1(@babel/core@7.28.4)': + dependencies: + '@babel/core': 7.28.4 + '@babel/helper-plugin-utils': 7.27.1 + + '@babel/plugin-transform-export-namespace-from@7.27.1(@babel/core@7.28.4)': + dependencies: + '@babel/core': 7.28.4 + '@babel/helper-plugin-utils': 7.27.1 + + '@babel/plugin-transform-flow-strip-types@7.27.1(@babel/core@7.28.4)': + dependencies: + '@babel/core': 7.28.4 + '@babel/helper-plugin-utils': 7.27.1 + '@babel/plugin-syntax-flow': 7.27.1(@babel/core@7.28.4) + + '@babel/plugin-transform-for-of@7.27.1(@babel/core@7.28.4)': + dependencies: + '@babel/core': 7.28.4 + '@babel/helper-plugin-utils': 7.27.1 + '@babel/helper-skip-transparent-expression-wrappers': 7.27.1 + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-function-name@7.27.1(@babel/core@7.28.4)': + dependencies: + '@babel/core': 7.28.4 + '@babel/helper-compilation-targets': 7.27.2 + '@babel/helper-plugin-utils': 7.27.1 + '@babel/traverse': 7.28.4 + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-json-strings@7.27.1(@babel/core@7.28.4)': + dependencies: + '@babel/core': 7.28.4 + '@babel/helper-plugin-utils': 7.27.1 + + '@babel/plugin-transform-literals@7.27.1(@babel/core@7.28.4)': + dependencies: + '@babel/core': 7.28.4 + '@babel/helper-plugin-utils': 7.27.1 + + '@babel/plugin-transform-logical-assignment-operators@7.27.1(@babel/core@7.28.4)': + dependencies: + '@babel/core': 7.28.4 + '@babel/helper-plugin-utils': 7.27.1 + + '@babel/plugin-transform-member-expression-literals@7.27.1(@babel/core@7.28.4)': + dependencies: + '@babel/core': 7.28.4 + '@babel/helper-plugin-utils': 7.27.1 + + '@babel/plugin-transform-modules-amd@7.27.1(@babel/core@7.28.4)': + dependencies: + '@babel/core': 7.28.4 + '@babel/helper-module-transforms': 7.28.3(@babel/core@7.28.4) + '@babel/helper-plugin-utils': 7.27.1 + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-modules-commonjs@7.27.1(@babel/core@7.28.4)': + dependencies: + '@babel/core': 7.28.4 + '@babel/helper-module-transforms': 7.28.3(@babel/core@7.28.4) + '@babel/helper-plugin-utils': 7.27.1 + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-modules-systemjs@7.27.1(@babel/core@7.28.4)': + dependencies: + '@babel/core': 7.28.4 + '@babel/helper-module-transforms': 7.28.3(@babel/core@7.28.4) + '@babel/helper-plugin-utils': 7.27.1 + '@babel/helper-validator-identifier': 7.27.1 + '@babel/traverse': 7.28.4 + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-modules-umd@7.27.1(@babel/core@7.28.4)': + dependencies: + '@babel/core': 7.28.4 + '@babel/helper-module-transforms': 7.28.3(@babel/core@7.28.4) + '@babel/helper-plugin-utils': 7.27.1 + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-named-capturing-groups-regex@7.27.1(@babel/core@7.28.4)': + dependencies: + '@babel/core': 7.28.4 + '@babel/helper-create-regexp-features-plugin': 7.27.1(@babel/core@7.28.4) + '@babel/helper-plugin-utils': 7.27.1 + + '@babel/plugin-transform-new-target@7.27.1(@babel/core@7.28.4)': + dependencies: + '@babel/core': 7.28.4 + '@babel/helper-plugin-utils': 7.27.1 + + '@babel/plugin-transform-nullish-coalescing-operator@7.27.1(@babel/core@7.28.4)': + dependencies: + '@babel/core': 7.28.4 + '@babel/helper-plugin-utils': 7.27.1 + + '@babel/plugin-transform-numeric-separator@7.27.1(@babel/core@7.28.4)': + dependencies: + '@babel/core': 7.28.4 + '@babel/helper-plugin-utils': 7.27.1 + + '@babel/plugin-transform-object-rest-spread@7.28.4(@babel/core@7.28.4)': + dependencies: + '@babel/core': 7.28.4 + '@babel/helper-compilation-targets': 7.27.2 + '@babel/helper-plugin-utils': 7.27.1 + '@babel/plugin-transform-destructuring': 7.28.0(@babel/core@7.28.4) + '@babel/plugin-transform-parameters': 7.27.7(@babel/core@7.28.4) + '@babel/traverse': 7.28.4 + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-object-super@7.27.1(@babel/core@7.28.4)': + dependencies: + '@babel/core': 7.28.4 + '@babel/helper-plugin-utils': 7.27.1 + '@babel/helper-replace-supers': 7.27.1(@babel/core@7.28.4) + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-optional-catch-binding@7.27.1(@babel/core@7.28.4)': + dependencies: + '@babel/core': 7.28.4 + '@babel/helper-plugin-utils': 7.27.1 + + '@babel/plugin-transform-optional-chaining@7.27.1(@babel/core@7.28.4)': + dependencies: + '@babel/core': 7.28.4 + '@babel/helper-plugin-utils': 7.27.1 + '@babel/helper-skip-transparent-expression-wrappers': 7.27.1 + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-parameters@7.27.7(@babel/core@7.28.4)': + dependencies: + '@babel/core': 7.28.4 + '@babel/helper-plugin-utils': 7.27.1 + + '@babel/plugin-transform-private-methods@7.27.1(@babel/core@7.28.4)': + dependencies: + '@babel/core': 7.28.4 + '@babel/helper-create-class-features-plugin': 7.28.3(@babel/core@7.28.4) + '@babel/helper-plugin-utils': 7.27.1 + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-private-property-in-object@7.27.1(@babel/core@7.28.4)': + dependencies: + '@babel/core': 7.28.4 + '@babel/helper-annotate-as-pure': 7.27.3 + '@babel/helper-create-class-features-plugin': 7.28.3(@babel/core@7.28.4) + '@babel/helper-plugin-utils': 7.27.1 + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-property-literals@7.27.1(@babel/core@7.28.4)': + dependencies: + '@babel/core': 7.28.4 + '@babel/helper-plugin-utils': 7.27.1 + + '@babel/plugin-transform-react-constant-elements@7.27.1(@babel/core@7.28.4)': + dependencies: + '@babel/core': 7.28.4 + '@babel/helper-plugin-utils': 7.27.1 + + '@babel/plugin-transform-react-display-name@7.28.0(@babel/core@7.28.4)': + dependencies: + '@babel/core': 7.28.4 + '@babel/helper-plugin-utils': 7.27.1 + + '@babel/plugin-transform-react-jsx-development@7.27.1(@babel/core@7.28.4)': + dependencies: + '@babel/core': 7.28.4 + '@babel/plugin-transform-react-jsx': 7.27.1(@babel/core@7.28.4) + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-react-jsx-self@7.27.1(@babel/core@7.28.4)': + dependencies: + '@babel/core': 7.28.4 + '@babel/helper-plugin-utils': 7.27.1 + + '@babel/plugin-transform-react-jsx-source@7.27.1(@babel/core@7.28.4)': + dependencies: + '@babel/core': 7.28.4 + '@babel/helper-plugin-utils': 7.27.1 + + '@babel/plugin-transform-react-jsx@7.27.1(@babel/core@7.28.4)': + dependencies: + '@babel/core': 7.28.4 + '@babel/helper-annotate-as-pure': 7.27.3 + '@babel/helper-module-imports': 7.27.1 + '@babel/helper-plugin-utils': 7.27.1 + '@babel/plugin-syntax-jsx': 7.27.1(@babel/core@7.28.4) + '@babel/types': 7.28.4 + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-react-pure-annotations@7.27.1(@babel/core@7.28.4)': + dependencies: + '@babel/core': 7.28.4 + '@babel/helper-annotate-as-pure': 7.27.3 + '@babel/helper-plugin-utils': 7.27.1 + + '@babel/plugin-transform-regenerator@7.28.4(@babel/core@7.28.4)': + dependencies: + '@babel/core': 7.28.4 + '@babel/helper-plugin-utils': 7.27.1 + + '@babel/plugin-transform-regexp-modifiers@7.27.1(@babel/core@7.28.4)': + dependencies: + '@babel/core': 7.28.4 + '@babel/helper-create-regexp-features-plugin': 7.27.1(@babel/core@7.28.4) + '@babel/helper-plugin-utils': 7.27.1 + + '@babel/plugin-transform-reserved-words@7.27.1(@babel/core@7.28.4)': + dependencies: + '@babel/core': 7.28.4 + '@babel/helper-plugin-utils': 7.27.1 + + '@babel/plugin-transform-runtime@7.28.3(@babel/core@7.28.4)': + dependencies: + '@babel/core': 7.28.4 + '@babel/helper-module-imports': 7.27.1 + '@babel/helper-plugin-utils': 7.27.1 + babel-plugin-polyfill-corejs2: 0.4.14(@babel/core@7.28.4) + babel-plugin-polyfill-corejs3: 0.13.0(@babel/core@7.28.4) + babel-plugin-polyfill-regenerator: 0.6.5(@babel/core@7.28.4) + semver: 7.7.1 + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-shorthand-properties@7.27.1(@babel/core@7.28.4)': + dependencies: + '@babel/core': 7.28.4 + '@babel/helper-plugin-utils': 7.27.1 + + '@babel/plugin-transform-spread@7.27.1(@babel/core@7.28.4)': + dependencies: + '@babel/core': 7.28.4 + '@babel/helper-plugin-utils': 7.27.1 + '@babel/helper-skip-transparent-expression-wrappers': 7.27.1 + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-sticky-regex@7.27.1(@babel/core@7.28.4)': + dependencies: + '@babel/core': 7.28.4 + '@babel/helper-plugin-utils': 7.27.1 + + '@babel/plugin-transform-template-literals@7.27.1(@babel/core@7.28.4)': + dependencies: + '@babel/core': 7.28.4 + '@babel/helper-plugin-utils': 7.27.1 + + '@babel/plugin-transform-typeof-symbol@7.27.1(@babel/core@7.28.4)': + dependencies: + '@babel/core': 7.28.4 + '@babel/helper-plugin-utils': 7.27.1 + + '@babel/plugin-transform-typescript@7.28.0(@babel/core@7.28.4)': + dependencies: + '@babel/core': 7.28.4 + '@babel/helper-annotate-as-pure': 7.27.3 + '@babel/helper-create-class-features-plugin': 7.28.3(@babel/core@7.28.4) + '@babel/helper-plugin-utils': 7.27.1 + '@babel/helper-skip-transparent-expression-wrappers': 7.27.1 + '@babel/plugin-syntax-typescript': 7.27.1(@babel/core@7.28.4) + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-unicode-escapes@7.27.1(@babel/core@7.28.4)': + dependencies: + '@babel/core': 7.28.4 + '@babel/helper-plugin-utils': 7.27.1 + + '@babel/plugin-transform-unicode-property-regex@7.27.1(@babel/core@7.28.4)': + dependencies: + '@babel/core': 7.28.4 + '@babel/helper-create-regexp-features-plugin': 7.27.1(@babel/core@7.28.4) + '@babel/helper-plugin-utils': 7.27.1 + + '@babel/plugin-transform-unicode-regex@7.27.1(@babel/core@7.28.4)': + dependencies: + '@babel/core': 7.28.4 + '@babel/helper-create-regexp-features-plugin': 7.27.1(@babel/core@7.28.4) + '@babel/helper-plugin-utils': 7.27.1 + + '@babel/plugin-transform-unicode-sets-regex@7.27.1(@babel/core@7.28.4)': + dependencies: + '@babel/core': 7.28.4 + '@babel/helper-create-regexp-features-plugin': 7.27.1(@babel/core@7.28.4) + '@babel/helper-plugin-utils': 7.27.1 + + '@babel/preset-env@7.28.3(@babel/core@7.28.4)': + dependencies: + '@babel/compat-data': 7.28.4 + '@babel/core': 7.28.4 + '@babel/helper-compilation-targets': 7.27.2 + '@babel/helper-plugin-utils': 7.27.1 + '@babel/helper-validator-option': 7.27.1 + '@babel/plugin-bugfix-firefox-class-in-computed-class-key': 7.27.1(@babel/core@7.28.4) + '@babel/plugin-bugfix-safari-class-field-initializer-scope': 7.27.1(@babel/core@7.28.4) + '@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression': 7.27.1(@babel/core@7.28.4) + '@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining': 7.27.1(@babel/core@7.28.4) + '@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly': 7.28.3(@babel/core@7.28.4) + '@babel/plugin-proposal-private-property-in-object': 7.21.0-placeholder-for-preset-env.2(@babel/core@7.28.4) + '@babel/plugin-syntax-import-assertions': 7.27.1(@babel/core@7.28.4) + '@babel/plugin-syntax-import-attributes': 7.27.1(@babel/core@7.28.4) + '@babel/plugin-syntax-unicode-sets-regex': 7.18.6(@babel/core@7.28.4) + '@babel/plugin-transform-arrow-functions': 7.27.1(@babel/core@7.28.4) + '@babel/plugin-transform-async-generator-functions': 7.28.0(@babel/core@7.28.4) + '@babel/plugin-transform-async-to-generator': 7.27.1(@babel/core@7.28.4) + '@babel/plugin-transform-block-scoped-functions': 7.27.1(@babel/core@7.28.4) + '@babel/plugin-transform-block-scoping': 7.28.4(@babel/core@7.28.4) + '@babel/plugin-transform-class-properties': 7.27.1(@babel/core@7.28.4) + '@babel/plugin-transform-class-static-block': 7.28.3(@babel/core@7.28.4) + '@babel/plugin-transform-classes': 7.28.4(@babel/core@7.28.4) + '@babel/plugin-transform-computed-properties': 7.27.1(@babel/core@7.28.4) + '@babel/plugin-transform-destructuring': 7.28.0(@babel/core@7.28.4) + '@babel/plugin-transform-dotall-regex': 7.27.1(@babel/core@7.28.4) + '@babel/plugin-transform-duplicate-keys': 7.27.1(@babel/core@7.28.4) + '@babel/plugin-transform-duplicate-named-capturing-groups-regex': 7.27.1(@babel/core@7.28.4) + '@babel/plugin-transform-dynamic-import': 7.27.1(@babel/core@7.28.4) + '@babel/plugin-transform-explicit-resource-management': 7.28.0(@babel/core@7.28.4) + '@babel/plugin-transform-exponentiation-operator': 7.27.1(@babel/core@7.28.4) + '@babel/plugin-transform-export-namespace-from': 7.27.1(@babel/core@7.28.4) + '@babel/plugin-transform-for-of': 7.27.1(@babel/core@7.28.4) + '@babel/plugin-transform-function-name': 7.27.1(@babel/core@7.28.4) + '@babel/plugin-transform-json-strings': 7.27.1(@babel/core@7.28.4) + '@babel/plugin-transform-literals': 7.27.1(@babel/core@7.28.4) + '@babel/plugin-transform-logical-assignment-operators': 7.27.1(@babel/core@7.28.4) + '@babel/plugin-transform-member-expression-literals': 7.27.1(@babel/core@7.28.4) + '@babel/plugin-transform-modules-amd': 7.27.1(@babel/core@7.28.4) + '@babel/plugin-transform-modules-commonjs': 7.27.1(@babel/core@7.28.4) + '@babel/plugin-transform-modules-systemjs': 7.27.1(@babel/core@7.28.4) + '@babel/plugin-transform-modules-umd': 7.27.1(@babel/core@7.28.4) + '@babel/plugin-transform-named-capturing-groups-regex': 7.27.1(@babel/core@7.28.4) + '@babel/plugin-transform-new-target': 7.27.1(@babel/core@7.28.4) + '@babel/plugin-transform-nullish-coalescing-operator': 7.27.1(@babel/core@7.28.4) + '@babel/plugin-transform-numeric-separator': 7.27.1(@babel/core@7.28.4) + '@babel/plugin-transform-object-rest-spread': 7.28.4(@babel/core@7.28.4) + '@babel/plugin-transform-object-super': 7.27.1(@babel/core@7.28.4) + '@babel/plugin-transform-optional-catch-binding': 7.27.1(@babel/core@7.28.4) + '@babel/plugin-transform-optional-chaining': 7.27.1(@babel/core@7.28.4) + '@babel/plugin-transform-parameters': 7.27.7(@babel/core@7.28.4) + '@babel/plugin-transform-private-methods': 7.27.1(@babel/core@7.28.4) + '@babel/plugin-transform-private-property-in-object': 7.27.1(@babel/core@7.28.4) + '@babel/plugin-transform-property-literals': 7.27.1(@babel/core@7.28.4) + '@babel/plugin-transform-regenerator': 7.28.4(@babel/core@7.28.4) + '@babel/plugin-transform-regexp-modifiers': 7.27.1(@babel/core@7.28.4) + '@babel/plugin-transform-reserved-words': 7.27.1(@babel/core@7.28.4) + '@babel/plugin-transform-shorthand-properties': 7.27.1(@babel/core@7.28.4) + '@babel/plugin-transform-spread': 7.27.1(@babel/core@7.28.4) + '@babel/plugin-transform-sticky-regex': 7.27.1(@babel/core@7.28.4) + '@babel/plugin-transform-template-literals': 7.27.1(@babel/core@7.28.4) + '@babel/plugin-transform-typeof-symbol': 7.27.1(@babel/core@7.28.4) + '@babel/plugin-transform-unicode-escapes': 7.27.1(@babel/core@7.28.4) + '@babel/plugin-transform-unicode-property-regex': 7.27.1(@babel/core@7.28.4) + '@babel/plugin-transform-unicode-regex': 7.27.1(@babel/core@7.28.4) + '@babel/plugin-transform-unicode-sets-regex': 7.27.1(@babel/core@7.28.4) + '@babel/preset-modules': 0.1.6-no-external-plugins(@babel/core@7.28.4) + babel-plugin-polyfill-corejs2: 0.4.14(@babel/core@7.28.4) + babel-plugin-polyfill-corejs3: 0.13.0(@babel/core@7.28.4) + babel-plugin-polyfill-regenerator: 0.6.5(@babel/core@7.28.4) + core-js-compat: 3.46.0 + semver: 7.7.1 + transitivePeerDependencies: + - supports-color + + '@babel/preset-modules@0.1.6-no-external-plugins(@babel/core@7.28.4)': + dependencies: + '@babel/core': 7.28.4 + '@babel/helper-plugin-utils': 7.27.1 + '@babel/types': 7.28.4 + esutils: 2.0.3 + + '@babel/preset-react@7.27.1(@babel/core@7.28.4)': + dependencies: + '@babel/core': 7.28.4 + '@babel/helper-plugin-utils': 7.27.1 + '@babel/helper-validator-option': 7.27.1 + '@babel/plugin-transform-react-display-name': 7.28.0(@babel/core@7.28.4) + '@babel/plugin-transform-react-jsx': 7.27.1(@babel/core@7.28.4) + '@babel/plugin-transform-react-jsx-development': 7.27.1(@babel/core@7.28.4) + '@babel/plugin-transform-react-pure-annotations': 7.27.1(@babel/core@7.28.4) + transitivePeerDependencies: + - supports-color + + '@babel/preset-typescript@7.18.6(@babel/core@7.28.4)': + dependencies: + '@babel/core': 7.28.4 + '@babel/helper-plugin-utils': 7.27.1 + '@babel/helper-validator-option': 7.27.1 + '@babel/plugin-transform-typescript': 7.28.0(@babel/core@7.28.4) + transitivePeerDependencies: + - supports-color + + '@babel/runtime@7.28.4': {} + + '@babel/template@7.27.2': + dependencies: + '@babel/code-frame': 7.27.1 + '@babel/parser': 7.28.4 + '@babel/types': 7.28.4 + + '@babel/traverse@7.28.4': + dependencies: + '@babel/code-frame': 7.27.1 + '@babel/generator': 7.28.3 + '@babel/helper-globals': 7.28.0 + '@babel/parser': 7.28.4 + '@babel/template': 7.27.2 + '@babel/types': 7.28.4 + debug: 4.4.3(supports-color@8.1.1) + transitivePeerDependencies: + - supports-color + + '@babel/types@7.28.4': + dependencies: + '@babel/helper-string-parser': 7.27.1 + '@babel/helper-validator-identifier': 7.27.1 + + '@bcoe/v8-coverage@0.2.3': {} + + '@codemirror/autocomplete@6.19.0': + dependencies: + '@codemirror/language': 6.11.3 + '@codemirror/state': 6.5.2 + '@codemirror/view': 6.38.5 + '@lezer/common': 1.2.3 + + '@codemirror/commands@6.9.0': + dependencies: + '@codemirror/language': 6.11.3 + '@codemirror/state': 6.5.2 + '@codemirror/view': 6.38.5 + '@lezer/common': 1.2.3 + + '@codemirror/lang-css@6.3.1': + dependencies: + '@codemirror/autocomplete': 6.19.0 + '@codemirror/language': 6.11.3 + '@codemirror/state': 6.5.2 + '@lezer/common': 1.2.3 + '@lezer/css': 1.3.3 + + '@codemirror/lang-html@6.4.11': + dependencies: + '@codemirror/autocomplete': 6.19.0 + '@codemirror/lang-css': 6.3.1 + '@codemirror/lang-javascript': 6.2.4 + '@codemirror/language': 6.11.3 + '@codemirror/state': 6.5.2 + '@codemirror/view': 6.38.5 + '@lezer/common': 1.2.3 + '@lezer/css': 1.3.3 + '@lezer/html': 1.3.13 + + '@codemirror/lang-javascript@6.2.4': + dependencies: + '@codemirror/autocomplete': 6.19.0 + '@codemirror/language': 6.11.3 + '@codemirror/lint': 6.9.0 + '@codemirror/state': 6.5.2 + '@codemirror/view': 6.38.5 + '@lezer/common': 1.2.3 + '@lezer/javascript': 1.5.4 + + '@codemirror/lang-json@6.0.2': + dependencies: + '@codemirror/language': 6.11.3 + '@lezer/json': 1.0.3 + + '@codemirror/lang-markdown@6.5.0': + dependencies: + '@codemirror/autocomplete': 6.19.0 + '@codemirror/lang-html': 6.4.11 + '@codemirror/language': 6.11.3 + '@codemirror/state': 6.5.2 + '@codemirror/view': 6.38.5 + '@lezer/common': 1.2.3 + '@lezer/markdown': 1.6.3 + + '@codemirror/language@6.11.3': + dependencies: + '@codemirror/state': 6.5.2 + '@codemirror/view': 6.38.5 + '@lezer/common': 1.2.3 + '@lezer/highlight': 1.2.1 + '@lezer/lr': 1.4.2 + style-mod: 4.1.2 + + '@codemirror/lint@6.9.0': + dependencies: + '@codemirror/state': 6.5.2 + '@codemirror/view': 6.38.5 + crelt: 1.0.6 + + '@codemirror/search@6.5.11': + dependencies: + '@codemirror/state': 6.5.2 + '@codemirror/view': 6.38.5 + crelt: 1.0.6 + + '@codemirror/state@6.5.2': + dependencies: + '@marijn/find-cluster-break': 1.0.2 + + '@codemirror/theme-one-dark@6.1.3': + dependencies: + '@codemirror/language': 6.11.3 + '@codemirror/state': 6.5.2 + '@codemirror/view': 6.38.5 + '@lezer/highlight': 1.2.1 + + '@codemirror/view@6.38.5': + dependencies: + '@codemirror/state': 6.5.2 + crelt: 1.0.6 + style-mod: 4.1.2 + w3c-keyname: 2.2.8 + + '@colors/colors@1.5.0': + optional: true + + '@colors/colors@1.6.0': {} + + '@cspotcode/source-map-support@0.8.1': + dependencies: + '@jridgewell/trace-mapping': 0.3.9 + + '@csstools/normalize.css@12.1.1': {} + + '@csstools/postcss-cascade-layers@1.1.1(postcss@8.5.6)': + dependencies: + '@csstools/selector-specificity': 2.2.0(postcss-selector-parser@6.1.2) + postcss: 8.5.6 + postcss-selector-parser: 6.1.2 + + '@csstools/postcss-color-function@1.1.1(postcss@8.5.6)': + dependencies: + '@csstools/postcss-progressive-custom-properties': 1.3.0(postcss@8.5.6) + postcss: 8.5.6 + postcss-value-parser: 4.2.0 + + '@csstools/postcss-font-format-keywords@1.0.1(postcss@8.5.6)': + dependencies: + postcss: 8.5.6 + postcss-value-parser: 4.2.0 + + '@csstools/postcss-hwb-function@1.0.2(postcss@8.5.6)': + dependencies: + postcss: 8.5.6 + postcss-value-parser: 4.2.0 + + '@csstools/postcss-ic-unit@1.0.1(postcss@8.5.6)': + dependencies: + '@csstools/postcss-progressive-custom-properties': 1.3.0(postcss@8.5.6) + postcss: 8.5.6 + postcss-value-parser: 4.2.0 + + '@csstools/postcss-is-pseudo-class@2.0.7(postcss@8.5.6)': + dependencies: + '@csstools/selector-specificity': 2.2.0(postcss-selector-parser@6.1.2) + postcss: 8.5.6 + postcss-selector-parser: 6.1.2 + + '@csstools/postcss-nested-calc@1.0.0(postcss@8.5.6)': + dependencies: + postcss: 8.5.6 + postcss-value-parser: 4.2.0 + + '@csstools/postcss-normalize-display-values@1.0.1(postcss@8.5.6)': + dependencies: + postcss: 8.5.6 + postcss-value-parser: 4.2.0 + + '@csstools/postcss-oklab-function@1.1.1(postcss@8.5.6)': + dependencies: + '@csstools/postcss-progressive-custom-properties': 1.3.0(postcss@8.5.6) + postcss: 8.5.6 + postcss-value-parser: 4.2.0 + + '@csstools/postcss-progressive-custom-properties@1.3.0(postcss@8.5.6)': + dependencies: + postcss: 8.5.6 + postcss-value-parser: 4.2.0 + + '@csstools/postcss-stepped-value-functions@1.0.1(postcss@8.5.6)': + dependencies: + postcss: 8.5.6 + postcss-value-parser: 4.2.0 + + '@csstools/postcss-text-decoration-shorthand@1.0.0(postcss@8.5.6)': + dependencies: + postcss: 8.5.6 + postcss-value-parser: 4.2.0 + + '@csstools/postcss-trigonometric-functions@1.0.2(postcss@8.5.6)': + dependencies: + postcss: 8.5.6 + postcss-value-parser: 4.2.0 + + '@csstools/postcss-unset-value@1.0.2(postcss@8.5.6)': + dependencies: + postcss: 8.5.6 + + '@csstools/selector-specificity@2.2.0(postcss-selector-parser@6.1.2)': + dependencies: + postcss-selector-parser: 6.1.2 + + '@cypress/request@3.0.9': + dependencies: + aws-sign2: 0.7.0 + aws4: 1.13.2 + caseless: 0.12.0 + combined-stream: 1.0.8 + extend: 3.0.2 + forever-agent: 0.6.1 + form-data: 4.0.4 + http-signature: 1.4.0 + is-typedarray: 1.0.0 + isstream: 0.1.2 + json-stringify-safe: 5.0.1 + mime-types: 2.1.35 + performance-now: 2.1.0 + qs: 6.14.0 + safe-buffer: 5.2.1 + tough-cookie: 5.1.2 + tunnel-agent: 0.6.0 + uuid: 8.3.2 + + '@cypress/xvfb@1.2.4(supports-color@8.1.1)': + dependencies: + debug: 3.2.7(supports-color@8.1.1) + lodash.once: 4.1.1 + transitivePeerDependencies: + - supports-color + + '@dabh/diagnostics@2.0.8': + dependencies: + '@so-ric/colorspace': 1.1.6 + enabled: 2.0.0 + kuler: 2.0.0 + + '@emotion/babel-plugin@11.13.5': + dependencies: + '@babel/helper-module-imports': 7.27.1 + '@babel/runtime': 7.28.4 + '@emotion/hash': 0.9.2 + '@emotion/memoize': 0.9.0 + '@emotion/serialize': 1.3.3 + babel-plugin-macros: 3.1.0 + convert-source-map: 1.9.0 + escape-string-regexp: 4.0.0 + find-root: 1.1.0 + source-map: 0.5.7 + stylis: 4.2.0 + transitivePeerDependencies: + - supports-color + + '@emotion/cache@11.14.0': + dependencies: + '@emotion/memoize': 0.9.0 + '@emotion/sheet': 1.4.0 + '@emotion/utils': 1.4.2 + '@emotion/weak-memoize': 0.4.0 + stylis: 4.2.0 + + '@emotion/hash@0.9.2': {} + + '@emotion/is-prop-valid@0.8.8': + dependencies: + '@emotion/memoize': 0.7.4 + optional: true + + '@emotion/is-prop-valid@1.4.0': + dependencies: + '@emotion/memoize': 0.9.0 + + '@emotion/memoize@0.7.4': + optional: true + + '@emotion/memoize@0.9.0': {} + + '@emotion/react@11.14.0(@types/react@18.3.26)(react@18.3.1)': + dependencies: + '@babel/runtime': 7.28.4 + '@emotion/babel-plugin': 11.13.5 + '@emotion/cache': 11.14.0 + '@emotion/serialize': 1.3.3 + '@emotion/use-insertion-effect-with-fallbacks': 1.2.0(react@18.3.1) + '@emotion/utils': 1.4.2 + '@emotion/weak-memoize': 0.4.0 + hoist-non-react-statics: 3.3.2 + react: 18.3.1 + optionalDependencies: + '@types/react': 18.3.26 + transitivePeerDependencies: + - supports-color + + '@emotion/serialize@1.3.3': + dependencies: + '@emotion/hash': 0.9.2 + '@emotion/memoize': 0.9.0 + '@emotion/unitless': 0.10.0 + '@emotion/utils': 1.4.2 + csstype: 3.1.3 + + '@emotion/sheet@1.4.0': {} + + '@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@18.3.26)(react@18.3.1))(@types/react@18.3.26)(react@18.3.1)': + dependencies: + '@babel/runtime': 7.28.4 + '@emotion/babel-plugin': 11.13.5 + '@emotion/is-prop-valid': 1.4.0 + '@emotion/react': 11.14.0(@types/react@18.3.26)(react@18.3.1) + '@emotion/serialize': 1.3.3 + '@emotion/use-insertion-effect-with-fallbacks': 1.2.0(react@18.3.1) + '@emotion/utils': 1.4.2 + react: 18.3.1 + optionalDependencies: + '@types/react': 18.3.26 + transitivePeerDependencies: + - supports-color + + '@emotion/unitless@0.10.0': {} + + '@emotion/use-insertion-effect-with-fallbacks@1.2.0(react@18.3.1)': + dependencies: + react: 18.3.1 + + '@emotion/utils@1.4.2': {} + + '@emotion/weak-memoize@0.4.0': {} + + '@esbuild/aix-ppc64@0.27.1': + optional: true + + '@esbuild/android-arm64@0.27.1': + optional: true + + '@esbuild/android-arm@0.27.1': + optional: true + + '@esbuild/android-x64@0.27.1': + optional: true + + '@esbuild/darwin-arm64@0.27.1': + optional: true + + '@esbuild/darwin-x64@0.27.1': + optional: true + + '@esbuild/freebsd-arm64@0.27.1': + optional: true + + '@esbuild/freebsd-x64@0.27.1': + optional: true + + '@esbuild/linux-arm64@0.27.1': + optional: true + + '@esbuild/linux-arm@0.27.1': + optional: true + + '@esbuild/linux-ia32@0.27.1': + optional: true + + '@esbuild/linux-loong64@0.27.1': + optional: true + + '@esbuild/linux-mips64el@0.27.1': + optional: true + + '@esbuild/linux-ppc64@0.27.1': + optional: true + + '@esbuild/linux-riscv64@0.27.1': + optional: true + + '@esbuild/linux-s390x@0.27.1': + optional: true + + '@esbuild/linux-x64@0.27.1': + optional: true + + '@esbuild/netbsd-arm64@0.27.1': + optional: true + + '@esbuild/netbsd-x64@0.27.1': + optional: true + + '@esbuild/openbsd-arm64@0.27.1': + optional: true + + '@esbuild/openbsd-x64@0.27.1': + optional: true + + '@esbuild/openharmony-arm64@0.27.1': + optional: true + + '@esbuild/sunos-x64@0.27.1': + optional: true + + '@esbuild/win32-arm64@0.27.1': + optional: true + + '@esbuild/win32-ia32@0.27.1': + optional: true + + '@esbuild/win32-x64@0.27.1': + optional: true + + '@eslint-community/eslint-utils@4.9.0(eslint@8.57.1)': + dependencies: + eslint: 8.57.1 + eslint-visitor-keys: 3.4.3 + + '@eslint-community/regexpp@4.12.1': {} + + '@eslint/eslintrc@2.1.4': + dependencies: + ajv: 6.12.6 + debug: 4.4.3(supports-color@8.1.1) + espree: 9.6.1 + globals: 13.24.0 + ignore: 5.3.2 + import-fresh: 3.3.1 + js-yaml: 4.1.0 + minimatch: 3.1.2 + strip-json-comments: 3.1.1 + transitivePeerDependencies: + - supports-color + + '@eslint/js@8.57.1': {} + + '@floating-ui/core@1.7.3': + dependencies: + '@floating-ui/utils': 0.2.10 + + '@floating-ui/dom@1.7.4': + dependencies: + '@floating-ui/core': 1.7.3 + '@floating-ui/utils': 0.2.10 + + '@floating-ui/react-dom@2.1.6(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + dependencies: + '@floating-ui/dom': 1.7.4 + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + + '@floating-ui/utils@0.2.10': {} + + '@gar/promisify@1.1.3': + optional: true + + '@grpc/grpc-js@1.14.0': + dependencies: + '@grpc/proto-loader': 0.8.0 + '@js-sdsl/ordered-map': 4.4.2 + + '@grpc/proto-loader@0.8.0': + dependencies: + lodash.camelcase: 4.3.0 + long: 5.3.2 + protobufjs: 7.4.0 + yargs: 17.7.2 + + '@hapi/address@5.1.1': + dependencies: + '@hapi/hoek': 11.0.7 + + '@hapi/formula@3.0.2': {} + + '@hapi/hoek@11.0.7': {} + + '@hapi/pinpoint@2.0.1': {} + + '@hapi/tlds@1.1.3': {} + + '@hapi/topo@6.0.2': + dependencies: + '@hapi/hoek': 11.0.7 + + '@humanwhocodes/config-array@0.13.0': + dependencies: + '@humanwhocodes/object-schema': 2.0.3 + debug: 4.4.3(supports-color@8.1.1) + minimatch: 3.1.2 + transitivePeerDependencies: + - supports-color + + '@humanwhocodes/module-importer@1.0.1': {} + + '@humanwhocodes/object-schema@2.0.3': {} + + '@icons/material@0.2.4(react@18.3.1)': + dependencies: + react: 18.3.1 + + '@ioredis/commands@1.4.0': + optional: true + + '@isaacs/cliui@8.0.2': + dependencies: + string-width: 5.1.2 + string-width-cjs: string-width@4.2.3 + strip-ansi: 7.1.2 + strip-ansi-cjs: strip-ansi@6.0.1 + wrap-ansi: 8.1.0 + wrap-ansi-cjs: wrap-ansi@7.0.0 + + '@istanbuljs/load-nyc-config@1.1.0': + dependencies: + camelcase: 5.3.1 + find-up: 4.1.0 + get-package-type: 0.1.0 + js-yaml: 3.14.1 + resolve-from: 5.0.0 + + '@istanbuljs/schema@0.1.3': {} + + '@jest/console@27.5.1': + dependencies: + '@jest/types': 27.5.1 + '@types/node': 24.7.2 + chalk: 4.1.2 + jest-message-util: 27.5.1 + jest-util: 27.5.1 + slash: 3.0.0 + + '@jest/console@28.1.3': + dependencies: + '@jest/types': 28.1.3 + '@types/node': 24.7.2 + chalk: 4.1.2 + jest-message-util: 28.1.3 + jest-util: 28.1.3 + slash: 3.0.0 + + '@jest/core@27.5.1(bufferutil@4.0.9)(canvas@2.11.2(encoding@0.1.13))(ts-node@10.9.2(@types/node@24.7.2)(typescript@5.9.3))(utf-8-validate@6.0.5)': + dependencies: + '@jest/console': 27.5.1 + '@jest/reporters': 27.5.1 + '@jest/test-result': 27.5.1 + '@jest/transform': 27.5.1 + '@jest/types': 27.5.1 + '@types/node': 24.7.2 + ansi-escapes: 4.3.2 + chalk: 4.1.2 + emittery: 0.8.1 + exit: 0.1.2 + graceful-fs: 4.2.11 + jest-changed-files: 27.5.1 + jest-config: 27.5.1(bufferutil@4.0.9)(canvas@2.11.2(encoding@0.1.13))(ts-node@10.9.2(@types/node@24.7.2)(typescript@5.9.3))(utf-8-validate@6.0.5) + jest-haste-map: 27.5.1 + jest-message-util: 27.5.1 + jest-regex-util: 27.5.1 + jest-resolve: 27.5.1 + jest-resolve-dependencies: 27.5.1 + jest-runner: 27.5.1(bufferutil@4.0.9)(canvas@2.11.2(encoding@0.1.13))(utf-8-validate@6.0.5) + jest-runtime: 27.5.1 + jest-snapshot: 27.5.1 + jest-util: 27.5.1 + jest-validate: 27.5.1 + jest-watcher: 27.5.1 + micromatch: 4.0.8 + rimraf: 3.0.2 + slash: 3.0.0 + strip-ansi: 6.0.1 + transitivePeerDependencies: + - bufferutil + - canvas + - supports-color + - ts-node + - utf-8-validate + + '@jest/diff-sequences@30.0.1': {} + + '@jest/environment@27.5.1': + dependencies: + '@jest/fake-timers': 27.5.1 + '@jest/types': 27.5.1 + '@types/node': 24.7.2 + jest-mock: 27.5.1 + + '@jest/expect-utils@30.2.0': + dependencies: + '@jest/get-type': 30.1.0 + + '@jest/fake-timers@27.5.1': + dependencies: + '@jest/types': 27.5.1 + '@sinonjs/fake-timers': 8.1.0 + '@types/node': 24.7.2 + jest-message-util: 27.5.1 + jest-mock: 27.5.1 + jest-util: 27.5.1 + + '@jest/get-type@30.1.0': {} + + '@jest/globals@27.5.1': + dependencies: + '@jest/environment': 27.5.1 + '@jest/types': 27.5.1 + expect: 27.5.1 + + '@jest/pattern@30.0.1': + dependencies: + '@types/node': 24.7.2 + jest-regex-util: 30.0.1 + + '@jest/reporters@27.5.1': + dependencies: + '@bcoe/v8-coverage': 0.2.3 + '@jest/console': 27.5.1 + '@jest/test-result': 27.5.1 + '@jest/transform': 27.5.1 + '@jest/types': 27.5.1 + '@types/node': 24.7.2 + chalk: 4.1.2 + collect-v8-coverage: 1.0.2 + exit: 0.1.2 + glob: 10.5.0 + graceful-fs: 4.2.11 + istanbul-lib-coverage: 3.2.2 + istanbul-lib-instrument: 5.2.1 + istanbul-lib-report: 3.0.1 + istanbul-lib-source-maps: 4.0.1 + istanbul-reports: 3.2.0 + jest-haste-map: 27.5.1 + jest-resolve: 27.5.1 + jest-util: 27.5.1 + jest-worker: 27.5.1 + slash: 3.0.0 + source-map: 0.6.1 + string-length: 4.0.2 + terminal-link: 2.1.1 + v8-to-istanbul: 8.1.1 + transitivePeerDependencies: + - supports-color + + '@jest/schemas@28.1.3': + dependencies: + '@sinclair/typebox': 0.24.51 + + '@jest/schemas@30.0.5': + dependencies: + '@sinclair/typebox': 0.34.41 + + '@jest/source-map@27.5.1': + dependencies: + callsites: 3.1.0 + graceful-fs: 4.2.11 + source-map: 0.6.1 + + '@jest/test-result@27.5.1': + dependencies: + '@jest/console': 27.5.1 + '@jest/types': 27.5.1 + '@types/istanbul-lib-coverage': 2.0.6 + collect-v8-coverage: 1.0.2 + + '@jest/test-result@28.1.3': + dependencies: + '@jest/console': 28.1.3 + '@jest/types': 28.1.3 + '@types/istanbul-lib-coverage': 2.0.6 + collect-v8-coverage: 1.0.2 + + '@jest/test-sequencer@27.5.1': + dependencies: + '@jest/test-result': 27.5.1 + graceful-fs: 4.2.11 + jest-haste-map: 27.5.1 + jest-runtime: 27.5.1 + transitivePeerDependencies: + - supports-color + + '@jest/transform@27.5.1': + dependencies: + '@babel/core': 7.28.4 + '@jest/types': 27.5.1 + babel-plugin-istanbul: 6.1.1 + chalk: 4.1.2 + convert-source-map: 1.9.0 + fast-json-stable-stringify: 2.1.0 + graceful-fs: 4.2.11 + jest-haste-map: 27.5.1 + jest-regex-util: 27.5.1 + jest-util: 27.5.1 + micromatch: 4.0.8 + pirates: 4.0.7 + slash: 3.0.0 + source-map: 0.6.1 + write-file-atomic: 3.0.3 + transitivePeerDependencies: + - supports-color + + '@jest/types@27.5.1': + dependencies: + '@types/istanbul-lib-coverage': 2.0.6 + '@types/istanbul-reports': 3.0.4 + '@types/node': 24.7.2 + '@types/yargs': 16.0.9 + chalk: 4.1.2 + + '@jest/types@28.1.3': + dependencies: + '@jest/schemas': 28.1.3 + '@types/istanbul-lib-coverage': 2.0.6 + '@types/istanbul-reports': 3.0.4 + '@types/node': 24.7.2 + '@types/yargs': 17.0.33 + chalk: 4.1.2 + + '@jest/types@30.2.0': + dependencies: + '@jest/pattern': 30.0.1 + '@jest/schemas': 30.0.5 + '@types/istanbul-lib-coverage': 2.0.6 + '@types/istanbul-reports': 3.0.4 + '@types/node': 24.7.2 + '@types/yargs': 17.0.33 + chalk: 4.1.2 + + '@jridgewell/gen-mapping@0.3.13': + dependencies: + '@jridgewell/sourcemap-codec': 1.5.5 + '@jridgewell/trace-mapping': 0.3.31 + + '@jridgewell/remapping@2.3.5': + dependencies: + '@jridgewell/gen-mapping': 0.3.13 + '@jridgewell/trace-mapping': 0.3.31 + + '@jridgewell/resolve-uri@3.1.2': {} + + '@jridgewell/source-map@0.3.11': + dependencies: + '@jridgewell/gen-mapping': 0.3.13 + '@jridgewell/trace-mapping': 0.3.31 + + '@jridgewell/sourcemap-codec@1.5.5': {} + + '@jridgewell/trace-mapping@0.3.31': + dependencies: + '@jridgewell/resolve-uri': 3.1.2 + '@jridgewell/sourcemap-codec': 1.5.5 + + '@jridgewell/trace-mapping@0.3.9': + dependencies: + '@jridgewell/resolve-uri': 3.1.2 + '@jridgewell/sourcemap-codec': 1.5.5 + + '@js-sdsl/ordered-map@4.4.2': {} + + '@jsonjoy.com/base64@1.1.2(tslib@2.8.1)': + dependencies: + tslib: 2.8.1 + + '@jsonjoy.com/base64@17.67.0(tslib@2.8.1)': + dependencies: + tslib: 2.8.1 + + '@jsonjoy.com/buffers@1.2.1(tslib@2.8.1)': + dependencies: + tslib: 2.8.1 + + '@jsonjoy.com/buffers@17.67.0(tslib@2.8.1)': + dependencies: + tslib: 2.8.1 + + '@jsonjoy.com/codegen@1.0.0(tslib@2.8.1)': + dependencies: + tslib: 2.8.1 + + '@jsonjoy.com/codegen@17.67.0(tslib@2.8.1)': + dependencies: + tslib: 2.8.1 + + '@jsonjoy.com/fs-core@4.57.1(tslib@2.8.1)': + dependencies: + '@jsonjoy.com/fs-node-builtins': 4.57.1(tslib@2.8.1) + '@jsonjoy.com/fs-node-utils': 4.57.1(tslib@2.8.1) + thingies: 2.6.0(tslib@2.8.1) + tslib: 2.8.1 + + '@jsonjoy.com/fs-fsa@4.57.1(tslib@2.8.1)': + dependencies: + '@jsonjoy.com/fs-core': 4.57.1(tslib@2.8.1) + '@jsonjoy.com/fs-node-builtins': 4.57.1(tslib@2.8.1) + '@jsonjoy.com/fs-node-utils': 4.57.1(tslib@2.8.1) + thingies: 2.6.0(tslib@2.8.1) + tslib: 2.8.1 + + '@jsonjoy.com/fs-node-builtins@4.57.1(tslib@2.8.1)': + dependencies: + tslib: 2.8.1 + + '@jsonjoy.com/fs-node-to-fsa@4.57.1(tslib@2.8.1)': + dependencies: + '@jsonjoy.com/fs-fsa': 4.57.1(tslib@2.8.1) + '@jsonjoy.com/fs-node-builtins': 4.57.1(tslib@2.8.1) + '@jsonjoy.com/fs-node-utils': 4.57.1(tslib@2.8.1) + tslib: 2.8.1 + + '@jsonjoy.com/fs-node-utils@4.57.1(tslib@2.8.1)': + dependencies: + '@jsonjoy.com/fs-node-builtins': 4.57.1(tslib@2.8.1) + tslib: 2.8.1 + + '@jsonjoy.com/fs-node@4.57.1(tslib@2.8.1)': + dependencies: + '@jsonjoy.com/fs-core': 4.57.1(tslib@2.8.1) + '@jsonjoy.com/fs-node-builtins': 4.57.1(tslib@2.8.1) + '@jsonjoy.com/fs-node-utils': 4.57.1(tslib@2.8.1) + '@jsonjoy.com/fs-print': 4.57.1(tslib@2.8.1) + '@jsonjoy.com/fs-snapshot': 4.57.1(tslib@2.8.1) + glob-to-regex.js: 1.2.0(tslib@2.8.1) + thingies: 2.6.0(tslib@2.8.1) + tslib: 2.8.1 + + '@jsonjoy.com/fs-print@4.57.1(tslib@2.8.1)': + dependencies: + '@jsonjoy.com/fs-node-utils': 4.57.1(tslib@2.8.1) + tree-dump: 1.1.0(tslib@2.8.1) + tslib: 2.8.1 + + '@jsonjoy.com/fs-snapshot@4.57.1(tslib@2.8.1)': + dependencies: + '@jsonjoy.com/buffers': 17.67.0(tslib@2.8.1) + '@jsonjoy.com/fs-node-utils': 4.57.1(tslib@2.8.1) + '@jsonjoy.com/json-pack': 17.67.0(tslib@2.8.1) + '@jsonjoy.com/util': 17.67.0(tslib@2.8.1) + tslib: 2.8.1 + + '@jsonjoy.com/json-pack@1.21.0(tslib@2.8.1)': + dependencies: + '@jsonjoy.com/base64': 1.1.2(tslib@2.8.1) + '@jsonjoy.com/buffers': 1.2.1(tslib@2.8.1) + '@jsonjoy.com/codegen': 1.0.0(tslib@2.8.1) + '@jsonjoy.com/json-pointer': 1.0.2(tslib@2.8.1) + '@jsonjoy.com/util': 1.9.0(tslib@2.8.1) + hyperdyperid: 1.2.0 + thingies: 2.6.0(tslib@2.8.1) + tree-dump: 1.1.0(tslib@2.8.1) + tslib: 2.8.1 + + '@jsonjoy.com/json-pack@17.67.0(tslib@2.8.1)': + dependencies: + '@jsonjoy.com/base64': 17.67.0(tslib@2.8.1) + '@jsonjoy.com/buffers': 17.67.0(tslib@2.8.1) + '@jsonjoy.com/codegen': 17.67.0(tslib@2.8.1) + '@jsonjoy.com/json-pointer': 17.67.0(tslib@2.8.1) + '@jsonjoy.com/util': 17.67.0(tslib@2.8.1) + hyperdyperid: 1.2.0 + thingies: 2.6.0(tslib@2.8.1) + tree-dump: 1.1.0(tslib@2.8.1) + tslib: 2.8.1 + + '@jsonjoy.com/json-pointer@1.0.2(tslib@2.8.1)': + dependencies: + '@jsonjoy.com/codegen': 1.0.0(tslib@2.8.1) + '@jsonjoy.com/util': 1.9.0(tslib@2.8.1) + tslib: 2.8.1 + + '@jsonjoy.com/json-pointer@17.67.0(tslib@2.8.1)': + dependencies: + '@jsonjoy.com/util': 17.67.0(tslib@2.8.1) + tslib: 2.8.1 + + '@jsonjoy.com/util@1.9.0(tslib@2.8.1)': + dependencies: + '@jsonjoy.com/buffers': 1.2.1(tslib@2.8.1) + '@jsonjoy.com/codegen': 1.0.0(tslib@2.8.1) + tslib: 2.8.1 + + '@jsonjoy.com/util@17.67.0(tslib@2.8.1)': + dependencies: + '@jsonjoy.com/buffers': 17.67.0(tslib@2.8.1) + '@jsonjoy.com/codegen': 17.67.0(tslib@2.8.1) + tslib: 2.8.1 + + '@ladle/react-context@1.0.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + dependencies: + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + + '@ladle/react@2.5.1(@types/node@24.7.2)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.93.2)(terser@5.44.0)(typescript@5.9.3)': + dependencies: + '@babel/code-frame': 7.27.1 + '@babel/core': 7.28.4 + '@babel/generator': 7.28.3 + '@babel/parser': 7.28.4 + '@babel/plugin-proposal-class-properties': 7.18.6(@babel/core@7.28.4) + '@babel/preset-env': 7.28.3(@babel/core@7.28.4) + '@babel/preset-react': 7.27.1(@babel/core@7.28.4) + '@babel/preset-typescript': 7.18.6(@babel/core@7.28.4) + '@babel/runtime': 7.28.4 + '@babel/template': 7.27.2 + '@babel/traverse': 7.28.4 + '@babel/types': 7.28.4 + '@ladle/react-context': 1.0.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@vitejs/plugin-react': 3.1.0(vite@4.5.14(@types/node@24.7.2)(sass@1.93.2)(terser@5.44.0)) + axe-core: 4.11.0 + boxen: 7.1.1 + chokidar: 3.6.0 + classnames: 2.5.1 + commander: 9.5.0 + cross-spawn: 7.0.6 + debug: 4.4.3(supports-color@8.1.1) + default-browser: 3.1.0 + express: 4.21.2 + get-port: 6.1.2 + globby: 13.2.2 + history: 5.3.0 + lodash.merge: 4.6.2 + open: 8.4.2 + prism-react-renderer: 1.3.5(react@18.3.1) + prop-types: 15.8.1 + query-string: 8.2.0 + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + react-frame-component: 5.2.7(prop-types@15.8.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + react-inspector: 6.0.2(react@18.3.1) + vite: 4.5.14(@types/node@24.7.2)(sass@1.93.2)(terser@5.44.0) + vite-tsconfig-paths: 4.3.2(typescript@5.9.3)(vite@4.5.14(@types/node@24.7.2)(sass@1.93.2)(terser@5.44.0)) + transitivePeerDependencies: + - '@types/node' + - less + - lightningcss + - sass + - stylus + - sugarss + - supports-color + - terser + - typescript + + '@leichtgewicht/ip-codec@2.0.5': {} + + '@lezer/common@1.2.3': {} + + '@lezer/common@1.5.2': {} + + '@lezer/css@1.3.3': + dependencies: + '@lezer/common': 1.2.3 + '@lezer/highlight': 1.2.1 + '@lezer/lr': 1.4.2 + + '@lezer/highlight@1.2.1': + dependencies: + '@lezer/common': 1.2.3 + + '@lezer/html@1.3.13': + dependencies: + '@lezer/common': 1.2.3 + '@lezer/highlight': 1.2.1 + '@lezer/lr': 1.4.2 + + '@lezer/javascript@1.5.4': + dependencies: + '@lezer/common': 1.2.3 + '@lezer/highlight': 1.2.1 + '@lezer/lr': 1.4.2 + + '@lezer/json@1.0.3': + dependencies: + '@lezer/common': 1.2.3 + '@lezer/highlight': 1.2.1 + '@lezer/lr': 1.4.2 + + '@lezer/lr@1.4.2': + dependencies: + '@lezer/common': 1.2.3 + + '@lezer/markdown@1.6.3': + dependencies: + '@lezer/common': 1.5.2 + '@lezer/highlight': 1.2.1 + + '@mapbox/node-pre-gyp@1.0.11(encoding@0.1.13)': + dependencies: + detect-libc: 2.1.2 + https-proxy-agent: 5.0.1 + make-dir: 3.1.0 + node-fetch: 2.7.0(encoding@0.1.13) + nopt: 5.0.0 + npmlog: 5.0.1 + rimraf: 3.0.2 + semver: 7.7.1 + tar: 6.2.1 + transitivePeerDependencies: + - encoding + - supports-color + optional: true + + '@marijn/find-cluster-break@1.0.2': {} + + '@microsoft/fetch-event-source@2.0.1': {} + + '@mongodb-js/saslprep@1.3.1': + dependencies: + sparse-bitfield: 3.0.3 + optional: true + + '@mui/base@5.0.0-beta.27(@types/react@18.3.26)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + dependencies: + '@babel/runtime': 7.28.4 + '@floating-ui/react-dom': 2.1.6(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@mui/types': 7.4.7(@types/react@18.3.26) + '@mui/utils': 5.17.1(@types/react@18.3.26)(react@18.3.1) + '@popperjs/core': 2.11.8 + clsx: 2.1.1 + prop-types: 15.8.1 + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + optionalDependencies: + '@types/react': 18.3.26 + + '@mui/base@5.0.0-beta.40(@types/react@18.3.26)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + dependencies: + '@babel/runtime': 7.28.4 + '@floating-ui/react-dom': 2.1.6(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@mui/types': 7.4.7(@types/react@18.3.26) + '@mui/utils': 5.17.1(@types/react@18.3.26)(react@18.3.1) + '@popperjs/core': 2.11.8 + clsx: 2.1.1 + prop-types: 15.8.1 + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + optionalDependencies: + '@types/react': 18.3.26 + + '@mui/core-downloads-tracker@5.18.0': {} + + '@mui/icons-material@5.18.0(@mui/material@5.15.0(@emotion/react@11.14.0(@types/react@18.3.26)(react@18.3.1))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@18.3.26)(react@18.3.1))(@types/react@18.3.26)(react@18.3.1))(@types/react@18.3.26)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(@types/react@18.3.26)(react@18.3.1)': + dependencies: + '@babel/runtime': 7.28.4 + '@mui/material': 5.15.0(@emotion/react@11.14.0(@types/react@18.3.26)(react@18.3.1))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@18.3.26)(react@18.3.1))(@types/react@18.3.26)(react@18.3.1))(@types/react@18.3.26)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + react: 18.3.1 + optionalDependencies: + '@types/react': 18.3.26 + + '@mui/lab@5.0.0-alpha.156(@emotion/react@11.14.0(@types/react@18.3.26)(react@18.3.1))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@18.3.26)(react@18.3.1))(@types/react@18.3.26)(react@18.3.1))(@mui/material@5.15.0(@emotion/react@11.14.0(@types/react@18.3.26)(react@18.3.1))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@18.3.26)(react@18.3.1))(@types/react@18.3.26)(react@18.3.1))(@types/react@18.3.26)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(@types/react@18.3.26)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + dependencies: + '@babel/runtime': 7.28.4 + '@mui/base': 5.0.0-beta.27(@types/react@18.3.26)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@mui/material': 5.15.0(@emotion/react@11.14.0(@types/react@18.3.26)(react@18.3.1))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@18.3.26)(react@18.3.1))(@types/react@18.3.26)(react@18.3.1))(@types/react@18.3.26)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@mui/system': 5.18.0(@emotion/react@11.14.0(@types/react@18.3.26)(react@18.3.1))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@18.3.26)(react@18.3.1))(@types/react@18.3.26)(react@18.3.1))(@types/react@18.3.26)(react@18.3.1) + '@mui/types': 7.4.7(@types/react@18.3.26) + '@mui/utils': 5.17.1(@types/react@18.3.26)(react@18.3.1) + clsx: 2.1.1 + prop-types: 15.8.1 + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + optionalDependencies: + '@emotion/react': 11.14.0(@types/react@18.3.26)(react@18.3.1) + '@emotion/styled': 11.14.1(@emotion/react@11.14.0(@types/react@18.3.26)(react@18.3.1))(@types/react@18.3.26)(react@18.3.1) + '@types/react': 18.3.26 + + '@mui/material@5.15.0(@emotion/react@11.14.0(@types/react@18.3.26)(react@18.3.1))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@18.3.26)(react@18.3.1))(@types/react@18.3.26)(react@18.3.1))(@types/react@18.3.26)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + dependencies: + '@babel/runtime': 7.28.4 + '@mui/base': 5.0.0-beta.27(@types/react@18.3.26)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@mui/core-downloads-tracker': 5.18.0 + '@mui/system': 5.18.0(@emotion/react@11.14.0(@types/react@18.3.26)(react@18.3.1))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@18.3.26)(react@18.3.1))(@types/react@18.3.26)(react@18.3.1))(@types/react@18.3.26)(react@18.3.1) + '@mui/types': 7.4.7(@types/react@18.3.26) + '@mui/utils': 5.17.1(@types/react@18.3.26)(react@18.3.1) + '@types/react-transition-group': 4.4.12(@types/react@18.3.26) + clsx: 2.1.1 + csstype: 3.1.3 + prop-types: 15.8.1 + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + react-is: 18.3.1 + react-transition-group: 4.4.5(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + optionalDependencies: + '@emotion/react': 11.14.0(@types/react@18.3.26)(react@18.3.1) + '@emotion/styled': 11.14.1(@emotion/react@11.14.0(@types/react@18.3.26)(react@18.3.1))(@types/react@18.3.26)(react@18.3.1) + '@types/react': 18.3.26 + + '@mui/private-theming@5.17.1(@types/react@18.3.26)(react@18.3.1)': + dependencies: + '@babel/runtime': 7.28.4 + '@mui/utils': 5.17.1(@types/react@18.3.26)(react@18.3.1) + prop-types: 15.8.1 + react: 18.3.1 + optionalDependencies: + '@types/react': 18.3.26 + + '@mui/styled-engine@5.18.0(@emotion/react@11.14.0(@types/react@18.3.26)(react@18.3.1))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@18.3.26)(react@18.3.1))(@types/react@18.3.26)(react@18.3.1))(react@18.3.1)': + dependencies: + '@babel/runtime': 7.28.4 + '@emotion/cache': 11.14.0 + '@emotion/serialize': 1.3.3 + csstype: 3.1.3 + prop-types: 15.8.1 + react: 18.3.1 + optionalDependencies: + '@emotion/react': 11.14.0(@types/react@18.3.26)(react@18.3.1) + '@emotion/styled': 11.14.1(@emotion/react@11.14.0(@types/react@18.3.26)(react@18.3.1))(@types/react@18.3.26)(react@18.3.1) + + '@mui/system@5.18.0(@emotion/react@11.14.0(@types/react@18.3.26)(react@18.3.1))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@18.3.26)(react@18.3.1))(@types/react@18.3.26)(react@18.3.1))(@types/react@18.3.26)(react@18.3.1)': + dependencies: + '@babel/runtime': 7.28.4 + '@mui/private-theming': 5.17.1(@types/react@18.3.26)(react@18.3.1) + '@mui/styled-engine': 5.18.0(@emotion/react@11.14.0(@types/react@18.3.26)(react@18.3.1))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@18.3.26)(react@18.3.1))(@types/react@18.3.26)(react@18.3.1))(react@18.3.1) + '@mui/types': 7.2.24(@types/react@18.3.26) + '@mui/utils': 5.17.1(@types/react@18.3.26)(react@18.3.1) + clsx: 2.1.1 + csstype: 3.1.3 + prop-types: 15.8.1 + react: 18.3.1 + optionalDependencies: + '@emotion/react': 11.14.0(@types/react@18.3.26)(react@18.3.1) + '@emotion/styled': 11.14.1(@emotion/react@11.14.0(@types/react@18.3.26)(react@18.3.1))(@types/react@18.3.26)(react@18.3.1) + '@types/react': 18.3.26 + + '@mui/types@7.2.24(@types/react@18.3.26)': + optionalDependencies: + '@types/react': 18.3.26 + + '@mui/types@7.4.7(@types/react@18.3.26)': + dependencies: + '@babel/runtime': 7.28.4 + optionalDependencies: + '@types/react': 18.3.26 + + '@mui/utils@5.17.1(@types/react@18.3.26)(react@18.3.1)': + dependencies: + '@babel/runtime': 7.28.4 + '@mui/types': 7.2.24(@types/react@18.3.26) + '@types/prop-types': 15.7.15 + clsx: 2.1.1 + prop-types: 15.8.1 + react: 18.3.1 + react-is: 19.2.0 + optionalDependencies: + '@types/react': 18.3.26 + + '@mui/x-data-grid@6.8.0(@mui/material@5.15.0(@emotion/react@11.14.0(@types/react@18.3.26)(react@18.3.1))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@18.3.26)(react@18.3.1))(@types/react@18.3.26)(react@18.3.1))(@types/react@18.3.26)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(@mui/system@5.18.0(@emotion/react@11.14.0(@types/react@18.3.26)(react@18.3.1))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@18.3.26)(react@18.3.1))(@types/react@18.3.26)(react@18.3.1))(@types/react@18.3.26)(react@18.3.1))(@types/react@18.3.26)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + dependencies: + '@babel/runtime': 7.28.4 + '@mui/material': 5.15.0(@emotion/react@11.14.0(@types/react@18.3.26)(react@18.3.1))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@18.3.26)(react@18.3.1))(@types/react@18.3.26)(react@18.3.1))(@types/react@18.3.26)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@mui/system': 5.18.0(@emotion/react@11.14.0(@types/react@18.3.26)(react@18.3.1))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@18.3.26)(react@18.3.1))(@types/react@18.3.26)(react@18.3.1))(@types/react@18.3.26)(react@18.3.1) + '@mui/utils': 5.17.1(@types/react@18.3.26)(react@18.3.1) + clsx: 1.2.1 + prop-types: 15.8.1 + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + reselect: 4.1.8 + transitivePeerDependencies: + - '@types/react' + + '@mui/x-tree-view@6.17.0(@emotion/react@11.14.0(@types/react@18.3.26)(react@18.3.1))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@18.3.26)(react@18.3.1))(@types/react@18.3.26)(react@18.3.1))(@mui/material@5.15.0(@emotion/react@11.14.0(@types/react@18.3.26)(react@18.3.1))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@18.3.26)(react@18.3.1))(@types/react@18.3.26)(react@18.3.1))(@types/react@18.3.26)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(@mui/system@5.18.0(@emotion/react@11.14.0(@types/react@18.3.26)(react@18.3.1))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@18.3.26)(react@18.3.1))(@types/react@18.3.26)(react@18.3.1))(@types/react@18.3.26)(react@18.3.1))(@types/react@18.3.26)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + dependencies: + '@babel/runtime': 7.28.4 + '@emotion/react': 11.14.0(@types/react@18.3.26)(react@18.3.1) + '@emotion/styled': 11.14.1(@emotion/react@11.14.0(@types/react@18.3.26)(react@18.3.1))(@types/react@18.3.26)(react@18.3.1) + '@mui/base': 5.0.0-beta.40(@types/react@18.3.26)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@mui/material': 5.15.0(@emotion/react@11.14.0(@types/react@18.3.26)(react@18.3.1))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@18.3.26)(react@18.3.1))(@types/react@18.3.26)(react@18.3.1))(@types/react@18.3.26)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@mui/system': 5.18.0(@emotion/react@11.14.0(@types/react@18.3.26)(react@18.3.1))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@18.3.26)(react@18.3.1))(@types/react@18.3.26)(react@18.3.1))(@types/react@18.3.26)(react@18.3.1) + '@mui/utils': 5.17.1(@types/react@18.3.26)(react@18.3.1) + '@types/react-transition-group': 4.4.12(@types/react@18.3.26) + clsx: 2.1.1 + prop-types: 15.8.1 + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + react-transition-group: 4.4.5(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + transitivePeerDependencies: + - '@types/react' + + '@nicolo-ribaudo/eslint-scope-5-internals@5.1.1-v1': + dependencies: + eslint-scope: 5.1.1 + + '@nodelib/fs.scandir@2.1.5': + dependencies: + '@nodelib/fs.stat': 2.0.5 + run-parallel: 1.2.0 + + '@nodelib/fs.stat@2.0.5': {} + + '@nodelib/fs.walk@1.2.8': + dependencies: + '@nodelib/fs.scandir': 2.1.5 + fastq: 1.19.1 + + '@npmcli/fs@1.1.1': + dependencies: + '@gar/promisify': 1.1.3 + semver: 7.7.1 + optional: true + + '@npmcli/move-file@1.1.2': + dependencies: + mkdirp: 1.0.4 + rimraf: 3.0.2 + optional: true + + '@opentelemetry/api-logs@0.214.0': + dependencies: + '@opentelemetry/api': 1.9.0 + + '@opentelemetry/api-logs@0.57.2': + dependencies: + '@opentelemetry/api': 1.9.0 + + '@opentelemetry/api@1.9.0': {} + + '@opentelemetry/context-async-hooks@1.30.1(@opentelemetry/api@1.9.0)': + dependencies: + '@opentelemetry/api': 1.9.0 + + '@opentelemetry/context-async-hooks@2.6.1(@opentelemetry/api@1.9.0)': + dependencies: + '@opentelemetry/api': 1.9.0 + + '@opentelemetry/core@1.30.1(@opentelemetry/api@1.9.0)': + dependencies: + '@opentelemetry/api': 1.9.0 + '@opentelemetry/semantic-conventions': 1.28.0 + + '@opentelemetry/core@2.6.1(@opentelemetry/api@1.9.0)': + dependencies: + '@opentelemetry/api': 1.9.0 + '@opentelemetry/semantic-conventions': 1.37.0 + + '@opentelemetry/exporter-trace-otlp-grpc@0.214.0(@opentelemetry/api@1.9.0)': + dependencies: + '@grpc/grpc-js': 1.14.0 + '@opentelemetry/api': 1.9.0 + '@opentelemetry/core': 2.6.1(@opentelemetry/api@1.9.0) + '@opentelemetry/otlp-exporter-base': 0.214.0(@opentelemetry/api@1.9.0) + '@opentelemetry/otlp-grpc-exporter-base': 0.214.0(@opentelemetry/api@1.9.0) + '@opentelemetry/otlp-transformer': 0.214.0(@opentelemetry/api@1.9.0) + '@opentelemetry/resources': 2.6.1(@opentelemetry/api@1.9.0) + '@opentelemetry/sdk-trace-base': 2.6.1(@opentelemetry/api@1.9.0) + + '@opentelemetry/exporter-trace-otlp-grpc@0.57.2(@opentelemetry/api@1.9.0)': + dependencies: + '@grpc/grpc-js': 1.14.0 + '@opentelemetry/api': 1.9.0 + '@opentelemetry/core': 1.30.1(@opentelemetry/api@1.9.0) + '@opentelemetry/otlp-exporter-base': 0.57.2(@opentelemetry/api@1.9.0) + '@opentelemetry/otlp-grpc-exporter-base': 0.57.2(@opentelemetry/api@1.9.0) + '@opentelemetry/otlp-transformer': 0.57.2(@opentelemetry/api@1.9.0) + '@opentelemetry/resources': 1.30.1(@opentelemetry/api@1.9.0) + '@opentelemetry/sdk-trace-base': 1.30.1(@opentelemetry/api@1.9.0) + + '@opentelemetry/exporter-trace-otlp-proto@0.214.0(@opentelemetry/api@1.9.0)': + dependencies: + '@opentelemetry/api': 1.9.0 + '@opentelemetry/core': 2.6.1(@opentelemetry/api@1.9.0) + '@opentelemetry/otlp-exporter-base': 0.214.0(@opentelemetry/api@1.9.0) + '@opentelemetry/otlp-transformer': 0.214.0(@opentelemetry/api@1.9.0) + '@opentelemetry/resources': 2.6.1(@opentelemetry/api@1.9.0) + '@opentelemetry/sdk-trace-base': 2.6.1(@opentelemetry/api@1.9.0) + + '@opentelemetry/exporter-trace-otlp-proto@0.57.2(@opentelemetry/api@1.9.0)': + dependencies: + '@opentelemetry/api': 1.9.0 + '@opentelemetry/core': 1.30.1(@opentelemetry/api@1.9.0) + '@opentelemetry/otlp-exporter-base': 0.57.2(@opentelemetry/api@1.9.0) + '@opentelemetry/otlp-transformer': 0.57.2(@opentelemetry/api@1.9.0) + '@opentelemetry/resources': 1.30.1(@opentelemetry/api@1.9.0) + '@opentelemetry/sdk-trace-base': 1.30.1(@opentelemetry/api@1.9.0) + + '@opentelemetry/otlp-exporter-base@0.214.0(@opentelemetry/api@1.9.0)': + dependencies: + '@opentelemetry/api': 1.9.0 + '@opentelemetry/core': 2.6.1(@opentelemetry/api@1.9.0) + '@opentelemetry/otlp-transformer': 0.214.0(@opentelemetry/api@1.9.0) + + '@opentelemetry/otlp-exporter-base@0.57.2(@opentelemetry/api@1.9.0)': + dependencies: + '@opentelemetry/api': 1.9.0 + '@opentelemetry/core': 1.30.1(@opentelemetry/api@1.9.0) + '@opentelemetry/otlp-transformer': 0.57.2(@opentelemetry/api@1.9.0) + + '@opentelemetry/otlp-grpc-exporter-base@0.214.0(@opentelemetry/api@1.9.0)': + dependencies: + '@grpc/grpc-js': 1.14.0 + '@opentelemetry/api': 1.9.0 + '@opentelemetry/core': 2.6.1(@opentelemetry/api@1.9.0) + '@opentelemetry/otlp-exporter-base': 0.214.0(@opentelemetry/api@1.9.0) + '@opentelemetry/otlp-transformer': 0.214.0(@opentelemetry/api@1.9.0) + + '@opentelemetry/otlp-grpc-exporter-base@0.57.2(@opentelemetry/api@1.9.0)': + dependencies: + '@grpc/grpc-js': 1.14.0 + '@opentelemetry/api': 1.9.0 + '@opentelemetry/core': 1.30.1(@opentelemetry/api@1.9.0) + '@opentelemetry/otlp-exporter-base': 0.57.2(@opentelemetry/api@1.9.0) + '@opentelemetry/otlp-transformer': 0.57.2(@opentelemetry/api@1.9.0) + + '@opentelemetry/otlp-transformer@0.214.0(@opentelemetry/api@1.9.0)': + dependencies: + '@opentelemetry/api': 1.9.0 + '@opentelemetry/api-logs': 0.214.0 + '@opentelemetry/core': 2.6.1(@opentelemetry/api@1.9.0) + '@opentelemetry/resources': 2.6.1(@opentelemetry/api@1.9.0) + '@opentelemetry/sdk-logs': 0.214.0(@opentelemetry/api@1.9.0) + '@opentelemetry/sdk-metrics': 2.6.1(@opentelemetry/api@1.9.0) + '@opentelemetry/sdk-trace-base': 2.6.1(@opentelemetry/api@1.9.0) + protobufjs: 7.4.0 + + '@opentelemetry/otlp-transformer@0.57.2(@opentelemetry/api@1.9.0)': + dependencies: + '@opentelemetry/api': 1.9.0 + '@opentelemetry/api-logs': 0.57.2 + '@opentelemetry/core': 1.30.1(@opentelemetry/api@1.9.0) + '@opentelemetry/resources': 1.30.1(@opentelemetry/api@1.9.0) + '@opentelemetry/sdk-logs': 0.57.2(@opentelemetry/api@1.9.0) + '@opentelemetry/sdk-metrics': 1.30.1(@opentelemetry/api@1.9.0) + '@opentelemetry/sdk-trace-base': 1.30.1(@opentelemetry/api@1.9.0) + protobufjs: 7.4.0 + + '@opentelemetry/propagator-b3@1.30.1(@opentelemetry/api@1.9.0)': + dependencies: + '@opentelemetry/api': 1.9.0 + '@opentelemetry/core': 1.30.1(@opentelemetry/api@1.9.0) + + '@opentelemetry/propagator-jaeger@1.30.1(@opentelemetry/api@1.9.0)': + dependencies: + '@opentelemetry/api': 1.9.0 + '@opentelemetry/core': 1.30.1(@opentelemetry/api@1.9.0) + + '@opentelemetry/resources@1.30.1(@opentelemetry/api@1.9.0)': + dependencies: + '@opentelemetry/api': 1.9.0 + '@opentelemetry/core': 1.30.1(@opentelemetry/api@1.9.0) + '@opentelemetry/semantic-conventions': 1.28.0 + + '@opentelemetry/resources@2.6.1(@opentelemetry/api@1.9.0)': + dependencies: + '@opentelemetry/api': 1.9.0 + '@opentelemetry/core': 2.6.1(@opentelemetry/api@1.9.0) + '@opentelemetry/semantic-conventions': 1.37.0 + + '@opentelemetry/sdk-logs@0.214.0(@opentelemetry/api@1.9.0)': + dependencies: + '@opentelemetry/api': 1.9.0 + '@opentelemetry/api-logs': 0.214.0 + '@opentelemetry/core': 2.6.1(@opentelemetry/api@1.9.0) + '@opentelemetry/resources': 2.6.1(@opentelemetry/api@1.9.0) + '@opentelemetry/semantic-conventions': 1.37.0 + + '@opentelemetry/sdk-logs@0.57.2(@opentelemetry/api@1.9.0)': + dependencies: + '@opentelemetry/api': 1.9.0 + '@opentelemetry/api-logs': 0.57.2 + '@opentelemetry/core': 1.30.1(@opentelemetry/api@1.9.0) + '@opentelemetry/resources': 1.30.1(@opentelemetry/api@1.9.0) + + '@opentelemetry/sdk-metrics@1.30.1(@opentelemetry/api@1.9.0)': + dependencies: + '@opentelemetry/api': 1.9.0 + '@opentelemetry/core': 1.30.1(@opentelemetry/api@1.9.0) + '@opentelemetry/resources': 1.30.1(@opentelemetry/api@1.9.0) + + '@opentelemetry/sdk-metrics@2.6.1(@opentelemetry/api@1.9.0)': + dependencies: + '@opentelemetry/api': 1.9.0 + '@opentelemetry/core': 2.6.1(@opentelemetry/api@1.9.0) + '@opentelemetry/resources': 2.6.1(@opentelemetry/api@1.9.0) + + '@opentelemetry/sdk-trace-base@1.30.1(@opentelemetry/api@1.9.0)': + dependencies: + '@opentelemetry/api': 1.9.0 + '@opentelemetry/core': 1.30.1(@opentelemetry/api@1.9.0) + '@opentelemetry/resources': 1.30.1(@opentelemetry/api@1.9.0) + '@opentelemetry/semantic-conventions': 1.28.0 + + '@opentelemetry/sdk-trace-base@2.6.1(@opentelemetry/api@1.9.0)': + dependencies: + '@opentelemetry/api': 1.9.0 + '@opentelemetry/core': 2.6.1(@opentelemetry/api@1.9.0) + '@opentelemetry/resources': 2.6.1(@opentelemetry/api@1.9.0) + '@opentelemetry/semantic-conventions': 1.37.0 + + '@opentelemetry/sdk-trace-node@1.30.1(@opentelemetry/api@1.9.0)': + dependencies: + '@opentelemetry/api': 1.9.0 + '@opentelemetry/context-async-hooks': 1.30.1(@opentelemetry/api@1.9.0) + '@opentelemetry/core': 1.30.1(@opentelemetry/api@1.9.0) + '@opentelemetry/propagator-b3': 1.30.1(@opentelemetry/api@1.9.0) + '@opentelemetry/propagator-jaeger': 1.30.1(@opentelemetry/api@1.9.0) + '@opentelemetry/sdk-trace-base': 1.30.1(@opentelemetry/api@1.9.0) + semver: 7.7.1 + + '@opentelemetry/sdk-trace-node@2.6.1(@opentelemetry/api@1.9.0)': + dependencies: + '@opentelemetry/api': 1.9.0 + '@opentelemetry/context-async-hooks': 2.6.1(@opentelemetry/api@1.9.0) + '@opentelemetry/core': 2.6.1(@opentelemetry/api@1.9.0) + '@opentelemetry/sdk-trace-base': 2.6.1(@opentelemetry/api@1.9.0) + + '@opentelemetry/semantic-conventions@1.28.0': {} + + '@opentelemetry/semantic-conventions@1.37.0': {} + + '@parcel/watcher-android-arm64@2.5.1': + optional: true + + '@parcel/watcher-darwin-arm64@2.5.1': + optional: true + + '@parcel/watcher-darwin-x64@2.5.1': + optional: true + + '@parcel/watcher-freebsd-x64@2.5.1': + optional: true + + '@parcel/watcher-linux-arm-glibc@2.5.1': + optional: true + + '@parcel/watcher-linux-arm-musl@2.5.1': + optional: true + + '@parcel/watcher-linux-arm64-glibc@2.5.1': + optional: true + + '@parcel/watcher-linux-arm64-musl@2.5.1': + optional: true + + '@parcel/watcher-linux-x64-glibc@2.5.1': + optional: true + + '@parcel/watcher-linux-x64-musl@2.5.1': + optional: true + + '@parcel/watcher-win32-arm64@2.5.1': + optional: true + + '@parcel/watcher-win32-ia32@2.5.1': + optional: true + + '@parcel/watcher-win32-x64@2.5.1': + optional: true + + '@parcel/watcher@2.5.1': + dependencies: + detect-libc: 1.0.3 + is-glob: 4.0.3 + micromatch: 4.0.8 + node-addon-api: 7.1.1 + optionalDependencies: + '@parcel/watcher-android-arm64': 2.5.1 + '@parcel/watcher-darwin-arm64': 2.5.1 + '@parcel/watcher-darwin-x64': 2.5.1 + '@parcel/watcher-freebsd-x64': 2.5.1 + '@parcel/watcher-linux-arm-glibc': 2.5.1 + '@parcel/watcher-linux-arm-musl': 2.5.1 + '@parcel/watcher-linux-arm64-glibc': 2.5.1 + '@parcel/watcher-linux-arm64-musl': 2.5.1 + '@parcel/watcher-linux-x64-glibc': 2.5.1 + '@parcel/watcher-linux-x64-musl': 2.5.1 + '@parcel/watcher-win32-arm64': 2.5.1 + '@parcel/watcher-win32-ia32': 2.5.1 + '@parcel/watcher-win32-x64': 2.5.1 + optional: true + + '@pkgjs/parseargs@0.11.0': + optional: true + + '@pmmmwh/react-refresh-webpack-plugin@0.5.17(react-refresh@0.11.0)(type-fest@4.41.0)(webpack-dev-server@4.15.2(bufferutil@4.0.9)(tslib@2.8.1)(utf-8-validate@6.0.5)(webpack@5.102.1))(webpack@5.102.1)': + dependencies: + ansi-html: 0.0.9 + core-js-pure: 3.46.0 + error-stack-parser: 2.1.4 + html-entities: 2.6.0 + loader-utils: 2.0.4 + react-refresh: 0.11.0 + schema-utils: 4.3.3 + source-map: 0.7.6 + webpack: 5.102.1 + optionalDependencies: + type-fest: 4.41.0 + webpack-dev-server: 4.15.2(bufferutil@4.0.9)(tslib@2.8.1)(utf-8-validate@6.0.5)(webpack@5.102.1) + + '@popperjs/core@2.11.8': {} + + '@protobufjs/aspromise@1.1.2': {} + + '@protobufjs/base64@1.1.2': {} + + '@protobufjs/codegen@2.0.4': {} + + '@protobufjs/eventemitter@1.1.0': {} + + '@protobufjs/fetch@1.1.0': + dependencies: + '@protobufjs/aspromise': 1.1.2 + '@protobufjs/inquire': 1.1.0 + + '@protobufjs/float@1.0.2': {} + + '@protobufjs/inquire@1.1.0': {} + + '@protobufjs/path@1.1.2': {} + + '@protobufjs/pool@1.1.0': {} + + '@protobufjs/utf8@1.1.0': {} + + '@react-keycloak/core@3.2.0(react@18.3.1)': + dependencies: + react: 18.3.1 + react-fast-compare: 3.2.2 + + '@react-keycloak/web@3.4.0(keycloak-js@26.2.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.9.3)': + dependencies: + '@babel/runtime': 7.28.4 + '@react-keycloak/core': 3.2.0(react@18.3.1) + hoist-non-react-statics: 3.3.2 + keycloak-js: 26.2.1 + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + optionalDependencies: + typescript: 5.9.3 + + '@reactflow/background@11.3.14(@types/react@18.3.26)(immer@11.1.4)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + dependencies: + '@reactflow/core': 11.11.4(@types/react@18.3.26)(immer@11.1.4)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + classcat: 5.0.5 + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + zustand: 4.5.7(@types/react@18.3.26)(immer@11.1.4)(react@18.3.1) + transitivePeerDependencies: + - '@types/react' + - immer + + '@reactflow/controls@11.2.14(@types/react@18.3.26)(immer@11.1.4)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + dependencies: + '@reactflow/core': 11.11.4(@types/react@18.3.26)(immer@11.1.4)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + classcat: 5.0.5 + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + zustand: 4.5.7(@types/react@18.3.26)(immer@11.1.4)(react@18.3.1) + transitivePeerDependencies: + - '@types/react' + - immer + + '@reactflow/core@11.11.4(@types/react@18.3.26)(immer@11.1.4)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + dependencies: + '@types/d3': 7.4.3 + '@types/d3-drag': 3.0.7 + '@types/d3-selection': 3.0.11 + '@types/d3-zoom': 3.0.8 + classcat: 5.0.5 + d3-drag: 3.0.0 + d3-selection: 3.0.0 + d3-zoom: 3.0.0 + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + zustand: 4.5.7(@types/react@18.3.26)(immer@11.1.4)(react@18.3.1) + transitivePeerDependencies: + - '@types/react' + - immer + + '@reactflow/minimap@11.7.14(@types/react@18.3.26)(immer@11.1.4)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + dependencies: + '@reactflow/core': 11.11.4(@types/react@18.3.26)(immer@11.1.4)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@types/d3-selection': 3.0.11 + '@types/d3-zoom': 3.0.8 + classcat: 5.0.5 + d3-selection: 3.0.0 + d3-zoom: 3.0.0 + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + zustand: 4.5.7(@types/react@18.3.26)(immer@11.1.4)(react@18.3.1) + transitivePeerDependencies: + - '@types/react' + - immer + + '@reactflow/node-resizer@2.2.14(@types/react@18.3.26)(immer@11.1.4)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + dependencies: + '@reactflow/core': 11.11.4(@types/react@18.3.26)(immer@11.1.4)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + classcat: 5.0.5 + d3-drag: 3.0.0 + d3-selection: 3.0.0 + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + zustand: 4.5.7(@types/react@18.3.26)(immer@11.1.4)(react@18.3.1) + transitivePeerDependencies: + - '@types/react' + - immer + + '@reactflow/node-toolbar@1.3.14(@types/react@18.3.26)(immer@11.1.4)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + dependencies: + '@reactflow/core': 11.11.4(@types/react@18.3.26)(immer@11.1.4)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + classcat: 5.0.5 + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + zustand: 4.5.7(@types/react@18.3.26)(immer@11.1.4)(react@18.3.1) + transitivePeerDependencies: + - '@types/react' + - immer + + '@redis/bloom@1.2.0(@redis/client@1.6.1)': + dependencies: + '@redis/client': 1.6.1 + optional: true + + '@redis/client@1.6.1': + dependencies: + cluster-key-slot: 1.1.2 + generic-pool: 3.9.0 + yallist: 4.0.0 + optional: true + + '@redis/graph@1.1.1(@redis/client@1.6.1)': + dependencies: + '@redis/client': 1.6.1 + optional: true + + '@redis/json@1.0.7(@redis/client@1.6.1)': + dependencies: + '@redis/client': 1.6.1 + optional: true + + '@redis/search@1.2.0(@redis/client@1.6.1)': + dependencies: + '@redis/client': 1.6.1 + optional: true + + '@redis/time-series@1.1.0(@redis/client@1.6.1)': + dependencies: + '@redis/client': 1.6.1 + optional: true + + '@reduxjs/toolkit@2.11.2(react-redux@8.1.3(@types/react-dom@18.3.7(@types/react@18.3.26))(@types/react@18.3.26)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(redux@4.2.1))(react@18.3.1)': + dependencies: + '@standard-schema/spec': 1.0.0 + '@standard-schema/utils': 0.3.0 + immer: 11.1.4 + redux: 5.0.1 + redux-thunk: 3.1.0(redux@5.0.1) + reselect: 5.1.1 + optionalDependencies: + react: 18.3.1 + react-redux: 8.1.3(@types/react-dom@18.3.7(@types/react@18.3.26))(@types/react@18.3.26)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(redux@4.2.1) + + '@rolldown/pluginutils@1.0.0-beta.27': {} + + '@rollup/plugin-babel@5.3.1(@babel/core@7.28.4)(@types/babel__core@7.20.5)(rollup@4.45.0)': + dependencies: + '@babel/core': 7.28.4 + '@babel/helper-module-imports': 7.27.1 + '@rollup/pluginutils': 3.1.0(rollup@4.45.0) + rollup: 4.45.0 + optionalDependencies: + '@types/babel__core': 7.20.5 + transitivePeerDependencies: + - supports-color + + '@rollup/plugin-node-resolve@11.2.1(rollup@4.45.0)': + dependencies: + '@rollup/pluginutils': 3.1.0(rollup@4.45.0) + '@types/resolve': 1.17.1 + builtin-modules: 3.3.0 + deepmerge: 4.3.1 + is-module: 1.0.0 + resolve: 1.22.10 + rollup: 4.45.0 + + '@rollup/plugin-replace@2.4.2(rollup@4.45.0)': + dependencies: + '@rollup/pluginutils': 3.1.0(rollup@4.45.0) + magic-string: 0.25.9 + rollup: 4.45.0 + + '@rollup/pluginutils@3.1.0(rollup@4.45.0)': + dependencies: + '@types/estree': 0.0.39 + estree-walker: 1.0.1 + picomatch: 2.3.1 + rollup: 4.45.0 + + '@rollup/rollup-android-arm-eabi@4.45.0': + optional: true + + '@rollup/rollup-android-arm64@4.45.0': + optional: true + + '@rollup/rollup-darwin-arm64@4.45.0': + optional: true + + '@rollup/rollup-darwin-x64@4.45.0': + optional: true + + '@rollup/rollup-freebsd-arm64@4.45.0': + optional: true + + '@rollup/rollup-freebsd-x64@4.45.0': + optional: true + + '@rollup/rollup-linux-arm-gnueabihf@4.45.0': + optional: true + + '@rollup/rollup-linux-arm-musleabihf@4.45.0': + optional: true + + '@rollup/rollup-linux-arm64-gnu@4.45.0': + optional: true + + '@rollup/rollup-linux-arm64-musl@4.45.0': + optional: true + + '@rollup/rollup-linux-loongarch64-gnu@4.45.0': + optional: true + + '@rollup/rollup-linux-powerpc64le-gnu@4.45.0': + optional: true + + '@rollup/rollup-linux-riscv64-gnu@4.45.0': + optional: true + + '@rollup/rollup-linux-riscv64-musl@4.45.0': + optional: true + + '@rollup/rollup-linux-s390x-gnu@4.45.0': + optional: true + + '@rollup/rollup-linux-x64-gnu@4.45.0': + optional: true + + '@rollup/rollup-linux-x64-musl@4.45.0': + optional: true + + '@rollup/rollup-win32-arm64-msvc@4.45.0': + optional: true + + '@rollup/rollup-win32-ia32-msvc@4.45.0': + optional: true + + '@rollup/rollup-win32-x64-msvc@4.45.0': + optional: true + + '@rtsao/scc@1.1.0': {} + + '@rushstack/eslint-patch@1.13.0': {} + + '@sinclair/typebox@0.24.51': {} + + '@sinclair/typebox@0.34.41': {} + + '@sinonjs/commons@1.8.6': + dependencies: + type-detect: 4.0.8 + + '@sinonjs/fake-timers@8.1.0': + dependencies: + '@sinonjs/commons': 1.8.6 + + '@smithy/config-resolver@4.4.14': + dependencies: + '@smithy/node-config-provider': 4.3.13 + '@smithy/types': 4.14.0 + '@smithy/util-config-provider': 4.2.2 + '@smithy/util-endpoints': 3.3.4 + '@smithy/util-middleware': 4.2.13 + tslib: 2.8.1 + optional: true + + '@smithy/core@3.23.14': + dependencies: + '@smithy/protocol-http': 5.3.13 + '@smithy/types': 4.14.0 + '@smithy/url-parser': 4.2.13 + '@smithy/util-base64': 4.3.2 + '@smithy/util-body-length-browser': 4.2.2 + '@smithy/util-middleware': 4.2.13 + '@smithy/util-stream': 4.5.22 + '@smithy/util-utf8': 4.2.2 + '@smithy/uuid': 1.1.2 + tslib: 2.8.1 + optional: true + + '@smithy/credential-provider-imds@4.2.13': + dependencies: + '@smithy/node-config-provider': 4.3.13 + '@smithy/property-provider': 4.2.13 + '@smithy/types': 4.14.0 + '@smithy/url-parser': 4.2.13 + tslib: 2.8.1 + optional: true + + '@smithy/fetch-http-handler@5.3.16': + dependencies: + '@smithy/protocol-http': 5.3.13 + '@smithy/querystring-builder': 4.2.13 + '@smithy/types': 4.14.0 + '@smithy/util-base64': 4.3.2 + tslib: 2.8.1 + optional: true + + '@smithy/hash-node@4.2.13': + dependencies: + '@smithy/types': 4.14.0 + '@smithy/util-buffer-from': 4.2.2 + '@smithy/util-utf8': 4.2.2 + tslib: 2.8.1 + optional: true + + '@smithy/invalid-dependency@4.2.13': + dependencies: + '@smithy/types': 4.14.0 + tslib: 2.8.1 + optional: true + + '@smithy/is-array-buffer@2.2.0': + dependencies: + tslib: 2.8.1 + optional: true + + '@smithy/is-array-buffer@4.2.2': + dependencies: + tslib: 2.8.1 + optional: true + + '@smithy/middleware-content-length@4.2.13': + dependencies: + '@smithy/protocol-http': 5.3.13 + '@smithy/types': 4.14.0 + tslib: 2.8.1 + optional: true + + '@smithy/middleware-endpoint@4.4.29': + dependencies: + '@smithy/core': 3.23.14 + '@smithy/middleware-serde': 4.2.17 + '@smithy/node-config-provider': 4.3.13 + '@smithy/shared-ini-file-loader': 4.4.8 + '@smithy/types': 4.14.0 + '@smithy/url-parser': 4.2.13 + '@smithy/util-middleware': 4.2.13 + tslib: 2.8.1 + optional: true + + '@smithy/middleware-retry@4.5.1': + dependencies: + '@smithy/core': 3.23.14 + '@smithy/node-config-provider': 4.3.13 + '@smithy/protocol-http': 5.3.13 + '@smithy/service-error-classification': 4.2.13 + '@smithy/smithy-client': 4.12.9 + '@smithy/types': 4.14.0 + '@smithy/util-middleware': 4.2.13 + '@smithy/util-retry': 4.3.1 + '@smithy/uuid': 1.1.2 + tslib: 2.8.1 + optional: true + + '@smithy/middleware-serde@4.2.17': + dependencies: + '@smithy/core': 3.23.14 + '@smithy/protocol-http': 5.3.13 + '@smithy/types': 4.14.0 + tslib: 2.8.1 + optional: true + + '@smithy/middleware-stack@4.2.13': + dependencies: + '@smithy/types': 4.14.0 + tslib: 2.8.1 + optional: true + + '@smithy/node-config-provider@4.3.13': + dependencies: + '@smithy/property-provider': 4.2.13 + '@smithy/shared-ini-file-loader': 4.4.8 + '@smithy/types': 4.14.0 + tslib: 2.8.1 + optional: true + + '@smithy/node-http-handler@4.5.2': + dependencies: + '@smithy/protocol-http': 5.3.13 + '@smithy/querystring-builder': 4.2.13 + '@smithy/types': 4.14.0 + tslib: 2.8.1 + optional: true + + '@smithy/property-provider@4.2.13': + dependencies: + '@smithy/types': 4.14.0 + tslib: 2.8.1 + optional: true + + '@smithy/protocol-http@5.3.13': + dependencies: + '@smithy/types': 4.14.0 + tslib: 2.8.1 + optional: true + + '@smithy/querystring-builder@4.2.13': + dependencies: + '@smithy/types': 4.14.0 + '@smithy/util-uri-escape': 4.2.2 + tslib: 2.8.1 + optional: true + + '@smithy/querystring-parser@4.2.13': + dependencies: + '@smithy/types': 4.14.0 + tslib: 2.8.1 + optional: true + + '@smithy/service-error-classification@4.2.13': + dependencies: + '@smithy/types': 4.14.0 + optional: true + + '@smithy/shared-ini-file-loader@4.4.8': + dependencies: + '@smithy/types': 4.14.0 + tslib: 2.8.1 + optional: true + + '@smithy/signature-v4@5.3.13': + dependencies: + '@smithy/is-array-buffer': 4.2.2 + '@smithy/protocol-http': 5.3.13 + '@smithy/types': 4.14.0 + '@smithy/util-hex-encoding': 4.2.2 + '@smithy/util-middleware': 4.2.13 + '@smithy/util-uri-escape': 4.2.2 + '@smithy/util-utf8': 4.2.2 + tslib: 2.8.1 + optional: true + + '@smithy/smithy-client@4.12.9': + dependencies: + '@smithy/core': 3.23.14 + '@smithy/middleware-endpoint': 4.4.29 + '@smithy/middleware-stack': 4.2.13 + '@smithy/protocol-http': 5.3.13 + '@smithy/types': 4.14.0 + '@smithy/util-stream': 4.5.22 + tslib: 2.8.1 + optional: true + + '@smithy/types@4.14.0': + dependencies: + tslib: 2.8.1 + optional: true + + '@smithy/url-parser@4.2.13': + dependencies: + '@smithy/querystring-parser': 4.2.13 + '@smithy/types': 4.14.0 + tslib: 2.8.1 + optional: true + + '@smithy/util-base64@4.3.2': + dependencies: + '@smithy/util-buffer-from': 4.2.2 + '@smithy/util-utf8': 4.2.2 + tslib: 2.8.1 + optional: true + + '@smithy/util-body-length-browser@4.2.2': + dependencies: + tslib: 2.8.1 + optional: true + + '@smithy/util-body-length-node@4.2.3': + dependencies: + tslib: 2.8.1 + optional: true + + '@smithy/util-buffer-from@2.2.0': + dependencies: + '@smithy/is-array-buffer': 2.2.0 + tslib: 2.8.1 + optional: true + + '@smithy/util-buffer-from@4.2.2': + dependencies: + '@smithy/is-array-buffer': 4.2.2 + tslib: 2.8.1 + optional: true + + '@smithy/util-config-provider@4.2.2': + dependencies: + tslib: 2.8.1 + optional: true + + '@smithy/util-defaults-mode-browser@4.3.45': + dependencies: + '@smithy/property-provider': 4.2.13 + '@smithy/smithy-client': 4.12.9 + '@smithy/types': 4.14.0 + tslib: 2.8.1 + optional: true + + '@smithy/util-defaults-mode-node@4.2.49': + dependencies: + '@smithy/config-resolver': 4.4.14 + '@smithy/credential-provider-imds': 4.2.13 + '@smithy/node-config-provider': 4.3.13 + '@smithy/property-provider': 4.2.13 + '@smithy/smithy-client': 4.12.9 + '@smithy/types': 4.14.0 + tslib: 2.8.1 + optional: true + + '@smithy/util-endpoints@3.3.4': + dependencies: + '@smithy/node-config-provider': 4.3.13 + '@smithy/types': 4.14.0 + tslib: 2.8.1 + optional: true + + '@smithy/util-hex-encoding@4.2.2': + dependencies: + tslib: 2.8.1 + optional: true + + '@smithy/util-middleware@4.2.13': + dependencies: + '@smithy/types': 4.14.0 + tslib: 2.8.1 + optional: true + + '@smithy/util-retry@4.3.1': + dependencies: + '@smithy/service-error-classification': 4.2.13 + '@smithy/types': 4.14.0 + tslib: 2.8.1 + optional: true + + '@smithy/util-stream@4.5.22': + dependencies: + '@smithy/fetch-http-handler': 5.3.16 + '@smithy/node-http-handler': 4.5.2 + '@smithy/types': 4.14.0 + '@smithy/util-base64': 4.3.2 + '@smithy/util-buffer-from': 4.2.2 + '@smithy/util-hex-encoding': 4.2.2 + '@smithy/util-utf8': 4.2.2 + tslib: 2.8.1 + optional: true + + '@smithy/util-uri-escape@4.2.2': + dependencies: + tslib: 2.8.1 + optional: true + + '@smithy/util-utf8@2.3.0': + dependencies: + '@smithy/util-buffer-from': 2.2.0 + tslib: 2.8.1 + optional: true + + '@smithy/util-utf8@4.2.2': + dependencies: + '@smithy/util-buffer-from': 4.2.2 + tslib: 2.8.1 + optional: true + + '@smithy/uuid@1.1.2': + dependencies: + tslib: 2.8.1 + optional: true + + '@so-ric/colorspace@1.1.6': + dependencies: + color: 5.0.2 + text-hex: 1.0.0 + + '@socket.io/component-emitter@3.1.2': {} + + '@sqltools/formatter@1.2.5': {} + + '@standard-schema/spec@1.0.0': {} + + '@standard-schema/utils@0.3.0': {} + + '@surma/rollup-plugin-off-main-thread@2.2.3': + dependencies: + ejs: 3.1.10 + json5: 2.2.3 + magic-string: 0.25.9 + string.prototype.matchall: 4.0.12 + + '@svgr/babel-plugin-add-jsx-attribute@5.4.0': {} + + '@svgr/babel-plugin-remove-jsx-attribute@5.4.0': {} + + '@svgr/babel-plugin-remove-jsx-empty-expression@5.0.1': {} + + '@svgr/babel-plugin-replace-jsx-attribute-value@5.0.1': {} + + '@svgr/babel-plugin-svg-dynamic-title@5.4.0': {} + + '@svgr/babel-plugin-svg-em-dimensions@5.4.0': {} + + '@svgr/babel-plugin-transform-react-native-svg@5.4.0': {} + + '@svgr/babel-plugin-transform-svg-component@5.5.0': {} + + '@svgr/babel-preset@5.5.0': + dependencies: + '@svgr/babel-plugin-add-jsx-attribute': 5.4.0 + '@svgr/babel-plugin-remove-jsx-attribute': 5.4.0 + '@svgr/babel-plugin-remove-jsx-empty-expression': 5.0.1 + '@svgr/babel-plugin-replace-jsx-attribute-value': 5.0.1 + '@svgr/babel-plugin-svg-dynamic-title': 5.4.0 + '@svgr/babel-plugin-svg-em-dimensions': 5.4.0 + '@svgr/babel-plugin-transform-react-native-svg': 5.4.0 + '@svgr/babel-plugin-transform-svg-component': 5.5.0 + + '@svgr/core@5.5.0': + dependencies: + '@svgr/plugin-jsx': 5.5.0 + camelcase: 6.3.0 + cosmiconfig: 7.1.0 + transitivePeerDependencies: + - supports-color + + '@svgr/hast-util-to-babel-ast@5.5.0': + dependencies: + '@babel/types': 7.28.4 + + '@svgr/plugin-jsx@5.5.0': + dependencies: + '@babel/core': 7.28.4 + '@svgr/babel-preset': 5.5.0 + '@svgr/hast-util-to-babel-ast': 5.5.0 + svg-parser: 2.0.4 + transitivePeerDependencies: + - supports-color + + '@svgr/plugin-svgo@5.5.0': + dependencies: + cosmiconfig: 7.1.0 + deepmerge: 4.3.1 + svgo: 1.3.2 + + '@svgr/webpack@5.5.0': + dependencies: + '@babel/core': 7.28.4 + '@babel/plugin-transform-react-constant-elements': 7.27.1(@babel/core@7.28.4) + '@babel/preset-env': 7.28.3(@babel/core@7.28.4) + '@babel/preset-react': 7.27.1(@babel/core@7.28.4) + '@svgr/core': 5.5.0 + '@svgr/plugin-jsx': 5.5.0 + '@svgr/plugin-svgo': 5.5.0 + loader-utils: 2.0.4 + transitivePeerDependencies: + - supports-color + + '@tabler/icons-react@3.41.1(react@18.3.1)': + dependencies: + '@tabler/icons': 3.41.1 + react: 18.3.1 + + '@tabler/icons@3.41.1': {} + + '@testing-library/dom@9.3.4': + dependencies: + '@babel/code-frame': 7.27.1 + '@babel/runtime': 7.28.4 + '@types/aria-query': 5.0.4 + aria-query: 5.1.3 + chalk: 4.1.2 + dom-accessibility-api: 0.5.16 + lz-string: 1.5.0 + pretty-format: 27.5.1 + + '@testing-library/jest-dom@5.17.0': + dependencies: + '@adobe/css-tools': 4.4.4 + '@babel/runtime': 7.28.4 + '@types/testing-library__jest-dom': 5.14.9 + aria-query: 5.3.2 + chalk: 3.0.0 + css.escape: 1.5.1 + dom-accessibility-api: 0.5.16 + lodash: 4.17.21 + redent: 3.0.0 + + '@testing-library/react@14.3.1(@types/react@18.3.26)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + dependencies: + '@babel/runtime': 7.28.4 + '@testing-library/dom': 9.3.4 + '@types/react-dom': 18.3.7(@types/react@18.3.26) + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + transitivePeerDependencies: + - '@types/react' + + '@testing-library/user-event@12.8.3(@testing-library/dom@9.3.4)': + dependencies: + '@babel/runtime': 7.28.4 + '@testing-library/dom': 9.3.4 + + '@tootallnate/once@1.1.2': {} + + '@tootallnate/once@2.0.0': {} + + '@trysound/sax@0.2.0': {} + + '@ts-stack/markdown@1.5.0': + dependencies: + tslib: 2.8.1 + + '@tsconfig/node10@1.0.11': {} + + '@tsconfig/node12@1.0.11': {} + + '@tsconfig/node14@1.0.3': {} + + '@tsconfig/node16@1.0.4': {} + + '@types/archiver@6.0.4': + dependencies: + '@types/readdir-glob': 1.1.5 + + '@types/aria-query@5.0.4': {} + + '@types/babel__core@7.20.5': + dependencies: + '@babel/parser': 7.28.4 + '@babel/types': 7.28.4 + '@types/babel__generator': 7.27.0 + '@types/babel__template': 7.4.4 + '@types/babel__traverse': 7.28.0 + + '@types/babel__generator@7.27.0': + dependencies: + '@babel/types': 7.28.4 + + '@types/babel__template@7.4.4': + dependencies: + '@babel/parser': 7.28.4 + '@babel/types': 7.28.4 + + '@types/babel__traverse@7.28.0': + dependencies: + '@babel/types': 7.28.4 + + '@types/body-parser@1.19.6': + dependencies: + '@types/connect': 3.4.38 + '@types/node': 24.7.2 + + '@types/bonjour@3.5.13': + dependencies: + '@types/node': 24.7.2 + + '@types/connect-history-api-fallback@1.5.4': + dependencies: + '@types/express-serve-static-core': 5.1.0 + '@types/node': 24.7.2 + + '@types/connect@3.4.38': + dependencies: + '@types/node': 24.7.2 + + '@types/content-disposition@0.5.8': {} + + '@types/cors@2.8.19': + dependencies: + '@types/node': 24.7.2 + + '@types/crypto-js@4.2.2': {} + + '@types/d3-array@3.2.2': {} + + '@types/d3-axis@3.0.6': + dependencies: + '@types/d3-selection': 3.0.11 + + '@types/d3-brush@3.0.6': + dependencies: + '@types/d3-selection': 3.0.11 + + '@types/d3-chord@3.0.6': {} + + '@types/d3-color@3.1.3': {} + + '@types/d3-contour@3.0.6': + dependencies: + '@types/d3-array': 3.2.2 + '@types/geojson': 7946.0.16 + + '@types/d3-delaunay@6.0.4': {} + + '@types/d3-dispatch@3.0.7': {} + + '@types/d3-drag@3.0.7': + dependencies: + '@types/d3-selection': 3.0.11 + + '@types/d3-dsv@3.0.7': {} + + '@types/d3-ease@3.0.2': {} + + '@types/d3-fetch@3.0.7': + dependencies: + '@types/d3-dsv': 3.0.7 + + '@types/d3-force@3.0.10': {} + + '@types/d3-format@3.0.4': {} + + '@types/d3-geo@3.1.0': + dependencies: + '@types/geojson': 7946.0.16 + + '@types/d3-hierarchy@3.1.7': {} + + '@types/d3-interpolate@3.0.4': + dependencies: + '@types/d3-color': 3.1.3 + + '@types/d3-path@3.1.1': {} + + '@types/d3-polygon@3.0.2': {} + + '@types/d3-quadtree@3.0.6': {} + + '@types/d3-random@3.0.3': {} + + '@types/d3-scale-chromatic@3.1.0': {} + + '@types/d3-scale@4.0.9': + dependencies: + '@types/d3-time': 3.0.4 + + '@types/d3-selection@3.0.11': {} + + '@types/d3-shape@3.1.7': + dependencies: + '@types/d3-path': 3.1.1 + + '@types/d3-time-format@4.0.3': {} + + '@types/d3-time@3.0.4': {} + + '@types/d3-timer@3.0.2': {} + + '@types/d3-transition@3.0.9': + dependencies: + '@types/d3-selection': 3.0.11 + + '@types/d3-zoom@3.0.8': + dependencies: + '@types/d3-interpolate': 3.0.4 + '@types/d3-selection': 3.0.11 + + '@types/d3@7.4.3': + dependencies: + '@types/d3-array': 3.2.2 + '@types/d3-axis': 3.0.6 + '@types/d3-brush': 3.0.6 + '@types/d3-chord': 3.0.6 + '@types/d3-color': 3.1.3 + '@types/d3-contour': 3.0.6 + '@types/d3-delaunay': 6.0.4 + '@types/d3-dispatch': 3.0.7 + '@types/d3-drag': 3.0.7 + '@types/d3-dsv': 3.0.7 + '@types/d3-ease': 3.0.2 + '@types/d3-fetch': 3.0.7 + '@types/d3-force': 3.0.10 + '@types/d3-format': 3.0.4 + '@types/d3-geo': 3.1.0 + '@types/d3-hierarchy': 3.1.7 + '@types/d3-interpolate': 3.0.4 + '@types/d3-path': 3.1.1 + '@types/d3-polygon': 3.0.2 + '@types/d3-quadtree': 3.0.6 + '@types/d3-random': 3.0.3 + '@types/d3-scale': 4.0.9 + '@types/d3-scale-chromatic': 3.1.0 + '@types/d3-selection': 3.0.11 + '@types/d3-shape': 3.1.7 + '@types/d3-time': 3.0.4 + '@types/d3-time-format': 4.0.3 + '@types/d3-timer': 3.0.2 + '@types/d3-transition': 3.0.9 + '@types/d3-zoom': 3.0.8 + + '@types/debug@4.1.12': + dependencies: + '@types/ms': 2.1.0 + + '@types/dompurify@3.2.0': + dependencies: + dompurify: 3.3.3 + + '@types/eslint-scope@3.7.7': + dependencies: + '@types/eslint': 9.6.1 + '@types/estree': 1.0.8 + + '@types/eslint@8.56.12': + dependencies: + '@types/estree': 1.0.8 + '@types/json-schema': 7.0.15 + + '@types/eslint@9.6.1': + dependencies: + '@types/estree': 1.0.8 + '@types/json-schema': 7.0.15 + + '@types/estree@0.0.39': {} + + '@types/estree@1.0.8': {} + + '@types/express-serve-static-core@4.19.7': + dependencies: + '@types/node': 24.7.2 + '@types/qs': 6.14.0 + '@types/range-parser': 1.2.7 + '@types/send': 1.2.0 + + '@types/express-serve-static-core@5.1.0': + dependencies: + '@types/node': 24.7.2 + '@types/qs': 6.14.0 + '@types/range-parser': 1.2.7 + '@types/send': 1.2.0 + + '@types/express@4.17.23': + dependencies: + '@types/body-parser': 1.19.6 + '@types/express-serve-static-core': 4.19.7 + '@types/qs': 6.14.0 + '@types/serve-static': 1.15.9 + + '@types/geojson@7946.0.16': {} + + '@types/graceful-fs@4.1.9': + dependencies: + '@types/node': 24.7.2 + + '@types/hast@2.3.10': + dependencies: + '@types/unist': 2.0.11 + + '@types/hast@3.0.4': + dependencies: + '@types/unist': 3.0.3 + + '@types/hoist-non-react-statics@3.3.7(@types/react@18.3.26)': + dependencies: + '@types/react': 18.3.26 + hoist-non-react-statics: 3.3.2 + + '@types/html-minifier-terser@6.1.0': {} + + '@types/http-errors@2.0.5': {} + + '@types/http-proxy@1.17.16': + dependencies: + '@types/node': 24.7.2 + + '@types/istanbul-lib-coverage@2.0.6': {} + + '@types/istanbul-lib-report@3.0.3': + dependencies: + '@types/istanbul-lib-coverage': 2.0.6 + + '@types/istanbul-reports@3.0.4': + dependencies: + '@types/istanbul-lib-report': 3.0.3 + + '@types/jest@30.0.0': + dependencies: + expect: 30.2.0 + pretty-format: 30.2.0 + + '@types/json-schema@7.0.15': {} + + '@types/json5@0.0.29': {} + + '@types/katex@0.16.7': {} + + '@types/lodash@4.17.20': {} + + '@types/mathjax@0.0.37': {} + + '@types/mdast@3.0.15': + dependencies: + '@types/unist': 2.0.11 + + '@types/mime@1.3.5': {} + + '@types/ms@2.1.0': {} + + '@types/multer@1.4.13': + dependencies: + '@types/express': 4.17.23 + + '@types/node-forge@1.3.14': + dependencies: + '@types/node': 24.7.2 + + '@types/node@24.7.2': + dependencies: + undici-types: 7.14.0 + + '@types/parse-json@4.0.2': {} + + '@types/prettier@2.7.3': {} + + '@types/prop-types@15.7.15': {} + + '@types/q@1.5.8': {} + + '@types/qs@6.14.0': {} + + '@types/range-parser@1.2.7': {} + + '@types/react-dom@18.3.7(@types/react@18.3.26)': + dependencies: + '@types/react': 18.3.26 + + '@types/react-transition-group@4.4.12(@types/react@18.3.26)': + dependencies: + '@types/react': 18.3.26 + + '@types/react@18.3.26': + dependencies: + '@types/prop-types': 15.7.15 + csstype: 3.2.3 + + '@types/readdir-glob@1.1.5': + dependencies: + '@types/node': 24.7.2 + + '@types/resolve@1.17.1': + dependencies: + '@types/node': 24.7.2 + + '@types/retry@0.12.0': {} + + '@types/sanitize-html@2.16.0': + dependencies: + htmlparser2: 8.0.2 + + '@types/semver@7.7.1': {} + + '@types/send@0.17.5': + dependencies: + '@types/mime': 1.3.5 + '@types/node': 24.7.2 + + '@types/send@1.2.0': + dependencies: + '@types/node': 24.7.2 + + '@types/serve-index@1.9.4': + dependencies: + '@types/express': 4.17.23 + + '@types/serve-static@1.15.9': + dependencies: + '@types/http-errors': 2.0.5 + '@types/node': 24.7.2 + '@types/send': 0.17.5 + + '@types/sinonjs__fake-timers@8.1.1': {} + + '@types/sizzle@2.3.10': {} + + '@types/sockjs@0.3.36': + dependencies: + '@types/node': 24.7.2 + + '@types/stack-utils@2.0.3': {} + + '@types/testing-library__jest-dom@5.14.9': + dependencies: + '@types/jest': 30.0.0 + + '@types/triple-beam@1.3.5': {} + + '@types/trusted-types@2.0.7': {} + + '@types/unist@2.0.11': {} + + '@types/unist@3.0.3': {} + + '@types/use-sync-external-store@0.0.3': {} + + '@types/uuid@9.0.8': {} + + '@types/webidl-conversions@7.0.3': + optional: true + + '@types/whatwg-url@11.0.5': + dependencies: + '@types/webidl-conversions': 7.0.3 + optional: true + + '@types/ws@8.18.1': + dependencies: + '@types/node': 24.7.2 + + '@types/yargs-parser@21.0.3': {} + + '@types/yargs@16.0.9': + dependencies: + '@types/yargs-parser': 21.0.3 + + '@types/yargs@17.0.33': + dependencies: + '@types/yargs-parser': 21.0.3 + + '@types/yauzl@2.10.3': + dependencies: + '@types/node': 24.7.2 + optional: true + + '@typescript-eslint/eslint-plugin@5.62.0(@typescript-eslint/parser@5.62.0(eslint@8.57.1)(typescript@5.9.3))(eslint@8.57.1)(typescript@5.9.3)': + dependencies: + '@eslint-community/regexpp': 4.12.1 + '@typescript-eslint/parser': 5.62.0(eslint@8.57.1)(typescript@5.9.3) + '@typescript-eslint/scope-manager': 5.62.0 + '@typescript-eslint/type-utils': 5.62.0(eslint@8.57.1)(typescript@5.9.3) + '@typescript-eslint/utils': 5.62.0(eslint@8.57.1)(typescript@5.9.3) + debug: 4.4.3(supports-color@8.1.1) + eslint: 8.57.1 + graphemer: 1.4.0 + ignore: 5.3.2 + natural-compare-lite: 1.4.0 + semver: 7.7.1 + tsutils: 3.21.0(typescript@5.9.3) + optionalDependencies: + typescript: 5.9.3 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/experimental-utils@5.62.0(eslint@8.57.1)(typescript@5.9.3)': + dependencies: + '@typescript-eslint/utils': 5.62.0(eslint@8.57.1)(typescript@5.9.3) + eslint: 8.57.1 + transitivePeerDependencies: + - supports-color + - typescript + + '@typescript-eslint/parser@5.62.0(eslint@8.57.1)(typescript@5.9.3)': + dependencies: + '@typescript-eslint/scope-manager': 5.62.0 + '@typescript-eslint/types': 5.62.0 + '@typescript-eslint/typescript-estree': 5.62.0(typescript@5.9.3) + debug: 4.4.3(supports-color@8.1.1) + eslint: 8.57.1 + optionalDependencies: + typescript: 5.9.3 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/scope-manager@5.62.0': + dependencies: + '@typescript-eslint/types': 5.62.0 + '@typescript-eslint/visitor-keys': 5.62.0 + + '@typescript-eslint/type-utils@5.62.0(eslint@8.57.1)(typescript@5.9.3)': + dependencies: + '@typescript-eslint/typescript-estree': 5.62.0(typescript@5.9.3) + '@typescript-eslint/utils': 5.62.0(eslint@8.57.1)(typescript@5.9.3) + debug: 4.4.3(supports-color@8.1.1) + eslint: 8.57.1 + tsutils: 3.21.0(typescript@5.9.3) + optionalDependencies: + typescript: 5.9.3 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/types@5.62.0': {} + + '@typescript-eslint/types@7.18.0': {} + + '@typescript-eslint/typescript-estree@5.62.0(typescript@5.9.3)': + dependencies: + '@typescript-eslint/types': 5.62.0 + '@typescript-eslint/visitor-keys': 5.62.0 + debug: 4.4.3(supports-color@8.1.1) + globby: 11.1.0 + is-glob: 4.0.3 + semver: 7.7.1 + tsutils: 3.21.0(typescript@5.9.3) + optionalDependencies: + typescript: 5.9.3 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/typescript-estree@7.18.0(typescript@5.9.3)': + dependencies: + '@typescript-eslint/types': 7.18.0 + '@typescript-eslint/visitor-keys': 7.18.0 + debug: 4.4.3(supports-color@8.1.1) + globby: 11.1.0 + is-glob: 4.0.3 + minimatch: 9.0.5 + semver: 7.7.1 + ts-api-utils: 1.4.3(typescript@5.9.3) + optionalDependencies: + typescript: 5.9.3 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/utils@5.62.0(eslint@8.57.1)(typescript@5.9.3)': + dependencies: + '@eslint-community/eslint-utils': 4.9.0(eslint@8.57.1) + '@types/json-schema': 7.0.15 + '@types/semver': 7.7.1 + '@typescript-eslint/scope-manager': 5.62.0 + '@typescript-eslint/types': 5.62.0 + '@typescript-eslint/typescript-estree': 5.62.0(typescript@5.9.3) + eslint: 8.57.1 + eslint-scope: 5.1.1 + semver: 7.7.1 + transitivePeerDependencies: + - supports-color + - typescript + + '@typescript-eslint/visitor-keys@5.62.0': + dependencies: + '@typescript-eslint/types': 5.62.0 + eslint-visitor-keys: 3.4.3 + + '@typescript-eslint/visitor-keys@7.18.0': + dependencies: + '@typescript-eslint/types': 7.18.0 + eslint-visitor-keys: 3.4.3 + + '@uiw/codemirror-extensions-basic-setup@4.25.2(@codemirror/autocomplete@6.19.0)(@codemirror/commands@6.9.0)(@codemirror/language@6.11.3)(@codemirror/lint@6.9.0)(@codemirror/search@6.5.11)(@codemirror/state@6.5.2)(@codemirror/view@6.38.5)': + dependencies: + '@codemirror/autocomplete': 6.19.0 + '@codemirror/commands': 6.9.0 + '@codemirror/language': 6.11.3 + '@codemirror/lint': 6.9.0 + '@codemirror/search': 6.5.11 + '@codemirror/state': 6.5.2 + '@codemirror/view': 6.38.5 + + '@uiw/codemirror-theme-sublime@4.25.2(@codemirror/language@6.11.3)(@codemirror/state@6.5.2)(@codemirror/view@6.38.5)': + dependencies: + '@uiw/codemirror-themes': 4.25.2(@codemirror/language@6.11.3)(@codemirror/state@6.5.2)(@codemirror/view@6.38.5) + transitivePeerDependencies: + - '@codemirror/language' + - '@codemirror/state' + - '@codemirror/view' + + '@uiw/codemirror-theme-vscode@4.25.2(@codemirror/language@6.11.3)(@codemirror/state@6.5.2)(@codemirror/view@6.38.5)': + dependencies: + '@uiw/codemirror-themes': 4.25.2(@codemirror/language@6.11.3)(@codemirror/state@6.5.2)(@codemirror/view@6.38.5) + transitivePeerDependencies: + - '@codemirror/language' + - '@codemirror/state' + - '@codemirror/view' + + '@uiw/codemirror-themes@4.25.2(@codemirror/language@6.11.3)(@codemirror/state@6.5.2)(@codemirror/view@6.38.5)': + dependencies: + '@codemirror/language': 6.11.3 + '@codemirror/state': 6.5.2 + '@codemirror/view': 6.38.5 + + '@uiw/react-codemirror@4.25.2(@babel/runtime@7.28.4)(@codemirror/autocomplete@6.19.0)(@codemirror/language@6.11.3)(@codemirror/lint@6.9.0)(@codemirror/search@6.5.11)(@codemirror/state@6.5.2)(@codemirror/theme-one-dark@6.1.3)(@codemirror/view@6.38.5)(codemirror@6.0.2)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + dependencies: + '@babel/runtime': 7.28.4 + '@codemirror/commands': 6.9.0 + '@codemirror/state': 6.5.2 + '@codemirror/theme-one-dark': 6.1.3 + '@codemirror/view': 6.38.5 + '@uiw/codemirror-extensions-basic-setup': 4.25.2(@codemirror/autocomplete@6.19.0)(@codemirror/commands@6.9.0)(@codemirror/language@6.11.3)(@codemirror/lint@6.9.0)(@codemirror/search@6.5.11)(@codemirror/state@6.5.2)(@codemirror/view@6.38.5) + codemirror: 6.0.2 + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + transitivePeerDependencies: + - '@codemirror/autocomplete' + - '@codemirror/language' + - '@codemirror/lint' + - '@codemirror/search' + + '@ungap/structured-clone@1.3.0': {} + + '@vitejs/plugin-react@3.1.0(vite@4.5.14(@types/node@24.7.2)(sass@1.93.2)(terser@5.44.0))': + dependencies: + '@babel/core': 7.28.4 + '@babel/plugin-transform-react-jsx-self': 7.27.1(@babel/core@7.28.4) + '@babel/plugin-transform-react-jsx-source': 7.27.1(@babel/core@7.28.4) + magic-string: 0.27.0 + react-refresh: 0.14.2 + vite: 4.5.14(@types/node@24.7.2)(sass@1.93.2)(terser@5.44.0) + transitivePeerDependencies: + - supports-color + + '@vitejs/plugin-react@4.7.0(vite@5.4.20(@types/node@24.7.2)(sass@1.93.2)(terser@5.44.0))': + dependencies: + '@babel/core': 7.28.4 + '@babel/plugin-transform-react-jsx-self': 7.27.1(@babel/core@7.28.4) + '@babel/plugin-transform-react-jsx-source': 7.27.1(@babel/core@7.28.4) + '@rolldown/pluginutils': 1.0.0-beta.27 + '@types/babel__core': 7.20.5 + react-refresh: 0.17.0 + vite: 5.4.20(@types/node@24.7.2)(sass@1.93.2)(terser@5.44.0) + transitivePeerDependencies: + - supports-color + + '@webassemblyjs/ast@1.14.1': + dependencies: + '@webassemblyjs/helper-numbers': 1.13.2 + '@webassemblyjs/helper-wasm-bytecode': 1.13.2 + + '@webassemblyjs/floating-point-hex-parser@1.13.2': {} + + '@webassemblyjs/helper-api-error@1.13.2': {} + + '@webassemblyjs/helper-buffer@1.14.1': {} + + '@webassemblyjs/helper-numbers@1.13.2': + dependencies: + '@webassemblyjs/floating-point-hex-parser': 1.13.2 + '@webassemblyjs/helper-api-error': 1.13.2 + '@xtuc/long': 4.2.2 + + '@webassemblyjs/helper-wasm-bytecode@1.13.2': {} + + '@webassemblyjs/helper-wasm-section@1.14.1': + dependencies: + '@webassemblyjs/ast': 1.14.1 + '@webassemblyjs/helper-buffer': 1.14.1 + '@webassemblyjs/helper-wasm-bytecode': 1.13.2 + '@webassemblyjs/wasm-gen': 1.14.1 + + '@webassemblyjs/ieee754@1.13.2': + dependencies: + '@xtuc/ieee754': 1.2.0 + + '@webassemblyjs/leb128@1.13.2': + dependencies: + '@xtuc/long': 4.2.2 + + '@webassemblyjs/utf8@1.13.2': {} + + '@webassemblyjs/wasm-edit@1.14.1': + dependencies: + '@webassemblyjs/ast': 1.14.1 + '@webassemblyjs/helper-buffer': 1.14.1 + '@webassemblyjs/helper-wasm-bytecode': 1.13.2 + '@webassemblyjs/helper-wasm-section': 1.14.1 + '@webassemblyjs/wasm-gen': 1.14.1 + '@webassemblyjs/wasm-opt': 1.14.1 + '@webassemblyjs/wasm-parser': 1.14.1 + '@webassemblyjs/wast-printer': 1.14.1 + + '@webassemblyjs/wasm-gen@1.14.1': + dependencies: + '@webassemblyjs/ast': 1.14.1 + '@webassemblyjs/helper-wasm-bytecode': 1.13.2 + '@webassemblyjs/ieee754': 1.13.2 + '@webassemblyjs/leb128': 1.13.2 + '@webassemblyjs/utf8': 1.13.2 + + '@webassemblyjs/wasm-opt@1.14.1': + dependencies: + '@webassemblyjs/ast': 1.14.1 + '@webassemblyjs/helper-buffer': 1.14.1 + '@webassemblyjs/wasm-gen': 1.14.1 + '@webassemblyjs/wasm-parser': 1.14.1 + + '@webassemblyjs/wasm-parser@1.14.1': + dependencies: + '@webassemblyjs/ast': 1.14.1 + '@webassemblyjs/helper-api-error': 1.13.2 + '@webassemblyjs/helper-wasm-bytecode': 1.13.2 + '@webassemblyjs/ieee754': 1.13.2 + '@webassemblyjs/leb128': 1.13.2 + '@webassemblyjs/utf8': 1.13.2 + + '@webassemblyjs/wast-printer@1.14.1': + dependencies: + '@webassemblyjs/ast': 1.14.1 + '@xtuc/long': 4.2.2 + + '@xmldom/xmldom@0.9.8': {} + + '@xtuc/ieee754@1.2.0': {} + + '@xtuc/long@4.2.2': {} + + abab@2.0.6: {} + + abbrev@1.1.1: + optional: true + + accepts@1.3.8: + dependencies: + mime-types: 2.1.35 + negotiator: 0.6.3 + + acorn-globals@6.0.0: + dependencies: + acorn: 7.4.1 + acorn-walk: 7.2.0 + + acorn-globals@7.0.1: + dependencies: + acorn: 8.15.0 + acorn-walk: 8.3.4 + + acorn-import-phases@1.0.4(acorn@8.15.0): + dependencies: + acorn: 8.15.0 + + acorn-jsx@5.3.2(acorn@8.15.0): + dependencies: + acorn: 8.15.0 + + acorn-walk@7.2.0: {} + + acorn-walk@8.3.4: + dependencies: + acorn: 8.15.0 + + acorn@7.4.1: {} + + acorn@8.15.0: {} + + address@1.2.2: {} + + adjust-sourcemap-loader@4.0.0: + dependencies: + loader-utils: 2.0.4 + regex-parser: 2.3.1 + + agent-base@6.0.2: + dependencies: + debug: 4.4.3(supports-color@8.1.1) + transitivePeerDependencies: + - supports-color + + agent-base@7.1.4: {} + + agentkeepalive@4.6.0: + dependencies: + humanize-ms: 1.2.1 + optional: true + + aggregate-error@3.1.0: + dependencies: + clean-stack: 2.2.0 + indent-string: 4.0.0 + + ajv-formats@2.1.1(ajv@8.17.1): + optionalDependencies: + ajv: 8.17.1 + + ajv-keywords@3.5.2(ajv@6.12.6): + dependencies: + ajv: 6.12.6 + + ajv-keywords@5.1.0(ajv@8.17.1): + dependencies: + ajv: 8.17.1 + fast-deep-equal: 3.1.3 + + ajv@6.12.6: + dependencies: + fast-deep-equal: 3.1.3 + fast-json-stable-stringify: 2.1.0 + json-schema-traverse: 0.4.1 + uri-js: 4.4.1 + + ajv@8.17.1: + dependencies: + fast-deep-equal: 3.1.3 + fast-uri: 3.1.0 + json-schema-traverse: 1.0.0 + require-from-string: 2.0.2 + + ansi-align@3.0.1: + dependencies: + string-width: 4.2.3 + + ansi-colors@4.1.3: {} + + ansi-escapes@4.3.2: + dependencies: + type-fest: 0.21.3 + + ansi-escapes@5.0.0: + dependencies: + type-fest: 1.4.0 + + ansi-html-community@0.0.8: {} + + ansi-html@0.0.9: {} + + ansi-regex@5.0.1: {} + + ansi-regex@6.2.2: {} + + ansi-styles@3.2.1: + dependencies: + color-convert: 1.9.3 + + ansi-styles@4.3.0: + dependencies: + color-convert: 2.0.1 + + ansi-styles@5.2.0: {} + + ansi-styles@6.2.3: {} + + ansis@3.17.0: {} + + any-promise@1.3.0: {} + + anymatch@3.1.3: + dependencies: + normalize-path: 3.0.0 + picomatch: 2.3.1 + + app-root-path@3.1.0: {} + + append-field@1.0.0: {} + + aproba@2.1.0: + optional: true + + arch@2.2.0: {} + + archiver-utils@4.0.1: + dependencies: + glob: 10.5.0 + graceful-fs: 4.2.11 + lazystream: 1.0.1 + lodash: 4.17.21 + normalize-path: 3.0.0 + readable-stream: 3.6.2 + + archiver@6.0.2: + dependencies: + archiver-utils: 4.0.1 + async: 3.2.6 + buffer-crc32: 0.2.13 + readable-stream: 3.6.2 + readdir-glob: 1.1.3 + tar-stream: 3.1.7 + zip-stream: 5.0.2 + transitivePeerDependencies: + - bare-abort-controller + - react-native-b4a + + are-we-there-yet@2.0.0: + dependencies: + delegates: 1.0.0 + readable-stream: 3.6.2 + optional: true + + are-we-there-yet@3.0.1: + dependencies: + delegates: 1.0.0 + readable-stream: 3.6.2 + optional: true + + arg@4.1.3: {} + + arg@5.0.2: {} + + argparse@1.0.10: + dependencies: + sprintf-js: 1.0.3 + + argparse@2.0.1: {} + + aria-query@5.1.3: + dependencies: + deep-equal: 2.2.3 + + aria-query@5.3.2: {} + + array-buffer-byte-length@1.0.2: + dependencies: + call-bound: 1.0.4 + is-array-buffer: 3.0.5 + + array-flatten@1.1.1: {} + + array-includes@3.1.9: + dependencies: + call-bind: 1.0.8 + call-bound: 1.0.4 + define-properties: 1.2.1 + es-abstract: 1.24.0 + es-object-atoms: 1.1.1 + get-intrinsic: 1.3.0 + is-string: 1.1.1 + math-intrinsics: 1.1.0 + + array-union@2.1.0: {} + + array.prototype.findlast@1.2.5: + dependencies: + call-bind: 1.0.8 + define-properties: 1.2.1 + es-abstract: 1.24.0 + es-errors: 1.3.0 + es-object-atoms: 1.1.1 + es-shim-unscopables: 1.1.0 + + array.prototype.findlastindex@1.2.6: + dependencies: + call-bind: 1.0.8 + call-bound: 1.0.4 + define-properties: 1.2.1 + es-abstract: 1.24.0 + es-errors: 1.3.0 + es-object-atoms: 1.1.1 + es-shim-unscopables: 1.1.0 + + array.prototype.flat@1.3.3: + dependencies: + call-bind: 1.0.8 + define-properties: 1.2.1 + es-abstract: 1.24.0 + es-shim-unscopables: 1.1.0 + + array.prototype.flatmap@1.3.3: + dependencies: + call-bind: 1.0.8 + define-properties: 1.2.1 + es-abstract: 1.24.0 + es-shim-unscopables: 1.1.0 + + array.prototype.reduce@1.0.8: + dependencies: + call-bind: 1.0.8 + call-bound: 1.0.4 + define-properties: 1.2.1 + es-abstract: 1.24.0 + es-array-method-boxes-properly: 1.0.0 + es-errors: 1.3.0 + es-object-atoms: 1.1.1 + is-string: 1.1.1 + + array.prototype.tosorted@1.1.4: + dependencies: + call-bind: 1.0.8 + define-properties: 1.2.1 + es-abstract: 1.24.0 + es-errors: 1.3.0 + es-shim-unscopables: 1.1.0 + + arraybuffer.prototype.slice@1.0.4: + dependencies: + array-buffer-byte-length: 1.0.2 + call-bind: 1.0.8 + define-properties: 1.2.1 + es-abstract: 1.24.0 + es-errors: 1.3.0 + get-intrinsic: 1.3.0 + is-array-buffer: 3.0.5 + + asap@2.0.6: {} + + asn1@0.2.6: + dependencies: + safer-buffer: 2.1.2 + + assert-plus@1.0.0: {} + + ast-types-flow@0.0.8: {} + + astral-regex@2.0.0: {} + + async-function@1.0.0: {} + + async-mutex@0.4.1: + dependencies: + tslib: 2.8.1 + + async@3.2.6: {} + + asynckit@0.4.0: {} + + at-least-node@1.0.0: {} + + autoprefixer@10.4.21(postcss@8.5.6): + dependencies: + browserslist: 4.26.3 + caniuse-lite: 1.0.30001750 + fraction.js: 4.3.7 + normalize-range: 0.1.2 + picocolors: 1.1.1 + postcss: 8.5.6 + postcss-value-parser: 4.2.0 + + available-typed-arrays@1.0.7: + dependencies: + possible-typed-array-names: 1.1.0 + + aws-sign2@0.7.0: {} + + aws-ssl-profiles@1.1.2: {} + + aws4@1.13.2: {} + + axe-core@4.11.0: {} + + axios@1.12.0(debug@4.4.3): + dependencies: + follow-redirects: 1.15.11(debug@4.4.3) + form-data: 4.0.4 + proxy-from-env: 1.1.0 + transitivePeerDependencies: + - debug + + axobject-query@4.1.0: {} + + b4a@1.7.3: {} + + babel-jest@27.5.1(@babel/core@7.28.4): + dependencies: + '@babel/core': 7.28.4 + '@jest/transform': 27.5.1 + '@jest/types': 27.5.1 + '@types/babel__core': 7.20.5 + babel-plugin-istanbul: 6.1.1 + babel-preset-jest: 27.5.1(@babel/core@7.28.4) + chalk: 4.1.2 + graceful-fs: 4.2.11 + slash: 3.0.0 + transitivePeerDependencies: + - supports-color + + babel-loader@8.4.1(@babel/core@7.28.4)(webpack@5.102.1): + dependencies: + '@babel/core': 7.28.4 + find-cache-dir: 3.3.2 + loader-utils: 2.0.4 + make-dir: 3.1.0 + schema-utils: 2.7.1 + webpack: 5.102.1 + + babel-plugin-istanbul@6.1.1: + dependencies: + '@babel/helper-plugin-utils': 7.27.1 + '@istanbuljs/load-nyc-config': 1.1.0 + '@istanbuljs/schema': 0.1.3 + istanbul-lib-instrument: 5.2.1 + test-exclude: 6.0.0 + transitivePeerDependencies: + - supports-color + + babel-plugin-jest-hoist@27.5.1: + dependencies: + '@babel/template': 7.27.2 + '@babel/types': 7.28.4 + '@types/babel__core': 7.20.5 + '@types/babel__traverse': 7.28.0 + + babel-plugin-macros@3.1.0: + dependencies: + '@babel/runtime': 7.28.4 + cosmiconfig: 7.1.0 + resolve: 1.22.10 + + babel-plugin-named-asset-import@0.3.8(@babel/core@7.28.4): + dependencies: + '@babel/core': 7.28.4 + + babel-plugin-polyfill-corejs2@0.4.14(@babel/core@7.28.4): + dependencies: + '@babel/compat-data': 7.28.4 + '@babel/core': 7.28.4 + '@babel/helper-define-polyfill-provider': 0.6.5(@babel/core@7.28.4) + semver: 7.7.1 + transitivePeerDependencies: + - supports-color + + babel-plugin-polyfill-corejs3@0.13.0(@babel/core@7.28.4): + dependencies: + '@babel/core': 7.28.4 + '@babel/helper-define-polyfill-provider': 0.6.5(@babel/core@7.28.4) + core-js-compat: 3.46.0 + transitivePeerDependencies: + - supports-color + + babel-plugin-polyfill-regenerator@0.6.5(@babel/core@7.28.4): + dependencies: + '@babel/core': 7.28.4 + '@babel/helper-define-polyfill-provider': 0.6.5(@babel/core@7.28.4) + transitivePeerDependencies: + - supports-color + + babel-plugin-transform-react-remove-prop-types@0.4.24: {} + + babel-preset-current-node-syntax@1.2.0(@babel/core@7.28.4): + dependencies: + '@babel/core': 7.28.4 + '@babel/plugin-syntax-async-generators': 7.8.4(@babel/core@7.28.4) + '@babel/plugin-syntax-bigint': 7.8.3(@babel/core@7.28.4) + '@babel/plugin-syntax-class-properties': 7.12.13(@babel/core@7.28.4) + '@babel/plugin-syntax-class-static-block': 7.14.5(@babel/core@7.28.4) + '@babel/plugin-syntax-import-attributes': 7.27.1(@babel/core@7.28.4) + '@babel/plugin-syntax-import-meta': 7.10.4(@babel/core@7.28.4) + '@babel/plugin-syntax-json-strings': 7.8.3(@babel/core@7.28.4) + '@babel/plugin-syntax-logical-assignment-operators': 7.10.4(@babel/core@7.28.4) + '@babel/plugin-syntax-nullish-coalescing-operator': 7.8.3(@babel/core@7.28.4) + '@babel/plugin-syntax-numeric-separator': 7.10.4(@babel/core@7.28.4) + '@babel/plugin-syntax-object-rest-spread': 7.8.3(@babel/core@7.28.4) + '@babel/plugin-syntax-optional-catch-binding': 7.8.3(@babel/core@7.28.4) + '@babel/plugin-syntax-optional-chaining': 7.8.3(@babel/core@7.28.4) + '@babel/plugin-syntax-private-property-in-object': 7.14.5(@babel/core@7.28.4) + '@babel/plugin-syntax-top-level-await': 7.14.5(@babel/core@7.28.4) + + babel-preset-jest@27.5.1(@babel/core@7.28.4): + dependencies: + '@babel/core': 7.28.4 + babel-plugin-jest-hoist: 27.5.1 + babel-preset-current-node-syntax: 1.2.0(@babel/core@7.28.4) + + babel-preset-react-app@10.1.0: + dependencies: + '@babel/core': 7.28.4 + '@babel/plugin-proposal-class-properties': 7.18.6(@babel/core@7.28.4) + '@babel/plugin-proposal-decorators': 7.28.0(@babel/core@7.28.4) + '@babel/plugin-proposal-nullish-coalescing-operator': 7.18.6(@babel/core@7.28.4) + '@babel/plugin-proposal-numeric-separator': 7.18.6(@babel/core@7.28.4) + '@babel/plugin-proposal-optional-chaining': 7.21.0(@babel/core@7.28.4) + '@babel/plugin-proposal-private-methods': 7.18.6(@babel/core@7.28.4) + '@babel/plugin-proposal-private-property-in-object': 7.21.11(@babel/core@7.28.4) + '@babel/plugin-transform-flow-strip-types': 7.27.1(@babel/core@7.28.4) + '@babel/plugin-transform-react-display-name': 7.28.0(@babel/core@7.28.4) + '@babel/plugin-transform-runtime': 7.28.3(@babel/core@7.28.4) + '@babel/preset-env': 7.28.3(@babel/core@7.28.4) + '@babel/preset-react': 7.27.1(@babel/core@7.28.4) + '@babel/preset-typescript': 7.18.6(@babel/core@7.28.4) + '@babel/runtime': 7.28.4 + babel-plugin-macros: 3.1.0 + babel-plugin-transform-react-remove-prop-types: 0.4.24 + transitivePeerDependencies: + - supports-color + + bail@2.0.2: {} + + balanced-match@1.0.2: {} + + bare-events@2.8.0: {} + + bare-fs@4.4.10: + dependencies: + bare-events: 2.8.0 + bare-path: 3.0.0 + bare-stream: 2.7.0(bare-events@2.8.0) + bare-url: 2.2.2 + fast-fifo: 1.3.2 + transitivePeerDependencies: + - bare-abort-controller + - react-native-b4a + optional: true + + bare-os@3.6.2: + optional: true + + bare-path@3.0.0: + dependencies: + bare-os: 3.6.2 + optional: true + + bare-stream@2.7.0(bare-events@2.8.0): + dependencies: + streamx: 2.23.0 + optionalDependencies: + bare-events: 2.8.0 + transitivePeerDependencies: + - bare-abort-controller + - react-native-b4a + optional: true + + bare-url@2.2.2: + dependencies: + bare-path: 3.0.0 + optional: true + + base16@1.0.0: {} + + base64-js@1.5.1: {} + + base64id@2.0.0: {} + + baseline-browser-mapping@2.8.16: {} + + basic-auth@2.0.1: + dependencies: + safe-buffer: 5.1.2 + + batch@0.6.1: {} + + bcrypt-pbkdf@1.0.2: + dependencies: + tweetnacl: 0.14.5 + + bfj@7.1.0: + dependencies: + bluebird: 3.7.2 + check-types: 11.2.3 + hoopy: 0.1.4 + jsonpath: 1.1.1 + tryer: 1.0.1 + + big-integer@1.6.52: {} + + big.js@5.2.2: {} + + binary-extensions@2.3.0: {} + + bindings@1.5.0: + dependencies: + file-uri-to-path: 1.0.0 + + blob-util@2.0.2: {} + + bluebird@3.7.2: {} + + body-parser@2.0.2: + dependencies: + bytes: 3.1.2 + content-type: 1.0.5 + debug: 3.1.0 + destroy: 1.2.0 + http-errors: 2.0.0 + iconv-lite: 0.5.2 + on-finished: 2.4.1 + qs: 6.13.0 + raw-body: 3.0.1 + type-is: 1.6.18 + transitivePeerDependencies: + - supports-color + + bonjour-service@1.3.0: + dependencies: + fast-deep-equal: 3.1.3 + multicast-dns: 7.2.5 + + boolbase@1.0.0: {} + + bowser@2.12.1: + optional: true + + boxen@7.1.1: + dependencies: + ansi-align: 3.0.1 + camelcase: 7.0.1 + chalk: 5.6.2 + cli-boxes: 3.0.0 + string-width: 5.1.2 + type-fest: 2.19.0 + widest-line: 4.0.1 + wrap-ansi: 8.1.0 + + bplist-parser@0.2.0: + dependencies: + big-integer: 1.6.52 + + brace-expansion@1.1.12: + dependencies: + balanced-match: 1.0.2 + concat-map: 0.0.1 + + brace-expansion@2.0.2: + dependencies: + balanced-match: 1.0.2 + + braces@3.0.3: + dependencies: + fill-range: 7.1.1 + + browser-process-hrtime@1.0.0: {} + + browserslist@4.26.3: + dependencies: + baseline-browser-mapping: 2.8.16 + caniuse-lite: 1.0.30001750 + electron-to-chromium: 1.5.234 + node-releases: 2.0.23 + update-browserslist-db: 1.1.3(browserslist@4.26.3) + + bser@2.1.1: + dependencies: + node-int64: 0.4.0 + + bson@6.10.4: + optional: true + + buffer-crc32@0.2.13: {} + + buffer-from@1.1.2: {} + + buffer@5.7.1: + dependencies: + base64-js: 1.5.1 + ieee754: 1.2.1 + + buffer@6.0.3: + dependencies: + base64-js: 1.5.1 + ieee754: 1.2.1 + + bufferutil@4.0.9: + dependencies: + node-gyp-build: 4.8.4 + optional: true + + builtin-modules@3.3.0: {} + + bundle-name@3.0.0: + dependencies: + run-applescript: 5.0.0 + + busboy@1.6.0: + dependencies: + streamsearch: 1.1.0 + + bytes@3.1.2: {} + + cacache@15.3.0: + dependencies: + '@npmcli/fs': 1.1.1 + '@npmcli/move-file': 1.1.2 + chownr: 2.0.0 + fs-minipass: 2.1.0 + glob: 10.5.0 + infer-owner: 1.0.4 + lru-cache: 6.0.0 + minipass: 3.3.6 + minipass-collect: 1.0.2 + minipass-flush: 1.0.5 + minipass-pipeline: 1.2.4 + mkdirp: 1.0.4 + p-map: 4.0.0 + promise-inflight: 1.0.1 + rimraf: 3.0.2 + ssri: 8.0.1 + tar: 6.2.1 + unique-filename: 1.1.1 + transitivePeerDependencies: + - bluebird + optional: true + + cachedir@2.4.0: {} + + call-bind-apply-helpers@1.0.2: + dependencies: + es-errors: 1.3.0 + function-bind: 1.1.2 + + call-bind@1.0.8: + dependencies: + call-bind-apply-helpers: 1.0.2 + es-define-property: 1.0.1 + get-intrinsic: 1.3.0 + set-function-length: 1.2.2 + + call-bound@1.0.4: + dependencies: + call-bind-apply-helpers: 1.0.2 + get-intrinsic: 1.3.0 + + callsites@3.1.0: {} + + camel-case@4.1.2: + dependencies: + pascal-case: 3.1.2 + tslib: 2.8.1 + + camelcase-css@2.0.1: {} + + camelcase@5.3.1: {} + + camelcase@6.3.0: {} + + camelcase@7.0.1: {} + + camelize@1.0.1: + optional: true + + caniuse-api@3.0.0: + dependencies: + browserslist: 4.26.3 + caniuse-lite: 1.0.30001750 + lodash.memoize: 4.1.2 + lodash.uniq: 4.5.0 + + caniuse-lite@1.0.30001750: {} + + canvas@2.11.2(encoding@0.1.13): + dependencies: + '@mapbox/node-pre-gyp': 1.0.11(encoding@0.1.13) + nan: 2.23.0 + simple-get: 3.1.1 + transitivePeerDependencies: + - encoding + - supports-color + optional: true + + case-sensitive-paths-webpack-plugin@2.4.0: {} + + caseless@0.12.0: {} + + ccount@2.0.1: {} + + chalk@2.4.2: + dependencies: + ansi-styles: 3.2.1 + escape-string-regexp: 1.0.5 + supports-color: 5.5.0 + + chalk@3.0.0: + dependencies: + ansi-styles: 4.3.0 + supports-color: 7.2.0 + + chalk@4.1.2: + dependencies: + ansi-styles: 4.3.0 + supports-color: 7.2.0 + + chalk@5.3.0: {} + + chalk@5.6.2: {} + + char-regex@1.0.2: {} + + char-regex@2.0.2: {} + + character-entities-legacy@1.1.4: {} + + character-entities@1.2.4: {} + + character-entities@2.0.2: {} + + character-reference-invalid@1.1.4: {} + + check-more-types@2.24.0: {} + + check-types@11.2.3: {} + + chokidar@3.6.0: + dependencies: + anymatch: 3.1.3 + braces: 3.0.3 + glob-parent: 6.0.2 + is-binary-path: 2.1.0 + is-glob: 4.0.3 + normalize-path: 3.0.0 + readdirp: 3.6.0 + optionalDependencies: + fsevents: 2.3.3 + + chokidar@4.0.3: + dependencies: + readdirp: 4.1.2 + + chownr@2.0.0: {} + + chrome-trace-event@1.0.4: {} + + ci-info@3.9.0: {} + + ci-info@4.3.1: {} + + cjs-module-lexer@1.4.3: {} + + classcat@5.0.5: {} + + classnames@2.5.1: {} + + clean-css@5.3.3: + dependencies: + source-map: 0.6.1 + + clean-stack@2.2.0: {} + + cli-boxes@3.0.0: {} + + cli-cursor@3.1.0: + dependencies: + restore-cursor: 3.1.0 + + cli-cursor@4.0.0: + dependencies: + restore-cursor: 4.0.0 + + cli-table3@0.6.5: + dependencies: + string-width: 4.2.3 + optionalDependencies: + '@colors/colors': 1.5.0 + + cli-truncate@2.1.0: + dependencies: + slice-ansi: 3.0.0 + string-width: 4.2.3 + + cli-truncate@3.1.0: + dependencies: + slice-ansi: 5.0.0 + string-width: 5.1.2 + + cliui@7.0.4: + dependencies: + string-width: 4.2.3 + strip-ansi: 6.0.1 + wrap-ansi: 7.0.0 + + cliui@8.0.1: + dependencies: + string-width: 4.2.3 + strip-ansi: 6.0.1 + wrap-ansi: 7.0.0 + + clsx@1.2.1: {} + + clsx@2.1.1: {} + + cluster-key-slot@1.1.2: + optional: true + + co@4.6.0: {} + + coa@2.0.2: + dependencies: + '@types/q': 1.5.8 + chalk: 2.4.2 + q: 1.5.1 + + codemirror@6.0.2: + dependencies: + '@codemirror/autocomplete': 6.19.0 + '@codemirror/commands': 6.9.0 + '@codemirror/language': 6.11.3 + '@codemirror/lint': 6.9.0 + '@codemirror/search': 6.5.11 + '@codemirror/state': 6.5.2 + '@codemirror/view': 6.38.5 + + collect-v8-coverage@1.0.2: {} + + color-convert@1.9.3: + dependencies: + color-name: 1.1.3 + + color-convert@2.0.1: + dependencies: + color-name: 1.1.4 + + color-convert@3.1.2: + dependencies: + color-name: 2.0.2 + + color-name@1.1.3: {} + + color-name@1.1.4: {} + + color-name@2.0.2: {} + + color-string@2.1.2: + dependencies: + color-name: 2.0.2 + + color-support@1.1.3: + optional: true + + color@5.0.2: + dependencies: + color-convert: 3.1.2 + color-string: 2.1.2 + + colord@2.9.3: {} + + colorette@2.0.20: {} + + combined-stream@1.0.8: + dependencies: + delayed-stream: 1.0.0 + + comma-separated-tokens@1.0.8: {} + + comma-separated-tokens@2.0.3: {} + + commander@11.0.0: {} + + commander@13.1.0: {} + + commander@2.20.3: {} + + commander@4.1.1: {} + + commander@6.2.1: {} + + commander@7.2.0: {} + + commander@8.3.0: {} + + commander@9.5.0: {} + + common-tags@1.8.2: {} + + commondir@1.0.1: {} + + component-register@0.8.8: {} + + compress-commons@5.0.3: + dependencies: + crc-32: 1.2.2 + crc32-stream: 5.0.1 + normalize-path: 3.0.0 + readable-stream: 3.6.2 + + compressible@2.0.18: + dependencies: + mime-db: 1.54.0 + + compression@1.8.1: + dependencies: + bytes: 3.1.2 + compressible: 2.0.18 + debug: 2.6.9 + negotiator: 0.6.4 + on-headers: 1.1.0 + safe-buffer: 5.2.1 + vary: 1.1.2 + transitivePeerDependencies: + - supports-color + + concat-map@0.0.1: {} + + concat-stream@1.6.2: + dependencies: + buffer-from: 1.1.2 + inherits: 2.0.4 + readable-stream: 2.3.8 + typedarray: 0.0.6 + + concurrently@7.6.0: + dependencies: + chalk: 4.1.2 + date-fns: 2.30.0 + lodash: 4.17.21 + rxjs: 7.8.2 + shell-quote: 1.8.3 + spawn-command: 0.0.2 + supports-color: 8.1.1 + tree-kill: 1.2.2 + yargs: 17.7.2 + + confusing-browser-globals@1.0.11: {} + + connect-history-api-fallback@2.0.0: {} + + console-control-strings@1.1.0: + optional: true + + content-disposition@0.5.4: + dependencies: + safe-buffer: 5.2.1 + + content-type@1.0.5: {} + + convert-source-map@1.9.0: {} + + convert-source-map@2.0.0: {} + + cookie-signature@1.0.6: {} + + cookie@0.7.1: {} + + cookie@0.7.2: {} + + core-js-compat@3.46.0: + dependencies: + browserslist: 4.26.3 + + core-js-pure@3.46.0: {} + + core-js@3.46.0: {} + + core-util-is@1.0.2: {} + + core-util-is@1.0.3: {} + + cors@2.8.5: + dependencies: + object-assign: 4.1.1 + vary: 1.1.2 + + cosmiconfig@6.0.0: + dependencies: + '@types/parse-json': 4.0.2 + import-fresh: 3.3.1 + parse-json: 5.2.0 + path-type: 4.0.0 + yaml: 1.10.2 + + cosmiconfig@7.1.0: + dependencies: + '@types/parse-json': 4.0.2 + import-fresh: 3.3.1 + parse-json: 5.2.0 + path-type: 4.0.0 + yaml: 1.10.2 + + crc-32@1.2.2: {} + + crc32-stream@5.0.1: + dependencies: + crc-32: 1.2.2 + readable-stream: 3.6.2 + + create-require@1.1.1: {} + + crelt@1.0.6: {} + + cross-env@7.0.3: + dependencies: + cross-spawn: 7.0.6 + + cross-fetch@3.2.0(encoding@0.1.13): + dependencies: + node-fetch: 2.7.0(encoding@0.1.13) + transitivePeerDependencies: + - encoding + + cross-spawn-async@2.2.5: + dependencies: + lru-cache: 4.1.5 + which: 1.3.1 + + cross-spawn@7.0.6: + dependencies: + path-key: 3.1.1 + shebang-command: 2.0.0 + which: 2.0.2 + + crypto-js@4.2.0: {} + + crypto-random-string@2.0.0: {} + + css-blank-pseudo@3.0.3(postcss@8.5.6): + dependencies: + postcss: 8.5.6 + postcss-selector-parser: 6.1.2 + + css-color-keywords@1.0.0: + optional: true + + css-declaration-sorter@6.4.1(postcss@8.5.6): + dependencies: + postcss: 8.5.6 + + css-has-pseudo@3.0.4(postcss@8.5.6): + dependencies: + postcss: 8.5.6 + postcss-selector-parser: 6.1.2 + + css-loader@6.11.0(webpack@5.102.1): + dependencies: + icss-utils: 5.1.0(postcss@8.5.6) + postcss: 8.5.6 + postcss-modules-extract-imports: 3.1.0(postcss@8.5.6) + postcss-modules-local-by-default: 4.2.0(postcss@8.5.6) + postcss-modules-scope: 3.2.1(postcss@8.5.6) + postcss-modules-values: 4.0.0(postcss@8.5.6) + postcss-value-parser: 4.2.0 + semver: 7.7.1 + optionalDependencies: + webpack: 5.102.1 + + css-minimizer-webpack-plugin@3.4.1(webpack@5.102.1): + dependencies: + cssnano: 5.1.15(postcss@8.5.6) + jest-worker: 27.5.1 + postcss: 8.5.6 + schema-utils: 4.3.3 + serialize-javascript: 6.0.2 + source-map: 0.6.1 + webpack: 5.102.1 + + css-prefers-color-scheme@6.0.3(postcss@8.5.6): + dependencies: + postcss: 8.5.6 + + css-select-base-adapter@0.1.1: {} + + css-select@2.1.0: + dependencies: + boolbase: 1.0.0 + css-what: 3.4.2 + domutils: 1.7.0 + nth-check: 2.1.1 + + css-select@4.3.0: + dependencies: + boolbase: 1.0.0 + css-what: 6.2.2 + domhandler: 4.3.1 + domutils: 2.8.0 + nth-check: 2.1.1 + + css-to-react-native@3.2.0: + dependencies: + camelize: 1.0.1 + css-color-keywords: 1.0.0 + postcss-value-parser: 4.2.0 + optional: true + + css-tree@1.0.0-alpha.37: + dependencies: + mdn-data: 2.0.4 + source-map: 0.6.1 + + css-tree@1.1.3: + dependencies: + mdn-data: 2.0.14 + source-map: 0.6.1 + + css-what@3.4.2: {} + + css-what@6.2.2: {} + + css.escape@1.5.1: {} + + cssdb@7.11.2: {} + + cssesc@3.0.0: {} + + cssnano-preset-default@5.2.14(postcss@8.5.6): + dependencies: + css-declaration-sorter: 6.4.1(postcss@8.5.6) + cssnano-utils: 3.1.0(postcss@8.5.6) + postcss: 8.5.6 + postcss-calc: 8.2.4(postcss@8.5.6) + postcss-colormin: 5.3.1(postcss@8.5.6) + postcss-convert-values: 5.1.3(postcss@8.5.6) + postcss-discard-comments: 5.1.2(postcss@8.5.6) + postcss-discard-duplicates: 5.1.0(postcss@8.5.6) + postcss-discard-empty: 5.1.1(postcss@8.5.6) + postcss-discard-overridden: 5.1.0(postcss@8.5.6) + postcss-merge-longhand: 5.1.7(postcss@8.5.6) + postcss-merge-rules: 5.1.4(postcss@8.5.6) + postcss-minify-font-values: 5.1.0(postcss@8.5.6) + postcss-minify-gradients: 5.1.1(postcss@8.5.6) + postcss-minify-params: 5.1.4(postcss@8.5.6) + postcss-minify-selectors: 5.2.1(postcss@8.5.6) + postcss-normalize-charset: 5.1.0(postcss@8.5.6) + postcss-normalize-display-values: 5.1.0(postcss@8.5.6) + postcss-normalize-positions: 5.1.1(postcss@8.5.6) + postcss-normalize-repeat-style: 5.1.1(postcss@8.5.6) + postcss-normalize-string: 5.1.0(postcss@8.5.6) + postcss-normalize-timing-functions: 5.1.0(postcss@8.5.6) + postcss-normalize-unicode: 5.1.1(postcss@8.5.6) + postcss-normalize-url: 5.1.0(postcss@8.5.6) + postcss-normalize-whitespace: 5.1.1(postcss@8.5.6) + postcss-ordered-values: 5.1.3(postcss@8.5.6) + postcss-reduce-initial: 5.1.2(postcss@8.5.6) + postcss-reduce-transforms: 5.1.0(postcss@8.5.6) + postcss-svgo: 5.1.0(postcss@8.5.6) + postcss-unique-selectors: 5.1.1(postcss@8.5.6) + + cssnano-utils@3.1.0(postcss@8.5.6): + dependencies: + postcss: 8.5.6 + + cssnano@5.1.15(postcss@8.5.6): + dependencies: + cssnano-preset-default: 5.2.14(postcss@8.5.6) + lilconfig: 2.1.0 + postcss: 8.5.6 + yaml: 1.10.2 + + csso@4.2.0: + dependencies: + css-tree: 1.1.3 + + cssom@0.3.8: {} + + cssom@0.4.4: {} + + cssom@0.5.0: {} + + cssstyle@2.3.0: + dependencies: + cssom: 0.3.8 + + csstype@3.1.3: {} + + csstype@3.2.3: {} + + cypress@13.17.0: + dependencies: + '@cypress/request': 3.0.9 + '@cypress/xvfb': 1.2.4(supports-color@8.1.1) + '@types/sinonjs__fake-timers': 8.1.1 + '@types/sizzle': 2.3.10 + arch: 2.2.0 + blob-util: 2.0.2 + bluebird: 3.7.2 + buffer: 5.7.1 + cachedir: 2.4.0 + chalk: 4.1.2 + check-more-types: 2.24.0 + ci-info: 4.3.1 + cli-cursor: 3.1.0 + cli-table3: 0.6.5 + commander: 6.2.1 + common-tags: 1.8.2 + dayjs: 1.11.18 + debug: 4.4.3(supports-color@8.1.1) + enquirer: 2.4.1 + eventemitter2: 6.4.7 + execa: 4.1.0 + executable: 4.1.1 + extract-zip: 2.0.1(supports-color@8.1.1) + figures: 3.2.0 + fs-extra: 9.1.0 + getos: 3.2.1 + is-installed-globally: 0.4.0 + lazy-ass: 1.6.0 + listr2: 3.14.0(enquirer@2.4.1) + lodash: 4.17.21 + log-symbols: 4.1.0 + minimist: 1.2.8 + ospath: 1.2.2 + pretty-bytes: 5.6.0 + process: 0.11.10 + proxy-from-env: 1.0.0 + request-progress: 3.0.0 + semver: 7.7.1 + supports-color: 8.1.1 + tmp: 0.2.5 + tree-kill: 1.2.2 + untildify: 4.0.0 + yauzl: 2.10.0 + + d3-array@3.2.4: + dependencies: + internmap: 2.0.3 + + d3-color@3.1.0: {} + + d3-dispatch@3.0.1: {} + + d3-drag@3.0.0: + dependencies: + d3-dispatch: 3.0.1 + d3-selection: 3.0.0 + + d3-ease@3.0.1: {} + + d3-format@3.1.2: {} + + d3-interpolate@3.0.1: + dependencies: + d3-color: 3.1.0 + + d3-path@3.1.0: {} + + d3-scale@4.0.2: + dependencies: + d3-array: 3.2.4 + d3-format: 3.1.2 + d3-interpolate: 3.0.1 + d3-time: 3.1.0 + d3-time-format: 4.1.0 + + d3-selection@3.0.0: {} + + d3-shape@3.2.0: + dependencies: + d3-path: 3.1.0 + + d3-time-format@4.1.0: + dependencies: + d3-time: 3.1.0 + + d3-time@3.1.0: + dependencies: + d3-array: 3.2.4 + + d3-timer@3.0.1: {} + + d3-transition@3.0.1(d3-selection@3.0.0): + dependencies: + d3-color: 3.1.0 + d3-dispatch: 3.0.1 + d3-ease: 3.0.1 + d3-interpolate: 3.0.1 + d3-selection: 3.0.0 + d3-timer: 3.0.1 + + d3-zoom@3.0.0: + dependencies: + d3-dispatch: 3.0.1 + d3-drag: 3.0.0 + d3-interpolate: 3.0.1 + d3-selection: 3.0.0 + d3-transition: 3.0.1(d3-selection@3.0.0) + + damerau-levenshtein@1.0.8: {} + + dashdash@1.14.1: + dependencies: + assert-plus: 1.0.0 + + data-uri-to-buffer@4.0.1: {} + + data-urls@2.0.0: + dependencies: + abab: 2.0.6 + whatwg-mimetype: 2.3.0 + whatwg-url: 8.7.0 + + data-urls@3.0.2: + dependencies: + abab: 2.0.6 + whatwg-mimetype: 3.0.0 + whatwg-url: 11.0.0 + + data-view-buffer@1.0.2: + dependencies: + call-bound: 1.0.4 + es-errors: 1.3.0 + is-data-view: 1.0.2 + + data-view-byte-length@1.0.2: + dependencies: + call-bound: 1.0.4 + es-errors: 1.3.0 + is-data-view: 1.0.2 + + data-view-byte-offset@1.0.1: + dependencies: + call-bound: 1.0.4 + es-errors: 1.3.0 + is-data-view: 1.0.2 + + date-fns@2.30.0: + dependencies: + '@babel/runtime': 7.28.4 + + dayjs@1.11.18: {} + + debug@2.6.9: + dependencies: + ms: 2.0.0 + + debug@3.1.0: + dependencies: + ms: 2.0.0 + + debug@3.2.7(supports-color@5.5.0): + dependencies: + ms: 2.1.3 + optionalDependencies: + supports-color: 5.5.0 + + debug@3.2.7(supports-color@8.1.1): + dependencies: + ms: 2.1.3 + optionalDependencies: + supports-color: 8.1.1 + + debug@4.3.4: + dependencies: + ms: 2.1.2 + + debug@4.3.7: + dependencies: + ms: 2.1.3 + + debug@4.4.3(supports-color@8.1.1): + dependencies: + ms: 2.1.3 + optionalDependencies: + supports-color: 8.1.1 + + decimal.js-light@2.5.1: {} + + decimal.js@10.6.0: {} + + decode-named-character-reference@1.2.0: + dependencies: + character-entities: 2.0.2 + + decode-uri-component@0.4.1: {} + + decompress-response@4.2.1: + dependencies: + mimic-response: 2.1.0 + optional: true + + decompress-response@6.0.0: + dependencies: + mimic-response: 3.1.0 + + dedent@0.7.0: {} + + dedent@1.7.0(babel-plugin-macros@3.1.0): + optionalDependencies: + babel-plugin-macros: 3.1.0 + + deep-equal@2.2.3: + dependencies: + array-buffer-byte-length: 1.0.2 + call-bind: 1.0.8 + es-get-iterator: 1.1.3 + get-intrinsic: 1.3.0 + is-arguments: 1.2.0 + is-array-buffer: 3.0.5 + is-date-object: 1.1.0 + is-regex: 1.2.1 + is-shared-array-buffer: 1.0.4 + isarray: 2.0.5 + object-is: 1.1.6 + object-keys: 1.1.1 + object.assign: 4.1.7 + regexp.prototype.flags: 1.5.4 + side-channel: 1.1.0 + which-boxed-primitive: 1.1.1 + which-collection: 1.0.2 + which-typed-array: 1.1.19 + + deep-extend@0.6.0: {} + + deep-is@0.1.4: {} + + deepmerge@2.2.1: {} + + deepmerge@4.3.1: {} + + default-browser-id@3.0.0: + dependencies: + bplist-parser: 0.2.0 + untildify: 4.0.0 + + default-browser@3.1.0: + dependencies: + bundle-name: 3.0.0 + default-browser-id: 3.0.0 + execa: 5.1.1 + xdg-default-browser: 2.1.0 + + default-gateway@6.0.3: + dependencies: + execa: 5.1.1 + + define-data-property@1.1.4: + dependencies: + es-define-property: 1.0.1 + es-errors: 1.3.0 + gopd: 1.2.0 + + define-lazy-prop@2.0.0: {} + + define-properties@1.2.1: + dependencies: + define-data-property: 1.1.4 + has-property-descriptors: 1.0.2 + object-keys: 1.1.1 + + delayed-stream@1.0.0: {} + + delegates@1.0.0: + optional: true + + denque@2.1.0: {} + + depd@1.1.2: {} + + depd@2.0.0: {} + + dequal@2.0.3: {} + + destroy@1.2.0: {} + + detect-libc@1.0.3: + optional: true + + detect-libc@2.1.2: {} + + detect-newline@3.1.0: {} + + detect-node@2.1.0: {} + + detect-port-alt@1.1.6: + dependencies: + address: 1.2.2 + debug: 2.6.9 + transitivePeerDependencies: + - supports-color + + device-detector-js@3.0.3: {} + + devlop@1.1.0: + dependencies: + dequal: 2.0.3 + + didyoumean@1.2.2: {} + + diff-sequences@27.5.1: {} + + diff@4.0.2: {} + + diff@5.2.0: {} + + dir-glob@3.0.1: + dependencies: + path-type: 4.0.0 + + dlv@1.1.3: {} + + dns-packet@5.6.1: + dependencies: + '@leichtgewicht/ip-codec': 2.0.5 + + doctrine@2.1.0: + dependencies: + esutils: 2.0.3 + + doctrine@3.0.0: + dependencies: + esutils: 2.0.3 + + dom-accessibility-api@0.5.16: {} + + dom-converter@0.2.0: + dependencies: + utila: 0.4.0 + + dom-helpers@5.2.1: + dependencies: + '@babel/runtime': 7.28.4 + csstype: 3.1.3 + + dom-serializer@0.2.2: + dependencies: + domelementtype: 2.3.0 + entities: 2.2.0 + + dom-serializer@1.4.1: + dependencies: + domelementtype: 2.3.0 + domhandler: 4.3.1 + entities: 2.2.0 + + dom-serializer@2.0.0: + dependencies: + domelementtype: 2.3.0 + domhandler: 5.0.3 + entities: 4.5.0 + + domelementtype@1.3.1: {} + + domelementtype@2.3.0: {} + + domexception@2.0.1: + dependencies: + webidl-conversions: 5.0.0 + + domexception@4.0.0: + dependencies: + webidl-conversions: 7.0.0 + + domhandler@4.3.1: + dependencies: + domelementtype: 2.3.0 + + domhandler@5.0.3: + dependencies: + domelementtype: 2.3.0 + + dompurify@3.3.3: + optionalDependencies: + '@types/trusted-types': 2.0.7 + + domutils@1.7.0: + dependencies: + dom-serializer: 0.2.2 + domelementtype: 1.3.1 + + domutils@2.8.0: + dependencies: + dom-serializer: 1.4.1 + domelementtype: 2.3.0 + domhandler: 4.3.1 + + domutils@3.2.2: + dependencies: + dom-serializer: 2.0.0 + domelementtype: 2.3.0 + domhandler: 5.0.3 + + dot-case@3.0.4: + dependencies: + no-case: 3.0.4 + tslib: 2.8.1 + + dotenv-expand@5.1.0: {} + + dotenv@10.0.0: {} + + dotenv@16.6.1: {} + + dunder-proto@1.0.1: + dependencies: + call-bind-apply-helpers: 1.0.2 + es-errors: 1.3.0 + gopd: 1.2.0 + + duplexer@0.1.2: {} + + eastasianwidth@0.2.0: {} + + ecc-jsbn@0.1.2: + dependencies: + jsbn: 0.1.1 + safer-buffer: 2.1.2 + + ee-first@1.1.1: {} + + ejs@3.1.10: + dependencies: + jake: 10.9.4 + + electron-to-chromium@1.5.234: {} + + emittery@0.10.2: {} + + emittery@0.8.1: {} + + emoji-regex@8.0.0: {} + + emoji-regex@9.2.2: {} + + emojis-list@3.0.0: {} + + enabled@2.0.0: {} + + encodeurl@1.0.2: {} + + encodeurl@2.0.0: {} + + encoding@0.1.13: + dependencies: + iconv-lite: 0.6.3 + optional: true + + end-of-stream@1.4.5: + dependencies: + once: 1.4.0 + + engine.io-client@6.6.3(bufferutil@4.0.9)(utf-8-validate@6.0.5): + dependencies: + '@socket.io/component-emitter': 3.1.2 + debug: 4.3.7 + engine.io-parser: 5.2.3 + ws: 8.18.3(bufferutil@4.0.9)(utf-8-validate@6.0.5) + xmlhttprequest-ssl: 2.1.2 + transitivePeerDependencies: + - bufferutil + - supports-color + - utf-8-validate + + engine.io-parser@5.2.3: {} + + engine.io@6.6.4(bufferutil@4.0.9)(utf-8-validate@6.0.5): + dependencies: + '@types/cors': 2.8.19 + '@types/node': 24.7.2 + accepts: 1.3.8 + base64id: 2.0.0 + cookie: 0.7.2 + cors: 2.8.5 + debug: 4.3.7 + engine.io-parser: 5.2.3 + ws: 8.18.3(bufferutil@4.0.9)(utf-8-validate@6.0.5) + transitivePeerDependencies: + - bufferutil + - supports-color + - utf-8-validate + + enhanced-resolve@5.18.3: + dependencies: + graceful-fs: 4.2.11 + tapable: 2.3.0 + + enquirer@2.4.1: + dependencies: + ansi-colors: 4.1.3 + strip-ansi: 6.0.1 + + entities@2.2.0: {} + + entities@4.5.0: {} + + entities@6.0.1: {} + + env-paths@2.2.1: + optional: true + + err-code@2.0.3: + optional: true + + error-ex@1.3.4: + dependencies: + is-arrayish: 0.2.1 + + error-stack-parser@2.1.4: + dependencies: + stackframe: 1.3.4 + + es-abstract@1.24.0: + dependencies: + array-buffer-byte-length: 1.0.2 + arraybuffer.prototype.slice: 1.0.4 + available-typed-arrays: 1.0.7 + call-bind: 1.0.8 + call-bound: 1.0.4 + data-view-buffer: 1.0.2 + data-view-byte-length: 1.0.2 + data-view-byte-offset: 1.0.1 + es-define-property: 1.0.1 + es-errors: 1.3.0 + es-object-atoms: 1.1.1 + es-set-tostringtag: 2.1.0 + es-to-primitive: 1.3.0 + function.prototype.name: 1.1.8 + get-intrinsic: 1.3.0 + get-proto: 1.0.1 + get-symbol-description: 1.1.0 + globalthis: 1.0.4 + gopd: 1.2.0 + has-property-descriptors: 1.0.2 + has-proto: 1.2.0 + has-symbols: 1.1.0 + hasown: 2.0.2 + internal-slot: 1.1.0 + is-array-buffer: 3.0.5 + is-callable: 1.2.7 + is-data-view: 1.0.2 + is-negative-zero: 2.0.3 + is-regex: 1.2.1 + is-set: 2.0.3 + is-shared-array-buffer: 1.0.4 + is-string: 1.1.1 + is-typed-array: 1.1.15 + is-weakref: 1.1.1 + math-intrinsics: 1.1.0 + object-inspect: 1.13.4 + object-keys: 1.1.1 + object.assign: 4.1.7 + own-keys: 1.0.1 + regexp.prototype.flags: 1.5.4 + safe-array-concat: 1.1.3 + safe-push-apply: 1.0.0 + safe-regex-test: 1.1.0 + set-proto: 1.0.0 + stop-iteration-iterator: 1.1.0 + string.prototype.trim: 1.2.10 + string.prototype.trimend: 1.0.9 + string.prototype.trimstart: 1.0.8 + typed-array-buffer: 1.0.3 + typed-array-byte-length: 1.0.3 + typed-array-byte-offset: 1.0.4 + typed-array-length: 1.0.7 + unbox-primitive: 1.1.0 + which-typed-array: 1.1.19 + + es-array-method-boxes-properly@1.0.0: {} + + es-define-property@1.0.1: {} + + es-errors@1.3.0: {} + + es-get-iterator@1.1.3: + dependencies: + call-bind: 1.0.8 + get-intrinsic: 1.3.0 + has-symbols: 1.1.0 + is-arguments: 1.2.0 + is-map: 2.0.3 + is-set: 2.0.3 + is-string: 1.1.1 + isarray: 2.0.5 + stop-iteration-iterator: 1.1.0 + + es-iterator-helpers@1.2.1: + dependencies: + call-bind: 1.0.8 + call-bound: 1.0.4 + define-properties: 1.2.1 + es-abstract: 1.24.0 + es-errors: 1.3.0 + es-set-tostringtag: 2.1.0 + function-bind: 1.1.2 + get-intrinsic: 1.3.0 + globalthis: 1.0.4 + gopd: 1.2.0 + has-property-descriptors: 1.0.2 + has-proto: 1.2.0 + has-symbols: 1.1.0 + internal-slot: 1.1.0 + iterator.prototype: 1.1.5 + safe-array-concat: 1.1.3 + + es-module-lexer@1.7.0: {} + + es-object-atoms@1.1.1: + dependencies: + es-errors: 1.3.0 + + es-set-tostringtag@2.1.0: + dependencies: + es-errors: 1.3.0 + get-intrinsic: 1.3.0 + has-tostringtag: 1.0.2 + hasown: 2.0.2 + + es-shim-unscopables@1.1.0: + dependencies: + hasown: 2.0.2 + + es-to-primitive@1.3.0: + dependencies: + is-callable: 1.2.7 + is-date-object: 1.1.0 + is-symbol: 1.1.1 + + esbuild@0.27.1: + optionalDependencies: + '@esbuild/aix-ppc64': 0.27.1 + '@esbuild/android-arm': 0.27.1 + '@esbuild/android-arm64': 0.27.1 + '@esbuild/android-x64': 0.27.1 + '@esbuild/darwin-arm64': 0.27.1 + '@esbuild/darwin-x64': 0.27.1 + '@esbuild/freebsd-arm64': 0.27.1 + '@esbuild/freebsd-x64': 0.27.1 + '@esbuild/linux-arm': 0.27.1 + '@esbuild/linux-arm64': 0.27.1 + '@esbuild/linux-ia32': 0.27.1 + '@esbuild/linux-loong64': 0.27.1 + '@esbuild/linux-mips64el': 0.27.1 + '@esbuild/linux-ppc64': 0.27.1 + '@esbuild/linux-riscv64': 0.27.1 + '@esbuild/linux-s390x': 0.27.1 + '@esbuild/linux-x64': 0.27.1 + '@esbuild/netbsd-arm64': 0.27.1 + '@esbuild/netbsd-x64': 0.27.1 + '@esbuild/openbsd-arm64': 0.27.1 + '@esbuild/openbsd-x64': 0.27.1 + '@esbuild/openharmony-arm64': 0.27.1 + '@esbuild/sunos-x64': 0.27.1 + '@esbuild/win32-arm64': 0.27.1 + '@esbuild/win32-ia32': 0.27.1 + '@esbuild/win32-x64': 0.27.1 + + escalade@3.2.0: {} + + escape-html@1.0.3: {} + + escape-string-regexp@1.0.5: {} + + escape-string-regexp@2.0.0: {} + + escape-string-regexp@4.0.0: {} + + escape-string-regexp@5.0.0: {} + + escodegen@1.14.3: + dependencies: + esprima: 4.0.1 + estraverse: 4.3.0 + esutils: 2.0.3 + optionator: 0.8.3 + optionalDependencies: + source-map: 0.6.1 + + escodegen@2.1.0: + dependencies: + esprima: 4.0.1 + estraverse: 5.3.0 + esutils: 2.0.3 + optionalDependencies: + source-map: 0.6.1 + + eslint-config-prettier@8.10.2(eslint@8.57.1): + dependencies: + eslint: 8.57.1 + + eslint-config-react-app@7.0.1(@babel/plugin-syntax-flow@7.27.1(@babel/core@7.28.4))(@babel/plugin-transform-react-jsx@7.27.1(@babel/core@7.28.4))(eslint@8.57.1)(jest@27.5.1(bufferutil@4.0.9)(canvas@2.11.2(encoding@0.1.13))(ts-node@10.9.2(@types/node@24.7.2)(typescript@5.9.3))(utf-8-validate@6.0.5))(typescript@5.9.3): + dependencies: + '@babel/core': 7.28.4 + '@babel/eslint-parser': 7.28.4(@babel/core@7.28.4)(eslint@8.57.1) + '@rushstack/eslint-patch': 1.13.0 + '@typescript-eslint/eslint-plugin': 5.62.0(@typescript-eslint/parser@5.62.0(eslint@8.57.1)(typescript@5.9.3))(eslint@8.57.1)(typescript@5.9.3) + '@typescript-eslint/parser': 5.62.0(eslint@8.57.1)(typescript@5.9.3) + babel-preset-react-app: 10.1.0 + confusing-browser-globals: 1.0.11 + eslint: 8.57.1 + eslint-plugin-flowtype: 8.0.3(@babel/plugin-syntax-flow@7.27.1(@babel/core@7.28.4))(@babel/plugin-transform-react-jsx@7.27.1(@babel/core@7.28.4))(eslint@8.57.1) + eslint-plugin-import: 2.32.0(@typescript-eslint/parser@5.62.0(eslint@8.57.1)(typescript@5.9.3))(eslint@8.57.1) + eslint-plugin-jest: 25.7.0(@typescript-eslint/eslint-plugin@5.62.0(@typescript-eslint/parser@5.62.0(eslint@8.57.1)(typescript@5.9.3))(eslint@8.57.1)(typescript@5.9.3))(eslint@8.57.1)(jest@27.5.1(bufferutil@4.0.9)(canvas@2.11.2(encoding@0.1.13))(ts-node@10.9.2(@types/node@24.7.2)(typescript@5.9.3))(utf-8-validate@6.0.5))(typescript@5.9.3) + eslint-plugin-jsx-a11y: 6.10.2(eslint@8.57.1) + eslint-plugin-react: 7.37.5(eslint@8.57.1) + eslint-plugin-react-hooks: 4.6.2(eslint@8.57.1) + eslint-plugin-testing-library: 5.11.1(eslint@8.57.1)(typescript@5.9.3) + optionalDependencies: + typescript: 5.9.3 + transitivePeerDependencies: + - '@babel/plugin-syntax-flow' + - '@babel/plugin-transform-react-jsx' + - eslint-import-resolver-typescript + - eslint-import-resolver-webpack + - jest + - supports-color + + eslint-import-resolver-node@0.3.9: + dependencies: + debug: 3.2.7(supports-color@5.5.0) + is-core-module: 2.16.1 + resolve: 1.22.10 + transitivePeerDependencies: + - supports-color + + eslint-module-utils@2.12.1(@typescript-eslint/parser@5.62.0(eslint@8.57.1)(typescript@5.9.3))(eslint-import-resolver-node@0.3.9)(eslint@8.57.1): + dependencies: + debug: 3.2.7(supports-color@5.5.0) + optionalDependencies: + '@typescript-eslint/parser': 5.62.0(eslint@8.57.1)(typescript@5.9.3) + eslint: 8.57.1 + eslint-import-resolver-node: 0.3.9 + transitivePeerDependencies: + - supports-color + + eslint-plugin-flowtype@8.0.3(@babel/plugin-syntax-flow@7.27.1(@babel/core@7.28.4))(@babel/plugin-transform-react-jsx@7.27.1(@babel/core@7.28.4))(eslint@8.57.1): + dependencies: + '@babel/plugin-syntax-flow': 7.27.1(@babel/core@7.28.4) + '@babel/plugin-transform-react-jsx': 7.27.1(@babel/core@7.28.4) + eslint: 8.57.1 + lodash: 4.17.21 + string-natural-compare: 3.0.1 + + eslint-plugin-import@2.32.0(@typescript-eslint/parser@5.62.0(eslint@8.57.1)(typescript@5.9.3))(eslint@8.57.1): + dependencies: + '@rtsao/scc': 1.1.0 + array-includes: 3.1.9 + array.prototype.findlastindex: 1.2.6 + array.prototype.flat: 1.3.3 + array.prototype.flatmap: 1.3.3 + debug: 3.2.7(supports-color@5.5.0) + doctrine: 2.1.0 + eslint: 8.57.1 + eslint-import-resolver-node: 0.3.9 + eslint-module-utils: 2.12.1(@typescript-eslint/parser@5.62.0(eslint@8.57.1)(typescript@5.9.3))(eslint-import-resolver-node@0.3.9)(eslint@8.57.1) + hasown: 2.0.2 + is-core-module: 2.16.1 + is-glob: 4.0.3 + minimatch: 3.1.2 + object.fromentries: 2.0.8 + object.groupby: 1.0.3 + object.values: 1.2.1 + semver: 7.7.1 + string.prototype.trimend: 1.0.9 + tsconfig-paths: 3.15.0 + optionalDependencies: + '@typescript-eslint/parser': 5.62.0(eslint@8.57.1)(typescript@5.9.3) + transitivePeerDependencies: + - eslint-import-resolver-typescript + - eslint-import-resolver-webpack + - supports-color + + eslint-plugin-jest@25.7.0(@typescript-eslint/eslint-plugin@5.62.0(@typescript-eslint/parser@5.62.0(eslint@8.57.1)(typescript@5.9.3))(eslint@8.57.1)(typescript@5.9.3))(eslint@8.57.1)(jest@27.5.1(bufferutil@4.0.9)(canvas@2.11.2(encoding@0.1.13))(ts-node@10.9.2(@types/node@24.7.2)(typescript@5.9.3))(utf-8-validate@6.0.5))(typescript@5.9.3): + dependencies: + '@typescript-eslint/experimental-utils': 5.62.0(eslint@8.57.1)(typescript@5.9.3) + eslint: 8.57.1 + optionalDependencies: + '@typescript-eslint/eslint-plugin': 5.62.0(@typescript-eslint/parser@5.62.0(eslint@8.57.1)(typescript@5.9.3))(eslint@8.57.1)(typescript@5.9.3) + jest: 27.5.1(bufferutil@4.0.9)(canvas@2.11.2(encoding@0.1.13))(ts-node@10.9.2(@types/node@24.7.2)(typescript@5.9.3))(utf-8-validate@6.0.5) + transitivePeerDependencies: + - supports-color + - typescript + + eslint-plugin-jsx-a11y@6.10.2(eslint@8.57.1): + dependencies: + aria-query: 5.3.2 + array-includes: 3.1.9 + array.prototype.flatmap: 1.3.3 + ast-types-flow: 0.0.8 + axe-core: 4.11.0 + axobject-query: 4.1.0 + damerau-levenshtein: 1.0.8 + emoji-regex: 9.2.2 + eslint: 8.57.1 + hasown: 2.0.2 + jsx-ast-utils: 3.3.5 + language-tags: 1.0.9 + minimatch: 3.1.2 + object.fromentries: 2.0.8 + safe-regex-test: 1.1.0 + string.prototype.includes: 2.0.1 + + eslint-plugin-markdown@3.0.1(eslint@8.57.1): + dependencies: + eslint: 8.57.1 + mdast-util-from-markdown: 0.8.5 + transitivePeerDependencies: + - supports-color + + eslint-plugin-prettier@3.4.1(eslint-config-prettier@8.10.2(eslint@8.57.1))(eslint@8.57.1)(prettier@2.8.8): + dependencies: + eslint: 8.57.1 + prettier: 2.8.8 + prettier-linter-helpers: 1.0.0 + optionalDependencies: + eslint-config-prettier: 8.10.2(eslint@8.57.1) + + eslint-plugin-react-hooks@4.6.2(eslint@8.57.1): + dependencies: + eslint: 8.57.1 + + eslint-plugin-react@7.37.5(eslint@8.57.1): + dependencies: + array-includes: 3.1.9 + array.prototype.findlast: 1.2.5 + array.prototype.flatmap: 1.3.3 + array.prototype.tosorted: 1.1.4 + doctrine: 2.1.0 + es-iterator-helpers: 1.2.1 + eslint: 8.57.1 + estraverse: 5.3.0 + hasown: 2.0.2 + jsx-ast-utils: 3.3.5 + minimatch: 3.1.2 + object.entries: 1.1.9 + object.fromentries: 2.0.8 + object.values: 1.2.1 + prop-types: 15.8.1 + resolve: 2.0.0-next.5 + semver: 7.7.1 + string.prototype.matchall: 4.0.12 + string.prototype.repeat: 1.0.0 + + eslint-plugin-testing-library@5.11.1(eslint@8.57.1)(typescript@5.9.3): + dependencies: + '@typescript-eslint/utils': 5.62.0(eslint@8.57.1)(typescript@5.9.3) + eslint: 8.57.1 + transitivePeerDependencies: + - supports-color + - typescript + + eslint-plugin-unused-imports@2.0.0(@typescript-eslint/eslint-plugin@5.62.0(@typescript-eslint/parser@5.62.0(eslint@8.57.1)(typescript@5.9.3))(eslint@8.57.1)(typescript@5.9.3))(eslint@8.57.1): + dependencies: + eslint: 8.57.1 + eslint-rule-composer: 0.3.0 + optionalDependencies: + '@typescript-eslint/eslint-plugin': 5.62.0(@typescript-eslint/parser@5.62.0(eslint@8.57.1)(typescript@5.9.3))(eslint@8.57.1)(typescript@5.9.3) + + eslint-rule-composer@0.3.0: {} + + eslint-scope@5.1.1: + dependencies: + esrecurse: 4.3.0 + estraverse: 4.3.0 + + eslint-scope@7.2.2: + dependencies: + esrecurse: 4.3.0 + estraverse: 5.3.0 + + eslint-visitor-keys@2.1.0: {} + + eslint-visitor-keys@3.4.3: {} + + eslint-webpack-plugin@3.2.0(eslint@8.57.1)(webpack@5.102.1): + dependencies: + '@types/eslint': 8.56.12 + eslint: 8.57.1 + jest-worker: 28.1.3 + micromatch: 4.0.8 + normalize-path: 3.0.0 + schema-utils: 4.3.3 + webpack: 5.102.1 + + eslint@8.57.1: + dependencies: + '@eslint-community/eslint-utils': 4.9.0(eslint@8.57.1) + '@eslint-community/regexpp': 4.12.1 + '@eslint/eslintrc': 2.1.4 + '@eslint/js': 8.57.1 + '@humanwhocodes/config-array': 0.13.0 + '@humanwhocodes/module-importer': 1.0.1 + '@nodelib/fs.walk': 1.2.8 + '@ungap/structured-clone': 1.3.0 + ajv: 6.12.6 + chalk: 4.1.2 + cross-spawn: 7.0.6 + debug: 4.4.3(supports-color@8.1.1) + doctrine: 3.0.0 + escape-string-regexp: 4.0.0 + eslint-scope: 7.2.2 + eslint-visitor-keys: 3.4.3 + espree: 9.6.1 + esquery: 1.6.0 + esutils: 2.0.3 + fast-deep-equal: 3.1.3 + file-entry-cache: 6.0.1 + find-up: 5.0.0 + glob-parent: 6.0.2 + globals: 13.24.0 + graphemer: 1.4.0 + ignore: 5.3.2 + imurmurhash: 0.1.4 + is-glob: 4.0.3 + is-path-inside: 3.0.3 + js-yaml: 4.1.0 + json-stable-stringify-without-jsonify: 1.0.1 + levn: 0.4.1 + lodash.merge: 4.6.2 + minimatch: 3.1.2 + natural-compare: 1.4.0 + optionator: 0.9.4 + strip-ansi: 6.0.1 + text-table: 0.2.0 + transitivePeerDependencies: + - supports-color + + esm@3.2.25: {} + + espree@9.6.1: + dependencies: + acorn: 8.15.0 + acorn-jsx: 5.3.2(acorn@8.15.0) + eslint-visitor-keys: 3.4.3 + + esprima@1.2.2: {} + + esprima@4.0.1: {} + + esquery@1.6.0: + dependencies: + estraverse: 5.3.0 + + esrecurse@4.3.0: + dependencies: + estraverse: 5.3.0 + + estraverse@4.3.0: {} + + estraverse@5.3.0: {} + + estree-walker@1.0.1: {} + + esutils@2.0.3: {} + + etag@1.8.1: {} + + event-stream@3.3.4: + dependencies: + duplexer: 0.1.2 + from: 0.1.7 + map-stream: 0.1.0 + pause-stream: 0.0.11 + split: 0.3.3 + stream-combiner: 0.0.4 + through: 2.3.8 + + eventemitter2@6.4.7: {} + + eventemitter3@4.0.7: {} + + eventemitter3@5.0.1: {} + + events-universal@1.0.1: + dependencies: + bare-events: 2.8.0 + transitivePeerDependencies: + - bare-abort-controller + + events@3.3.0: {} + + execa@0.2.2: + dependencies: + cross-spawn-async: 2.2.5 + npm-run-path: 1.0.0 + object-assign: 4.1.1 + path-key: 1.0.0 + strip-eof: 1.0.0 + + execa@4.1.0: + dependencies: + cross-spawn: 7.0.6 + get-stream: 5.2.0 + human-signals: 1.1.1 + is-stream: 2.0.1 + merge-stream: 2.0.0 + npm-run-path: 4.0.1 + onetime: 5.1.2 + signal-exit: 3.0.7 + strip-final-newline: 2.0.0 + + execa@5.1.1: + dependencies: + cross-spawn: 7.0.6 + get-stream: 6.0.1 + human-signals: 2.1.0 + is-stream: 2.0.1 + merge-stream: 2.0.0 + npm-run-path: 4.0.1 + onetime: 5.1.2 + signal-exit: 3.0.7 + strip-final-newline: 2.0.0 + + execa@7.2.0: + dependencies: + cross-spawn: 7.0.6 + get-stream: 6.0.1 + human-signals: 4.3.1 + is-stream: 3.0.0 + merge-stream: 2.0.0 + npm-run-path: 5.3.0 + onetime: 6.0.0 + signal-exit: 3.0.7 + strip-final-newline: 3.0.0 + + executable@4.1.1: + dependencies: + pify: 2.3.0 + + exit@0.1.2: {} + + expand-template@2.0.3: {} + + expect@27.5.1: + dependencies: + '@jest/types': 27.5.1 + jest-get-type: 27.5.1 + jest-matcher-utils: 27.5.1 + jest-message-util: 27.5.1 + + expect@30.2.0: + dependencies: + '@jest/expect-utils': 30.2.0 + '@jest/get-type': 30.1.0 + jest-matcher-utils: 30.2.0 + jest-message-util: 30.2.0 + jest-mock: 30.2.0 + jest-util: 30.2.0 + + express-basic-auth@1.2.1: + dependencies: + basic-auth: 2.0.1 + + express-rate-limit@6.11.2(express@4.21.2): + dependencies: + express: 4.21.2 + + express@4.21.2: + dependencies: + accepts: 1.3.8 + array-flatten: 1.1.1 + body-parser: 2.0.2 + content-disposition: 0.5.4 + content-type: 1.0.5 + cookie: 0.7.1 + cookie-signature: 1.0.6 + debug: 2.6.9 + depd: 2.0.0 + encodeurl: 2.0.0 + escape-html: 1.0.3 + etag: 1.8.1 + finalhandler: 1.3.1 + fresh: 0.5.2 + http-errors: 2.0.0 + merge-descriptors: 1.0.3 + methods: 1.1.2 + on-finished: 2.4.1 + parseurl: 1.3.3 + path-to-regexp: 0.1.12 + proxy-addr: 2.0.7 + qs: 6.13.0 + range-parser: 1.2.1 + safe-buffer: 5.2.1 + send: 0.19.0 + serve-static: 1.16.2 + setprototypeof: 1.2.0 + statuses: 2.0.1 + type-is: 1.6.18 + utils-merge: 1.0.1 + vary: 1.1.2 + transitivePeerDependencies: + - supports-color + + extend@3.0.2: {} + + extract-zip@2.0.1(supports-color@8.1.1): + dependencies: + debug: 4.4.3(supports-color@8.1.1) + get-stream: 5.2.0 + yauzl: 2.10.0 + optionalDependencies: + '@types/yauzl': 2.10.3 + transitivePeerDependencies: + - supports-color + + extsprintf@1.3.0: {} + + fast-deep-equal@3.1.3: {} + + fast-diff@1.3.0: {} + + fast-equals@5.4.0: {} + + fast-fifo@1.3.2: {} + + fast-glob@3.3.3: + dependencies: + '@nodelib/fs.stat': 2.0.5 + '@nodelib/fs.walk': 1.2.8 + glob-parent: 6.0.2 + merge2: 1.4.1 + micromatch: 4.0.8 + + fast-json-stable-stringify@2.1.0: {} + + fast-levenshtein@2.0.6: {} + + fast-uri@3.1.0: {} + + fast-xml-parser@5.2.5: + dependencies: + strnum: 2.2.3 + optional: true + + fastq@1.19.1: + dependencies: + reusify: 1.1.0 + + fault@1.0.4: + dependencies: + format: 0.2.2 + + faye-websocket@0.11.4: + dependencies: + websocket-driver: 0.7.4 + + fb-watchman@2.0.2: + dependencies: + bser: 2.1.1 + + fbemitter@3.0.0(encoding@0.1.13): + dependencies: + fbjs: 3.0.5(encoding@0.1.13) + transitivePeerDependencies: + - encoding + + fbjs-css-vars@1.0.2: {} + + fbjs@3.0.5(encoding@0.1.13): + dependencies: + cross-fetch: 3.2.0(encoding@0.1.13) + fbjs-css-vars: 1.0.2 + loose-envify: 1.4.0 + object-assign: 4.1.1 + promise: 7.3.1 + setimmediate: 1.0.5 + ua-parser-js: 1.0.41 + transitivePeerDependencies: + - encoding + + fd-slicer@1.1.0: + dependencies: + pend: 1.2.0 + + fecha@4.2.3: {} + + fetch-blob@3.2.0: + dependencies: + node-domexception: 1.0.0 + web-streams-polyfill: 3.3.3 + + figures@3.2.0: + dependencies: + escape-string-regexp: 1.0.5 + + file-entry-cache@6.0.1: + dependencies: + flat-cache: 3.2.0 + + file-loader@6.2.0(webpack@5.102.1): + dependencies: + loader-utils: 2.0.4 + schema-utils: 3.3.0 + webpack: 5.102.1 + + file-stream-rotator@0.6.1: + dependencies: + moment: 2.30.1 + + file-uri-to-path@1.0.0: {} + + filelist@1.0.4: + dependencies: + minimatch: 5.1.6 + + filesize@8.0.7: {} + + fill-range@7.1.1: + dependencies: + to-regex-range: 5.0.1 + + filter-obj@5.1.0: {} + + finalhandler@1.3.1: + dependencies: + debug: 2.6.9 + encodeurl: 2.0.0 + escape-html: 1.0.3 + on-finished: 2.4.1 + parseurl: 1.3.3 + statuses: 2.0.1 + unpipe: 1.0.0 + transitivePeerDependencies: + - supports-color + + find-cache-dir@3.3.2: + dependencies: + commondir: 1.0.1 + make-dir: 3.1.0 + pkg-dir: 4.2.0 + + find-root@1.1.0: {} + + find-up@3.0.0: + dependencies: + locate-path: 3.0.0 + + find-up@4.1.0: + dependencies: + locate-path: 5.0.0 + path-exists: 4.0.0 + + find-up@5.0.0: + dependencies: + locate-path: 6.0.0 + path-exists: 4.0.0 + + flat-cache@3.2.0: + dependencies: + flatted: 3.3.3 + keyv: 4.5.4 + rimraf: 3.0.2 + + flatted@3.3.3: {} + + flowise-embed-react@3.1.5(@types/node@24.7.2)(flowise-embed@3.1.5)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.93.2)(terser@5.44.0)(typescript@5.9.3): + dependencies: + '@ladle/react': 2.5.1(@types/node@24.7.2)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.93.2)(terser@5.44.0)(typescript@5.9.3) + flowise-embed: 3.1.5 + react: 18.3.1 + transitivePeerDependencies: + - '@types/node' + - less + - lightningcss + - react-dom + - sass + - stylus + - sugarss + - supports-color + - terser + - typescript + + flowise-embed@3.1.5: + dependencies: + '@babel/core': 7.28.4 + '@microsoft/fetch-event-source': 2.0.1 + '@ts-stack/markdown': 1.5.0 + '@types/dompurify': 3.2.0 + axios: 1.12.0(debug@4.4.3) + cors: 2.8.5 + cross-env: 7.0.3 + device-detector-js: 3.0.3 + dompurify: 3.3.3 + dotenv: 16.6.1 + express: 4.21.2 + form-data: 4.0.4 + lodash: 4.17.21 + multer: 1.4.5-lts.2 + node-fetch: 3.3.2 + prettier: 3.6.2 + solid-element: 1.7.0(solid-js@1.9.7) + solid-js: 1.9.7 + zod: 3.25.76 + transitivePeerDependencies: + - debug + - supports-color + + flowise-react-json-view@1.21.7(@types/react@18.3.26)(encoding@0.1.13)(react-dom@18.3.1(react@18.3.1))(react@18.3.1): + dependencies: + flux: 4.0.4(encoding@0.1.13)(react@18.3.1) + react: 18.3.1 + react-base16-styling: 0.6.0 + react-dom: 18.3.1(react@18.3.1) + react-lifecycles-compat: 3.0.4 + react-textarea-autosize: 8.5.9(@types/react@18.3.26)(react@18.3.1) + transitivePeerDependencies: + - '@types/react' + - encoding + + flux@4.0.4(encoding@0.1.13)(react@18.3.1): + dependencies: + fbemitter: 3.0.0(encoding@0.1.13) + fbjs: 3.0.5(encoding@0.1.13) + react: 18.3.1 + transitivePeerDependencies: + - encoding + + fn.name@1.1.0: {} + + follow-redirects@1.15.11(debug@4.4.3): + optionalDependencies: + debug: 4.4.3(supports-color@8.1.1) + + for-each@0.3.5: + dependencies: + is-callable: 1.2.7 + + foreground-child@3.3.1: + dependencies: + cross-spawn: 7.0.6 + signal-exit: 4.1.0 + + forever-agent@0.6.1: {} + + fork-ts-checker-webpack-plugin@6.5.3(eslint@8.57.1)(typescript@5.9.3)(webpack@5.102.1): + dependencies: + '@babel/code-frame': 7.27.1 + '@types/json-schema': 7.0.15 + chalk: 4.1.2 + chokidar: 3.6.0 + cosmiconfig: 6.0.0 + deepmerge: 4.3.1 + fs-extra: 9.1.0 + glob: 10.5.0 + memfs: 3.5.3 + minimatch: 3.1.2 + schema-utils: 2.7.0 + semver: 7.7.1 + tapable: 1.1.3 + typescript: 5.9.3 + webpack: 5.102.1 + optionalDependencies: + eslint: 8.57.1 + + form-data@4.0.4: + dependencies: + asynckit: 0.4.0 + combined-stream: 1.0.8 + es-set-tostringtag: 2.1.0 + hasown: 2.0.2 + mime-types: 2.1.35 + + format@0.2.2: {} + + formdata-polyfill@4.0.10: + dependencies: + fetch-blob: 3.2.0 + + formik@2.4.6(@types/react@18.3.26)(react@18.3.1): + dependencies: + '@types/hoist-non-react-statics': 3.3.7(@types/react@18.3.26) + deepmerge: 2.2.1 + hoist-non-react-statics: 3.3.2 + lodash: 4.17.21 + lodash-es: 4.17.21 + react: 18.3.1 + react-fast-compare: 2.0.4 + tiny-warning: 1.0.3 + tslib: 2.8.1 + transitivePeerDependencies: + - '@types/react' + + forwarded@0.2.0: {} + + fraction.js@4.3.7: {} + + framer-motion@4.1.17(react-dom@18.3.1(react@18.3.1))(react@18.3.1): + dependencies: + framesync: 5.3.0 + hey-listen: 1.0.8 + popmotion: 9.3.6 + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + style-value-types: 4.1.4 + tslib: 2.8.1 + optionalDependencies: + '@emotion/is-prop-valid': 0.8.8 + + framesync@5.3.0: + dependencies: + tslib: 2.8.1 + + fresh@0.5.2: {} + + from@0.1.7: {} + + fs-extra@10.1.0: + dependencies: + graceful-fs: 4.2.11 + jsonfile: 6.2.0 + universalify: 2.0.1 + + fs-extra@9.1.0: + dependencies: + at-least-node: 1.0.0 + graceful-fs: 4.2.11 + jsonfile: 6.2.0 + universalify: 2.0.1 + + fs-minipass@2.1.0: + dependencies: + minipass: 3.3.6 + + fs-monkey@1.1.0: {} + + fsevents@2.3.3: + optional: true + + function-bind@1.1.2: {} + + function.prototype.name@1.1.8: + dependencies: + call-bind: 1.0.8 + call-bound: 1.0.4 + define-properties: 1.2.1 + functions-have-names: 1.2.3 + hasown: 2.0.2 + is-callable: 1.2.7 + + functions-have-names@1.2.3: {} + + gauge@3.0.2: + dependencies: + aproba: 2.1.0 + color-support: 1.1.3 + console-control-strings: 1.1.0 + has-unicode: 2.0.1 + object-assign: 4.1.1 + signal-exit: 3.0.7 + string-width: 4.2.3 + strip-ansi: 6.0.1 + wide-align: 1.1.5 + optional: true + + gauge@4.0.4: + dependencies: + aproba: 2.1.0 + color-support: 1.1.3 + console-control-strings: 1.1.0 + has-unicode: 2.0.1 + signal-exit: 3.0.7 + string-width: 4.2.3 + strip-ansi: 6.0.1 + wide-align: 1.1.5 + optional: true + + generate-function@2.3.1: + dependencies: + is-property: 1.0.2 + + generator-function@2.0.1: {} + + generic-pool@3.9.0: + optional: true + + gensync@1.0.0-beta.2: {} + + get-caller-file@2.0.5: {} + + get-intrinsic@1.3.0: + dependencies: + call-bind-apply-helpers: 1.0.2 + es-define-property: 1.0.1 + es-errors: 1.3.0 + es-object-atoms: 1.1.1 + function-bind: 1.1.2 + get-proto: 1.0.1 + gopd: 1.2.0 + has-symbols: 1.1.0 + hasown: 2.0.2 + math-intrinsics: 1.1.0 + + get-own-enumerable-property-symbols@3.0.2: {} + + get-package-type@0.1.0: {} + + get-port@6.1.2: {} + + get-proto@1.0.1: + dependencies: + dunder-proto: 1.0.1 + es-object-atoms: 1.1.1 + + get-stream@5.2.0: + dependencies: + pump: 3.0.3 + + get-stream@6.0.1: {} + + get-symbol-description@1.1.0: + dependencies: + call-bound: 1.0.4 + es-errors: 1.3.0 + get-intrinsic: 1.3.0 + + get-them-args@1.3.2: {} + + getos@3.2.1: + dependencies: + async: 3.2.6 + + getpass@0.1.7: + dependencies: + assert-plus: 1.0.0 + + github-from-package@0.0.0: {} + + glob-parent@6.0.2: + dependencies: + is-glob: 4.0.3 + + glob-to-regex.js@1.2.0(tslib@2.8.1): + dependencies: + tslib: 2.8.1 + + glob-to-regexp@0.4.1: {} + + glob@10.5.0: + dependencies: + foreground-child: 3.3.1 + jackspeak: 3.4.3 + minimatch: 9.0.5 + minipass: 7.1.2 + package-json-from-dist: 1.0.1 + path-scurry: 1.11.1 + + global-dirs@3.0.1: + dependencies: + ini: 2.0.0 + + global-modules@2.0.0: + dependencies: + global-prefix: 3.0.0 + + global-prefix@3.0.0: + dependencies: + ini: 1.3.8 + kind-of: 6.0.3 + which: 1.3.1 + + globals@13.24.0: + dependencies: + type-fest: 0.20.2 + + globalthis@1.0.4: + dependencies: + define-properties: 1.2.1 + gopd: 1.2.0 + + globby@11.1.0: + dependencies: + array-union: 2.1.0 + dir-glob: 3.0.1 + fast-glob: 3.3.3 + ignore: 5.3.2 + merge2: 1.4.1 + slash: 3.0.0 + + globby@13.2.2: + dependencies: + dir-glob: 3.0.1 + fast-glob: 3.3.3 + ignore: 5.3.2 + merge2: 1.4.1 + slash: 4.0.0 + + globrex@0.1.2: {} + + gopd@1.2.0: {} + + graceful-fs@4.2.11: {} + + graphemer@1.4.0: {} + + gzip-size@6.0.0: + dependencies: + duplexer: 0.1.2 + + handle-thing@2.0.1: {} + + harmony-reflect@1.6.2: {} + + has-bigints@1.1.0: {} + + has-flag@3.0.0: {} + + has-flag@4.0.0: {} + + has-property-descriptors@1.0.2: + dependencies: + es-define-property: 1.0.1 + + has-proto@1.2.0: + dependencies: + dunder-proto: 1.0.1 + + has-symbols@1.1.0: {} + + has-tostringtag@1.0.2: + dependencies: + has-symbols: 1.1.0 + + has-unicode@2.0.1: + optional: true + + hasown@2.0.2: + dependencies: + function-bind: 1.1.2 + + hast-util-from-dom@4.2.0: + dependencies: + hastscript: 7.2.0 + web-namespaces: 2.0.1 + + hast-util-is-element@2.1.3: + dependencies: + '@types/hast': 2.3.10 + '@types/unist': 2.0.11 + + hast-util-parse-selector@2.2.5: {} + + hast-util-parse-selector@3.1.1: + dependencies: + '@types/hast': 2.3.10 + + hast-util-to-text@3.1.2: + dependencies: + '@types/hast': 2.3.10 + '@types/unist': 2.0.11 + hast-util-is-element: 2.1.3 + unist-util-find-after: 4.0.1 + + hast-util-whitespace@2.0.1: {} + + hastscript@6.0.0: + dependencies: + '@types/hast': 2.3.10 + comma-separated-tokens: 1.0.8 + hast-util-parse-selector: 2.2.5 + property-information: 5.6.0 + space-separated-tokens: 1.1.5 + + hastscript@7.2.0: + dependencies: + '@types/hast': 2.3.10 + comma-separated-tokens: 2.0.3 + hast-util-parse-selector: 3.1.1 + property-information: 6.5.0 + space-separated-tokens: 2.0.2 + + he@1.2.0: {} + + hey-listen@1.0.8: {} + + highlight.js@10.7.3: {} + + highlight.js@11.11.1: {} + + highlightjs-vue@1.0.0: {} + + history@5.3.0: + dependencies: + '@babel/runtime': 7.28.4 + + hoist-non-react-statics@3.3.2: + dependencies: + react-is: 16.13.1 + + hoopy@0.1.4: {} + + hpack.js@2.1.6: + dependencies: + inherits: 2.0.4 + obuf: 1.1.2 + readable-stream: 2.3.8 + wbuf: 1.7.3 + + html-dom-parser@3.1.7: + dependencies: + domhandler: 5.0.3 + htmlparser2: 8.0.2 + + html-encoding-sniffer@2.0.1: + dependencies: + whatwg-encoding: 1.0.5 + + html-encoding-sniffer@3.0.0: + dependencies: + whatwg-encoding: 2.0.0 + + html-entities@2.6.0: {} + + html-escaper@2.0.2: {} + + html-minifier-terser@6.1.0: + dependencies: + camel-case: 4.1.2 + clean-css: 5.3.3 + commander: 8.3.0 + he: 1.2.0 + param-case: 3.0.4 + relateurl: 0.2.7 + terser: 5.44.0 + + html-react-parser@3.0.16(react@18.3.1): + dependencies: + domhandler: 5.0.3 + html-dom-parser: 3.1.7 + react: 18.3.1 + react-property: 2.0.0 + style-to-js: 1.1.3 + + html-webpack-plugin@5.6.4(webpack@5.102.1): + dependencies: + '@types/html-minifier-terser': 6.1.0 + html-minifier-terser: 6.1.0 + lodash: 4.17.21 + pretty-error: 4.0.0 + tapable: 2.3.0 + optionalDependencies: + webpack: 5.102.1 + + htmlparser2@6.1.0: + dependencies: + domelementtype: 2.3.0 + domhandler: 4.3.1 + domutils: 2.8.0 + entities: 2.2.0 + + htmlparser2@8.0.2: + dependencies: + domelementtype: 2.3.0 + domhandler: 5.0.3 + domutils: 3.2.2 + entities: 4.5.0 + + http-cache-semantics@4.2.0: + optional: true + + http-deceiver@1.2.7: {} + + http-errors@1.6.3: + dependencies: + depd: 1.1.2 + inherits: 2.0.3 + setprototypeof: 1.1.0 + statuses: 1.5.0 + + http-errors@2.0.0: + dependencies: + depd: 2.0.0 + inherits: 2.0.4 + setprototypeof: 1.2.0 + statuses: 2.0.1 + toidentifier: 1.0.1 + + http-parser-js@0.5.10: {} + + http-proxy-agent@4.0.1: + dependencies: + '@tootallnate/once': 1.1.2 + agent-base: 6.0.2 + debug: 4.4.3(supports-color@8.1.1) + transitivePeerDependencies: + - supports-color + + http-proxy-agent@5.0.0: + dependencies: + '@tootallnate/once': 2.0.0 + agent-base: 6.0.2 + debug: 4.4.3(supports-color@8.1.1) + transitivePeerDependencies: + - supports-color + + http-proxy-middleware@3.0.3: + dependencies: + '@types/http-proxy': 1.17.16 + debug: 4.4.3(supports-color@8.1.1) + http-proxy: 1.18.1(debug@4.4.3) + is-glob: 4.0.3 + is-plain-object: 5.0.0 + micromatch: 4.0.8 + transitivePeerDependencies: + - supports-color + + http-proxy@1.18.1(debug@4.4.3): + dependencies: + eventemitter3: 4.0.7 + follow-redirects: 1.15.11(debug@4.4.3) + requires-port: 1.0.0 + transitivePeerDependencies: + - debug + + http-signature@1.4.0: + dependencies: + assert-plus: 1.0.0 + jsprim: 2.0.2 + sshpk: 1.18.0 + + http-status-codes@2.3.0: {} + + https-proxy-agent@5.0.1: + dependencies: + agent-base: 6.0.2 + debug: 4.4.3(supports-color@8.1.1) + transitivePeerDependencies: + - supports-color + + https-proxy-agent@7.0.6: + dependencies: + agent-base: 7.1.4 + debug: 4.4.3(supports-color@8.1.1) + transitivePeerDependencies: + - supports-color + + human-signals@1.1.1: {} + + human-signals@2.1.0: {} + + human-signals@4.3.1: {} + + humanize-ms@1.2.1: + dependencies: + ms: 2.1.3 + optional: true + + husky@8.0.3: {} + + hyperdyperid@1.2.0: {} + + iconv-lite@0.4.24: + dependencies: + safer-buffer: 2.1.2 + + iconv-lite@0.5.2: + dependencies: + safer-buffer: 2.1.2 + + iconv-lite@0.6.3: + dependencies: + safer-buffer: 2.1.2 + + iconv-lite@0.7.0: + dependencies: + safer-buffer: 2.1.2 + + icss-utils@5.1.0(postcss@8.5.6): + dependencies: + postcss: 8.5.6 + + idb@7.1.1: {} + + identity-obj-proxy@3.0.0: + dependencies: + harmony-reflect: 1.6.2 + + ieee754@1.2.1: {} + + ignore-by-default@1.0.1: {} + + ignore@5.3.2: {} + + immer@11.1.4: {} + + immer@9.0.21: {} + + immutable@5.1.3: {} + + import-fresh@3.3.1: + dependencies: + parent-module: 1.0.1 + resolve-from: 4.0.0 + + import-local@3.2.0: + dependencies: + pkg-dir: 4.2.0 + resolve-cwd: 3.0.0 + + imurmurhash@0.1.4: {} + + indent-string@4.0.0: {} + + infer-owner@1.0.4: + optional: true + + inherits@2.0.3: {} + + inherits@2.0.4: {} + + ini@1.3.8: {} + + ini@2.0.0: {} + + inline-style-parser@0.1.1: {} + + internal-slot@1.1.0: + dependencies: + es-errors: 1.3.0 + hasown: 2.0.2 + side-channel: 1.1.0 + + internmap@2.0.3: {} + + interpret@1.4.0: {} + + ioredis@5.8.1: + dependencies: + '@ioredis/commands': 1.4.0 + cluster-key-slot: 1.1.2 + debug: 4.4.3(supports-color@8.1.1) + denque: 2.1.0 + lodash.defaults: 4.2.0 + lodash.isarguments: 3.1.0 + redis-errors: 1.2.0 + redis-parser: 3.0.0 + standard-as-callback: 2.1.0 + transitivePeerDependencies: + - supports-color + optional: true + + ip-address@10.0.1: + optional: true + + ipaddr.js@1.9.1: {} + + ipaddr.js@2.2.0: {} + + is-alphabetical@1.0.4: {} + + is-alphanumerical@1.0.4: + dependencies: + is-alphabetical: 1.0.4 + is-decimal: 1.0.4 + + is-arguments@1.2.0: + dependencies: + call-bound: 1.0.4 + has-tostringtag: 1.0.2 + + is-array-buffer@3.0.5: + dependencies: + call-bind: 1.0.8 + call-bound: 1.0.4 + get-intrinsic: 1.3.0 + + is-arrayish@0.2.1: {} + + is-async-function@2.1.1: + dependencies: + async-function: 1.0.0 + call-bound: 1.0.4 + get-proto: 1.0.1 + has-tostringtag: 1.0.2 + safe-regex-test: 1.1.0 + + is-bigint@1.1.0: + dependencies: + has-bigints: 1.1.0 + + is-binary-path@2.1.0: + dependencies: + binary-extensions: 2.3.0 + + is-boolean-object@1.2.2: + dependencies: + call-bound: 1.0.4 + has-tostringtag: 1.0.2 + + is-buffer@2.0.5: {} + + is-callable@1.2.7: {} + + is-core-module@2.16.1: + dependencies: + hasown: 2.0.2 + + is-data-view@1.0.2: + dependencies: + call-bound: 1.0.4 + get-intrinsic: 1.3.0 + is-typed-array: 1.1.15 + + is-date-object@1.1.0: + dependencies: + call-bound: 1.0.4 + has-tostringtag: 1.0.2 + + is-decimal@1.0.4: {} + + is-docker@2.2.1: {} + + is-extglob@2.1.1: {} + + is-finalizationregistry@1.1.1: + dependencies: + call-bound: 1.0.4 + + is-fullwidth-code-point@3.0.0: {} + + is-fullwidth-code-point@4.0.0: {} + + is-generator-fn@2.1.0: {} + + is-generator-function@1.1.2: + dependencies: + call-bound: 1.0.4 + generator-function: 2.0.1 + get-proto: 1.0.1 + has-tostringtag: 1.0.2 + safe-regex-test: 1.1.0 + + is-glob@4.0.3: + dependencies: + is-extglob: 2.1.1 + + is-hexadecimal@1.0.4: {} + + is-installed-globally@0.4.0: + dependencies: + global-dirs: 3.0.1 + is-path-inside: 3.0.3 + + is-lambda@1.0.1: + optional: true + + is-map@2.0.3: {} + + is-module@1.0.0: {} + + is-negative-zero@2.0.3: {} + + is-number-object@1.1.1: + dependencies: + call-bound: 1.0.4 + has-tostringtag: 1.0.2 + + is-number@7.0.0: {} + + is-obj@1.0.1: {} + + is-path-inside@3.0.3: {} + + is-plain-obj@4.1.0: {} + + is-plain-object@5.0.0: {} + + is-potential-custom-element-name@1.0.1: {} + + is-property@1.0.2: {} + + is-regex@1.2.1: + dependencies: + call-bound: 1.0.4 + gopd: 1.2.0 + has-tostringtag: 1.0.2 + hasown: 2.0.2 + + is-regexp@1.0.0: {} + + is-root@2.1.0: {} + + is-set@2.0.3: {} + + is-shared-array-buffer@1.0.4: + dependencies: + call-bound: 1.0.4 + + is-stream@2.0.1: {} + + is-stream@3.0.0: {} + + is-string@1.1.1: + dependencies: + call-bound: 1.0.4 + has-tostringtag: 1.0.2 + + is-symbol@1.1.1: + dependencies: + call-bound: 1.0.4 + has-symbols: 1.1.0 + safe-regex-test: 1.1.0 + + is-typed-array@1.1.15: + dependencies: + which-typed-array: 1.1.19 + + is-typedarray@1.0.0: {} + + is-unicode-supported@0.1.0: {} + + is-weakmap@2.0.2: {} + + is-weakref@1.1.1: + dependencies: + call-bound: 1.0.4 + + is-weakset@2.0.4: + dependencies: + call-bound: 1.0.4 + get-intrinsic: 1.3.0 + + is-wsl@2.2.0: + dependencies: + is-docker: 2.2.1 + + isarray@1.0.0: {} + + isarray@2.0.5: {} + + isexe@2.0.0: {} + + isstream@0.1.2: {} + + istanbul-lib-coverage@3.2.2: {} + + istanbul-lib-instrument@5.2.1: + dependencies: + '@babel/core': 7.28.4 + '@babel/parser': 7.28.4 + '@istanbuljs/schema': 0.1.3 + istanbul-lib-coverage: 3.2.2 + semver: 7.7.1 + transitivePeerDependencies: + - supports-color + + istanbul-lib-report@3.0.1: + dependencies: + istanbul-lib-coverage: 3.2.2 + make-dir: 4.0.0 + supports-color: 7.2.0 + + istanbul-lib-source-maps@4.0.1: + dependencies: + debug: 4.4.3(supports-color@8.1.1) + istanbul-lib-coverage: 3.2.2 + source-map: 0.6.1 + transitivePeerDependencies: + - supports-color + + istanbul-reports@3.2.0: + dependencies: + html-escaper: 2.0.2 + istanbul-lib-report: 3.0.1 + + iterator.prototype@1.1.5: + dependencies: + define-data-property: 1.1.4 + es-object-atoms: 1.1.1 + get-intrinsic: 1.3.0 + get-proto: 1.0.1 + has-symbols: 1.1.0 + set-function-name: 2.0.2 + + jackspeak@3.4.3: + dependencies: + '@isaacs/cliui': 8.0.2 + optionalDependencies: + '@pkgjs/parseargs': 0.11.0 + + jake@10.9.4: + dependencies: + async: 3.2.6 + filelist: 1.0.4 + picocolors: 1.1.1 + + jest-changed-files@27.5.1: + dependencies: + '@jest/types': 27.5.1 + execa: 5.1.1 + throat: 6.0.2 + + jest-circus@27.5.1: + dependencies: + '@jest/environment': 27.5.1 + '@jest/test-result': 27.5.1 + '@jest/types': 27.5.1 + '@types/node': 24.7.2 + chalk: 4.1.2 + co: 4.6.0 + dedent: 0.7.0 + expect: 27.5.1 + is-generator-fn: 2.1.0 + jest-each: 27.5.1 + jest-matcher-utils: 27.5.1 + jest-message-util: 27.5.1 + jest-runtime: 27.5.1 + jest-snapshot: 27.5.1 + jest-util: 27.5.1 + pretty-format: 27.5.1 + slash: 3.0.0 + stack-utils: 2.0.6 + throat: 6.0.2 + transitivePeerDependencies: + - supports-color + + jest-cli@27.5.1(bufferutil@4.0.9)(canvas@2.11.2(encoding@0.1.13))(ts-node@10.9.2(@types/node@24.7.2)(typescript@5.9.3))(utf-8-validate@6.0.5): + dependencies: + '@jest/core': 27.5.1(bufferutil@4.0.9)(canvas@2.11.2(encoding@0.1.13))(ts-node@10.9.2(@types/node@24.7.2)(typescript@5.9.3))(utf-8-validate@6.0.5) + '@jest/test-result': 27.5.1 + '@jest/types': 27.5.1 + chalk: 4.1.2 + exit: 0.1.2 + graceful-fs: 4.2.11 + import-local: 3.2.0 + jest-config: 27.5.1(bufferutil@4.0.9)(canvas@2.11.2(encoding@0.1.13))(ts-node@10.9.2(@types/node@24.7.2)(typescript@5.9.3))(utf-8-validate@6.0.5) + jest-util: 27.5.1 + jest-validate: 27.5.1 + prompts: 2.4.2 + yargs: 16.2.0 + transitivePeerDependencies: + - bufferutil + - canvas + - supports-color + - ts-node + - utf-8-validate + + jest-config@27.5.1(bufferutil@4.0.9)(canvas@2.11.2(encoding@0.1.13))(ts-node@10.9.2(@types/node@24.7.2)(typescript@5.9.3))(utf-8-validate@6.0.5): + dependencies: + '@babel/core': 7.28.4 + '@jest/test-sequencer': 27.5.1 + '@jest/types': 27.5.1 + babel-jest: 27.5.1(@babel/core@7.28.4) + chalk: 4.1.2 + ci-info: 3.9.0 + deepmerge: 4.3.1 + glob: 10.5.0 + graceful-fs: 4.2.11 + jest-circus: 27.5.1 + jest-environment-jsdom: 27.5.1(bufferutil@4.0.9)(canvas@2.11.2(encoding@0.1.13))(utf-8-validate@6.0.5) + jest-environment-node: 27.5.1 + jest-get-type: 27.5.1 + jest-jasmine2: 27.5.1 + jest-regex-util: 27.5.1 + jest-resolve: 27.5.1 + jest-runner: 27.5.1(bufferutil@4.0.9)(canvas@2.11.2(encoding@0.1.13))(utf-8-validate@6.0.5) + jest-util: 27.5.1 + jest-validate: 27.5.1 + micromatch: 4.0.8 + parse-json: 5.2.0 + pretty-format: 27.5.1 + slash: 3.0.0 + strip-json-comments: 3.1.1 + optionalDependencies: + ts-node: 10.9.2(@types/node@24.7.2)(typescript@5.9.3) + transitivePeerDependencies: + - bufferutil + - canvas + - supports-color + - utf-8-validate + + jest-diff@27.5.1: + dependencies: + chalk: 4.1.2 + diff-sequences: 27.5.1 + jest-get-type: 27.5.1 + pretty-format: 27.5.1 + + jest-diff@30.2.0: + dependencies: + '@jest/diff-sequences': 30.0.1 + '@jest/get-type': 30.1.0 + chalk: 4.1.2 + pretty-format: 30.2.0 + + jest-docblock@27.5.1: + dependencies: + detect-newline: 3.1.0 + + jest-each@27.5.1: + dependencies: + '@jest/types': 27.5.1 + chalk: 4.1.2 + jest-get-type: 27.5.1 + jest-util: 27.5.1 + pretty-format: 27.5.1 + + jest-environment-jsdom@27.5.1(bufferutil@4.0.9)(canvas@2.11.2(encoding@0.1.13))(utf-8-validate@6.0.5): + dependencies: + '@jest/environment': 27.5.1 + '@jest/fake-timers': 27.5.1 + '@jest/types': 27.5.1 + '@types/node': 24.7.2 + jest-mock: 27.5.1 + jest-util: 27.5.1 + jsdom: 16.7.0(bufferutil@4.0.9)(canvas@2.11.2(encoding@0.1.13))(utf-8-validate@6.0.5) + transitivePeerDependencies: + - bufferutil + - canvas + - supports-color + - utf-8-validate + + jest-environment-node@27.5.1: + dependencies: + '@jest/environment': 27.5.1 + '@jest/fake-timers': 27.5.1 + '@jest/types': 27.5.1 + '@types/node': 24.7.2 + jest-mock: 27.5.1 + jest-util: 27.5.1 + + jest-get-type@27.5.1: {} + + jest-haste-map@27.5.1: + dependencies: + '@jest/types': 27.5.1 + '@types/graceful-fs': 4.1.9 + '@types/node': 24.7.2 + anymatch: 3.1.3 + fb-watchman: 2.0.2 + graceful-fs: 4.2.11 + jest-regex-util: 27.5.1 + jest-serializer: 27.5.1 + jest-util: 27.5.1 + jest-worker: 27.5.1 + micromatch: 4.0.8 + walker: 1.0.8 + optionalDependencies: + fsevents: 2.3.3 + + jest-jasmine2@27.5.1: + dependencies: + '@jest/environment': 27.5.1 + '@jest/source-map': 27.5.1 + '@jest/test-result': 27.5.1 + '@jest/types': 27.5.1 + '@types/node': 24.7.2 + chalk: 4.1.2 + co: 4.6.0 + expect: 27.5.1 + is-generator-fn: 2.1.0 + jest-each: 27.5.1 + jest-matcher-utils: 27.5.1 + jest-message-util: 27.5.1 + jest-runtime: 27.5.1 + jest-snapshot: 27.5.1 + jest-util: 27.5.1 + pretty-format: 27.5.1 + throat: 6.0.2 + transitivePeerDependencies: + - supports-color + + jest-leak-detector@27.5.1: + dependencies: + jest-get-type: 27.5.1 + pretty-format: 27.5.1 + + jest-matcher-utils@27.5.1: + dependencies: + chalk: 4.1.2 + jest-diff: 27.5.1 + jest-get-type: 27.5.1 + pretty-format: 27.5.1 + + jest-matcher-utils@30.2.0: + dependencies: + '@jest/get-type': 30.1.0 + chalk: 4.1.2 + jest-diff: 30.2.0 + pretty-format: 30.2.0 + + jest-message-util@27.5.1: + dependencies: + '@babel/code-frame': 7.27.1 + '@jest/types': 27.5.1 + '@types/stack-utils': 2.0.3 + chalk: 4.1.2 + graceful-fs: 4.2.11 + micromatch: 4.0.8 + pretty-format: 27.5.1 + slash: 3.0.0 + stack-utils: 2.0.6 + + jest-message-util@28.1.3: + dependencies: + '@babel/code-frame': 7.27.1 + '@jest/types': 28.1.3 + '@types/stack-utils': 2.0.3 + chalk: 4.1.2 + graceful-fs: 4.2.11 + micromatch: 4.0.8 + pretty-format: 28.1.3 + slash: 3.0.0 + stack-utils: 2.0.6 + + jest-message-util@30.2.0: + dependencies: + '@babel/code-frame': 7.27.1 + '@jest/types': 30.2.0 + '@types/stack-utils': 2.0.3 + chalk: 4.1.2 + graceful-fs: 4.2.11 + micromatch: 4.0.8 + pretty-format: 30.2.0 + slash: 3.0.0 + stack-utils: 2.0.6 + + jest-mock@27.5.1: + dependencies: + '@jest/types': 27.5.1 + '@types/node': 24.7.2 + + jest-mock@30.2.0: + dependencies: + '@jest/types': 30.2.0 + '@types/node': 24.7.2 + jest-util: 30.2.0 + + jest-pnp-resolver@1.2.3(jest-resolve@27.5.1): + optionalDependencies: + jest-resolve: 27.5.1 + + jest-regex-util@27.5.1: {} + + jest-regex-util@28.0.2: {} + + jest-regex-util@30.0.1: {} + + jest-resolve-dependencies@27.5.1: + dependencies: + '@jest/types': 27.5.1 + jest-regex-util: 27.5.1 + jest-snapshot: 27.5.1 + transitivePeerDependencies: + - supports-color + + jest-resolve@27.5.1: + dependencies: + '@jest/types': 27.5.1 + chalk: 4.1.2 + graceful-fs: 4.2.11 + jest-haste-map: 27.5.1 + jest-pnp-resolver: 1.2.3(jest-resolve@27.5.1) + jest-util: 27.5.1 + jest-validate: 27.5.1 + resolve: 1.22.10 + resolve.exports: 1.1.1 + slash: 3.0.0 + + jest-runner@27.5.1(bufferutil@4.0.9)(canvas@2.11.2(encoding@0.1.13))(utf-8-validate@6.0.5): + dependencies: + '@jest/console': 27.5.1 + '@jest/environment': 27.5.1 + '@jest/test-result': 27.5.1 + '@jest/transform': 27.5.1 + '@jest/types': 27.5.1 + '@types/node': 24.7.2 + chalk: 4.1.2 + emittery: 0.8.1 + graceful-fs: 4.2.11 + jest-docblock: 27.5.1 + jest-environment-jsdom: 27.5.1(bufferutil@4.0.9)(canvas@2.11.2(encoding@0.1.13))(utf-8-validate@6.0.5) + jest-environment-node: 27.5.1 + jest-haste-map: 27.5.1 + jest-leak-detector: 27.5.1 + jest-message-util: 27.5.1 + jest-resolve: 27.5.1 + jest-runtime: 27.5.1 + jest-util: 27.5.1 + jest-worker: 27.5.1 + source-map-support: 0.5.21 + throat: 6.0.2 + transitivePeerDependencies: + - bufferutil + - canvas + - supports-color + - utf-8-validate + + jest-runtime@27.5.1: + dependencies: + '@jest/environment': 27.5.1 + '@jest/fake-timers': 27.5.1 + '@jest/globals': 27.5.1 + '@jest/source-map': 27.5.1 + '@jest/test-result': 27.5.1 + '@jest/transform': 27.5.1 + '@jest/types': 27.5.1 + chalk: 4.1.2 + cjs-module-lexer: 1.4.3 + collect-v8-coverage: 1.0.2 + execa: 5.1.1 + glob: 10.5.0 + graceful-fs: 4.2.11 + jest-haste-map: 27.5.1 + jest-message-util: 27.5.1 + jest-mock: 27.5.1 + jest-regex-util: 27.5.1 + jest-resolve: 27.5.1 + jest-snapshot: 27.5.1 + jest-util: 27.5.1 + slash: 3.0.0 + strip-bom: 4.0.0 + transitivePeerDependencies: + - supports-color + + jest-serializer@27.5.1: + dependencies: + '@types/node': 24.7.2 + graceful-fs: 4.2.11 + + jest-snapshot@27.5.1: + dependencies: + '@babel/core': 7.28.4 + '@babel/generator': 7.28.3 + '@babel/plugin-syntax-typescript': 7.27.1(@babel/core@7.28.4) + '@babel/traverse': 7.28.4 + '@babel/types': 7.28.4 + '@jest/transform': 27.5.1 + '@jest/types': 27.5.1 + '@types/babel__traverse': 7.28.0 + '@types/prettier': 2.7.3 + babel-preset-current-node-syntax: 1.2.0(@babel/core@7.28.4) + chalk: 4.1.2 + expect: 27.5.1 + graceful-fs: 4.2.11 + jest-diff: 27.5.1 + jest-get-type: 27.5.1 + jest-haste-map: 27.5.1 + jest-matcher-utils: 27.5.1 + jest-message-util: 27.5.1 + jest-util: 27.5.1 + natural-compare: 1.4.0 + pretty-format: 27.5.1 + semver: 7.7.1 + transitivePeerDependencies: + - supports-color + + jest-util@27.5.1: + dependencies: + '@jest/types': 27.5.1 + '@types/node': 24.7.2 + chalk: 4.1.2 + ci-info: 3.9.0 + graceful-fs: 4.2.11 + picomatch: 2.3.1 + + jest-util@28.1.3: + dependencies: + '@jest/types': 28.1.3 + '@types/node': 24.7.2 + chalk: 4.1.2 + ci-info: 3.9.0 + graceful-fs: 4.2.11 + picomatch: 2.3.1 + + jest-util@30.2.0: + dependencies: + '@jest/types': 30.2.0 + '@types/node': 24.7.2 + chalk: 4.1.2 + ci-info: 4.3.1 + graceful-fs: 4.2.11 + picomatch: 4.0.3 + + jest-validate@27.5.1: + dependencies: + '@jest/types': 27.5.1 + camelcase: 6.3.0 + chalk: 4.1.2 + jest-get-type: 27.5.1 + leven: 3.1.0 + pretty-format: 27.5.1 + + jest-watch-typeahead@1.1.0(jest@27.5.1(bufferutil@4.0.9)(canvas@2.11.2(encoding@0.1.13))(ts-node@10.9.2(@types/node@24.7.2)(typescript@5.9.3))(utf-8-validate@6.0.5)): + dependencies: + ansi-escapes: 4.3.2 + chalk: 4.1.2 + jest: 27.5.1(bufferutil@4.0.9)(canvas@2.11.2(encoding@0.1.13))(ts-node@10.9.2(@types/node@24.7.2)(typescript@5.9.3))(utf-8-validate@6.0.5) + jest-regex-util: 28.0.2 + jest-watcher: 28.1.3 + slash: 4.0.0 + string-length: 5.0.1 + strip-ansi: 7.1.2 + + jest-watcher@27.5.1: + dependencies: + '@jest/test-result': 27.5.1 + '@jest/types': 27.5.1 + '@types/node': 24.7.2 + ansi-escapes: 4.3.2 + chalk: 4.1.2 + jest-util: 27.5.1 + string-length: 4.0.2 + + jest-watcher@28.1.3: + dependencies: + '@jest/test-result': 28.1.3 + '@jest/types': 28.1.3 + '@types/node': 24.7.2 + ansi-escapes: 4.3.2 + chalk: 4.1.2 + emittery: 0.10.2 + jest-util: 28.1.3 + string-length: 4.0.2 + + jest-worker@26.6.2: + dependencies: + '@types/node': 24.7.2 + merge-stream: 2.0.0 + supports-color: 7.2.0 + + jest-worker@27.5.1: + dependencies: + '@types/node': 24.7.2 + merge-stream: 2.0.0 + supports-color: 8.1.1 + + jest-worker@28.1.3: + dependencies: + '@types/node': 24.7.2 + merge-stream: 2.0.0 + supports-color: 8.1.1 + + jest@27.5.1(bufferutil@4.0.9)(canvas@2.11.2(encoding@0.1.13))(ts-node@10.9.2(@types/node@24.7.2)(typescript@5.9.3))(utf-8-validate@6.0.5): + dependencies: + '@jest/core': 27.5.1(bufferutil@4.0.9)(canvas@2.11.2(encoding@0.1.13))(ts-node@10.9.2(@types/node@24.7.2)(typescript@5.9.3))(utf-8-validate@6.0.5) + import-local: 3.2.0 + jest-cli: 27.5.1(bufferutil@4.0.9)(canvas@2.11.2(encoding@0.1.13))(ts-node@10.9.2(@types/node@24.7.2)(typescript@5.9.3))(utf-8-validate@6.0.5) + transitivePeerDependencies: + - bufferutil + - canvas + - supports-color + - ts-node + - utf-8-validate + + jiti@1.21.7: {} + + joi@18.0.1: + dependencies: + '@hapi/address': 5.1.1 + '@hapi/formula': 3.0.2 + '@hapi/hoek': 11.0.7 + '@hapi/pinpoint': 2.0.1 + '@hapi/tlds': 1.1.3 + '@hapi/topo': 6.0.2 + '@standard-schema/spec': 1.0.0 + + js-tokens@4.0.0: {} + + js-yaml@3.14.1: + dependencies: + argparse: 1.0.10 + esprima: 4.0.1 + + js-yaml@4.1.0: + dependencies: + argparse: 2.0.1 + + jsbn@0.1.1: {} + + jsdom@16.7.0(bufferutil@4.0.9)(canvas@2.11.2(encoding@0.1.13))(utf-8-validate@6.0.5): + dependencies: + abab: 2.0.6 + acorn: 8.15.0 + acorn-globals: 6.0.0 + cssom: 0.4.4 + cssstyle: 2.3.0 + data-urls: 2.0.0 + decimal.js: 10.6.0 + domexception: 2.0.1 + escodegen: 2.1.0 + form-data: 4.0.4 + html-encoding-sniffer: 2.0.1 + http-proxy-agent: 4.0.1 + https-proxy-agent: 5.0.1 + is-potential-custom-element-name: 1.0.1 + nwsapi: 2.2.22 + parse5: 6.0.1 + saxes: 5.0.1 + symbol-tree: 3.2.4 + tough-cookie: 4.1.4 + w3c-hr-time: 1.0.2 + w3c-xmlserializer: 2.0.0 + webidl-conversions: 6.1.0 + whatwg-encoding: 1.0.5 + whatwg-mimetype: 2.3.0 + whatwg-url: 8.7.0 + ws: 8.18.3(bufferutil@4.0.9)(utf-8-validate@6.0.5) + xml-name-validator: 3.0.0 + optionalDependencies: + canvas: 2.11.2(encoding@0.1.13) + transitivePeerDependencies: + - bufferutil + - supports-color + - utf-8-validate + + jsdom@20.0.3(bufferutil@4.0.9)(canvas@2.11.2(encoding@0.1.13))(utf-8-validate@6.0.5): + dependencies: + abab: 2.0.6 + acorn: 8.15.0 + acorn-globals: 7.0.1 + cssom: 0.5.0 + cssstyle: 2.3.0 + data-urls: 3.0.2 + decimal.js: 10.6.0 + domexception: 4.0.0 + escodegen: 2.1.0 + form-data: 4.0.4 + html-encoding-sniffer: 3.0.0 + http-proxy-agent: 5.0.0 + https-proxy-agent: 5.0.1 + is-potential-custom-element-name: 1.0.1 + nwsapi: 2.2.22 + parse5: 7.3.0 + saxes: 6.0.0 + symbol-tree: 3.2.4 + tough-cookie: 4.1.4 + w3c-xmlserializer: 4.0.0 + webidl-conversions: 7.0.0 + whatwg-encoding: 2.0.0 + whatwg-mimetype: 3.0.0 + whatwg-url: 11.0.0 + ws: 8.18.3(bufferutil@4.0.9)(utf-8-validate@6.0.5) + xml-name-validator: 4.0.0 + optionalDependencies: + canvas: 2.11.2(encoding@0.1.13) + transitivePeerDependencies: + - bufferutil + - supports-color + - utf-8-validate + + jsesc@3.1.0: {} + + json-buffer@3.0.1: {} + + json-parse-even-better-errors@2.3.1: {} + + json-schema-traverse@0.4.1: {} + + json-schema-traverse@1.0.0: {} + + json-schema@0.4.0: {} + + json-stable-stringify-without-jsonify@1.0.1: {} + + json-stringify-safe@5.0.1: {} + + json5@2.2.3: {} + + jsonfile@6.2.0: + dependencies: + universalify: 2.0.1 + optionalDependencies: + graceful-fs: 4.2.11 + + jsonpath@1.1.1: + dependencies: + esprima: 1.2.2 + static-eval: 2.0.2 + underscore: 1.12.1 + + jsonpointer@5.0.1: {} + + jsprim@2.0.2: + dependencies: + assert-plus: 1.0.0 + extsprintf: 1.3.0 + json-schema: 0.4.0 + verror: 1.10.0 + + jsx-ast-utils@3.3.5: + dependencies: + array-includes: 3.1.9 + array.prototype.flat: 1.3.3 + object.assign: 4.1.7 + object.values: 1.2.1 + + katex@0.16.25: + dependencies: + commander: 8.3.0 + + keycloak-js@26.2.1: {} + + keyv@4.5.4: + dependencies: + json-buffer: 3.0.1 + + kill-port@2.0.1: + dependencies: + get-them-args: 1.3.2 + shell-exec: 1.0.2 + + kind-of@6.0.3: {} + + kleur@3.0.3: {} + + kleur@4.1.5: {} + + klona@2.0.6: {} + + kuler@2.0.0: {} + + language-subtag-registry@0.3.23: {} + + language-tags@1.0.9: + dependencies: + language-subtag-registry: 0.3.23 + + launch-editor@2.11.1: + dependencies: + picocolors: 1.1.1 + shell-quote: 1.8.3 + + lazy-ass@1.6.0: {} + + lazystream@1.0.1: + dependencies: + readable-stream: 2.3.8 + + leven@3.1.0: {} + + levn@0.3.0: + dependencies: + prelude-ls: 1.1.2 + type-check: 0.3.2 + + levn@0.4.1: + dependencies: + prelude-ls: 1.2.1 + type-check: 0.4.0 + + lilconfig@2.1.0: {} + + lilconfig@3.1.3: {} + + lines-and-columns@1.2.4: {} + + lint-staged@13.3.0(enquirer@2.4.1): + dependencies: + chalk: 5.3.0 + commander: 11.0.0 + debug: 4.3.4 + execa: 7.2.0 + lilconfig: 2.1.0 + listr2: 6.6.1(enquirer@2.4.1) + micromatch: 4.0.5 + pidtree: 0.6.0 + string-argv: 0.3.2 + yaml: 2.3.1 + transitivePeerDependencies: + - enquirer + - supports-color + + listr2@3.14.0(enquirer@2.4.1): + dependencies: + cli-truncate: 2.1.0 + colorette: 2.0.20 + log-update: 4.0.0 + p-map: 4.0.0 + rfdc: 1.4.1 + rxjs: 7.8.2 + through: 2.3.8 + wrap-ansi: 7.0.0 + optionalDependencies: + enquirer: 2.4.1 + + listr2@6.6.1(enquirer@2.4.1): + dependencies: + cli-truncate: 3.1.0 + colorette: 2.0.20 + eventemitter3: 5.0.1 + log-update: 5.0.1 + rfdc: 1.4.1 + wrap-ansi: 8.1.0 + optionalDependencies: + enquirer: 2.4.1 + + loader-runner@4.3.1: {} + + loader-utils@2.0.4: + dependencies: + big.js: 5.2.2 + emojis-list: 3.0.0 + json5: 2.2.3 + + loader-utils@3.3.1: {} + + locate-path@3.0.0: + dependencies: + p-locate: 3.0.0 + path-exists: 3.0.0 + + locate-path@5.0.0: + dependencies: + p-locate: 4.1.0 + + locate-path@6.0.0: + dependencies: + p-locate: 5.0.0 + + lodash-es@4.17.21: {} + + lodash.camelcase@4.3.0: {} + + lodash.curry@4.1.1: {} + + lodash.debounce@4.0.8: {} + + lodash.defaults@4.2.0: + optional: true + + lodash.flow@3.5.0: {} + + lodash.isarguments@3.1.0: + optional: true + + lodash.memoize@4.1.2: {} + + lodash.merge@4.6.2: {} + + lodash.once@4.1.1: {} + + lodash.sortby@4.7.0: {} + + lodash.uniq@4.5.0: {} + + lodash@4.17.21: {} + + log-symbols@4.1.0: + dependencies: + chalk: 4.1.2 + is-unicode-supported: 0.1.0 + + log-update@4.0.0: + dependencies: + ansi-escapes: 4.3.2 + cli-cursor: 3.1.0 + slice-ansi: 4.0.0 + wrap-ansi: 6.2.0 + + log-update@5.0.1: + dependencies: + ansi-escapes: 5.0.0 + cli-cursor: 4.0.0 + slice-ansi: 5.0.0 + strip-ansi: 7.1.2 + wrap-ansi: 8.1.0 + + logform@2.7.0: + dependencies: + '@colors/colors': 1.6.0 + '@types/triple-beam': 1.3.5 + fecha: 4.2.3 + ms: 2.1.3 + safe-stable-stringify: 2.5.0 + triple-beam: 1.4.1 + + long@5.3.2: {} + + longest-streak@3.1.0: {} + + loose-envify@1.4.0: + dependencies: + js-tokens: 4.0.0 + + lower-case@2.0.2: + dependencies: + tslib: 2.8.1 + + lowlight@1.20.0: + dependencies: + fault: 1.0.4 + highlight.js: 10.7.3 + + lowlight@3.3.0: + dependencies: + '@types/hast': 3.0.4 + devlop: 1.1.0 + highlight.js: 11.11.1 + + lru-cache@10.4.3: {} + + lru-cache@4.1.5: + dependencies: + pseudomap: 1.0.2 + yallist: 2.1.2 + + lru-cache@5.1.1: + dependencies: + yallist: 3.1.1 + + lru-cache@6.0.0: + dependencies: + yallist: 4.0.0 + optional: true + + lru-cache@7.18.3: {} + + lru.min@1.1.2: {} + + lz-string@1.5.0: {} + + magic-string@0.25.9: + dependencies: + sourcemap-codec: 1.4.8 + + magic-string@0.27.0: + dependencies: + '@jridgewell/sourcemap-codec': 1.5.5 + + make-dir@3.1.0: + dependencies: + semver: 7.7.1 + + make-dir@4.0.0: + dependencies: + semver: 7.7.1 + + make-error@1.3.6: {} + + make-fetch-happen@9.1.0: + dependencies: + agentkeepalive: 4.6.0 + cacache: 15.3.0 + http-cache-semantics: 4.2.0 + http-proxy-agent: 4.0.1 + https-proxy-agent: 5.0.1 + is-lambda: 1.0.1 + lru-cache: 6.0.0 + minipass: 3.3.6 + minipass-collect: 1.0.2 + minipass-fetch: 1.4.1 + minipass-flush: 1.0.5 + minipass-pipeline: 1.2.4 + negotiator: 0.6.4 + promise-retry: 2.0.1 + socks-proxy-agent: 6.2.1 + ssri: 8.0.1 + transitivePeerDependencies: + - bluebird + - supports-color + optional: true + + makeerror@1.0.12: + dependencies: + tmpl: 1.0.5 + + map-stream@0.1.0: {} + + markdown-table@3.0.4: {} + + material-colors@1.2.6: {} + + math-intrinsics@1.1.0: {} + + mathjax-full@3.2.2: + dependencies: + esm: 3.2.25 + mhchemparser: 4.2.1 + mj-context-menu: 0.6.1 + speech-rule-engine: 4.1.2 + + mdast-util-definitions@5.1.2: + dependencies: + '@types/mdast': 3.0.15 + '@types/unist': 2.0.11 + unist-util-visit: 4.1.2 + + mdast-util-find-and-replace@2.2.2: + dependencies: + '@types/mdast': 3.0.15 + escape-string-regexp: 5.0.0 + unist-util-is: 5.2.1 + unist-util-visit-parents: 5.1.3 + + mdast-util-from-markdown@0.8.5: + dependencies: + '@types/mdast': 3.0.15 + mdast-util-to-string: 2.0.0 + micromark: 2.11.4 + parse-entities: 2.0.0 + unist-util-stringify-position: 2.0.3 + transitivePeerDependencies: + - supports-color + + mdast-util-from-markdown@1.3.1: + dependencies: + '@types/mdast': 3.0.15 + '@types/unist': 2.0.11 + decode-named-character-reference: 1.2.0 + mdast-util-to-string: 3.2.0 + micromark: 3.2.0 + micromark-util-decode-numeric-character-reference: 1.1.0 + micromark-util-decode-string: 1.1.0 + micromark-util-normalize-identifier: 1.1.0 + micromark-util-symbol: 1.1.0 + micromark-util-types: 1.1.0 + unist-util-stringify-position: 3.0.3 + uvu: 0.5.6 + transitivePeerDependencies: + - supports-color + + mdast-util-gfm-autolink-literal@1.0.3: + dependencies: + '@types/mdast': 3.0.15 + ccount: 2.0.1 + mdast-util-find-and-replace: 2.2.2 + micromark-util-character: 1.2.0 + + mdast-util-gfm-footnote@1.0.2: + dependencies: + '@types/mdast': 3.0.15 + mdast-util-to-markdown: 1.5.0 + micromark-util-normalize-identifier: 1.1.0 + + mdast-util-gfm-strikethrough@1.0.3: + dependencies: + '@types/mdast': 3.0.15 + mdast-util-to-markdown: 1.5.0 + + mdast-util-gfm-table@1.0.7: + dependencies: + '@types/mdast': 3.0.15 + markdown-table: 3.0.4 + mdast-util-from-markdown: 1.3.1 + mdast-util-to-markdown: 1.5.0 + transitivePeerDependencies: + - supports-color + + mdast-util-gfm-task-list-item@1.0.2: + dependencies: + '@types/mdast': 3.0.15 + mdast-util-to-markdown: 1.5.0 + + mdast-util-gfm@2.0.2: + dependencies: + mdast-util-from-markdown: 1.3.1 + mdast-util-gfm-autolink-literal: 1.0.3 + mdast-util-gfm-footnote: 1.0.2 + mdast-util-gfm-strikethrough: 1.0.3 + mdast-util-gfm-table: 1.0.7 + mdast-util-gfm-task-list-item: 1.0.2 + mdast-util-to-markdown: 1.5.0 + transitivePeerDependencies: + - supports-color + + mdast-util-math@2.0.2: + dependencies: + '@types/mdast': 3.0.15 + longest-streak: 3.1.0 + mdast-util-to-markdown: 1.5.0 + + mdast-util-phrasing@3.0.1: + dependencies: + '@types/mdast': 3.0.15 + unist-util-is: 5.2.1 + + mdast-util-to-hast@12.3.0: + dependencies: + '@types/hast': 2.3.10 + '@types/mdast': 3.0.15 + mdast-util-definitions: 5.1.2 + micromark-util-sanitize-uri: 1.2.0 + trim-lines: 3.0.1 + unist-util-generated: 2.0.1 + unist-util-position: 4.0.4 + unist-util-visit: 4.1.2 + + mdast-util-to-markdown@1.5.0: + dependencies: + '@types/mdast': 3.0.15 + '@types/unist': 2.0.11 + longest-streak: 3.1.0 + mdast-util-phrasing: 3.0.1 + mdast-util-to-string: 3.2.0 + micromark-util-decode-string: 1.1.0 + unist-util-visit: 4.1.2 + zwitch: 2.0.4 + + mdast-util-to-string@2.0.0: {} + + mdast-util-to-string@3.2.0: + dependencies: + '@types/mdast': 3.0.15 + + mdn-data@2.0.14: {} + + mdn-data@2.0.4: {} + + media-typer@0.3.0: {} + + memfs@3.5.3: + dependencies: + fs-monkey: 1.1.0 + + memfs@4.57.1(tslib@2.8.1): + dependencies: + '@jsonjoy.com/fs-core': 4.57.1(tslib@2.8.1) + '@jsonjoy.com/fs-fsa': 4.57.1(tslib@2.8.1) + '@jsonjoy.com/fs-node': 4.57.1(tslib@2.8.1) + '@jsonjoy.com/fs-node-builtins': 4.57.1(tslib@2.8.1) + '@jsonjoy.com/fs-node-to-fsa': 4.57.1(tslib@2.8.1) + '@jsonjoy.com/fs-node-utils': 4.57.1(tslib@2.8.1) + '@jsonjoy.com/fs-print': 4.57.1(tslib@2.8.1) + '@jsonjoy.com/fs-snapshot': 4.57.1(tslib@2.8.1) + '@jsonjoy.com/json-pack': 1.21.0(tslib@2.8.1) + '@jsonjoy.com/util': 1.9.0(tslib@2.8.1) + glob-to-regex.js: 1.2.0(tslib@2.8.1) + thingies: 2.6.0(tslib@2.8.1) + tree-dump: 1.1.0(tslib@2.8.1) + tslib: 2.8.1 + + memory-pager@1.5.0: + optional: true + + merge-descriptors@1.0.3: {} + + merge-stream@2.0.0: {} + + merge2@1.4.1: {} + + methods@1.1.2: {} + + mhchemparser@4.2.1: {} + + micromark-core-commonmark@1.1.0: + dependencies: + decode-named-character-reference: 1.2.0 + micromark-factory-destination: 1.1.0 + micromark-factory-label: 1.1.0 + micromark-factory-space: 1.1.0 + micromark-factory-title: 1.1.0 + micromark-factory-whitespace: 1.1.0 + micromark-util-character: 1.2.0 + micromark-util-chunked: 1.1.0 + micromark-util-classify-character: 1.1.0 + micromark-util-html-tag-name: 1.2.0 + micromark-util-normalize-identifier: 1.1.0 + micromark-util-resolve-all: 1.1.0 + micromark-util-subtokenize: 1.1.0 + micromark-util-symbol: 1.1.0 + micromark-util-types: 1.1.0 + uvu: 0.5.6 + + micromark-extension-gfm-autolink-literal@1.0.5: + dependencies: + micromark-util-character: 1.2.0 + micromark-util-sanitize-uri: 1.2.0 + micromark-util-symbol: 1.1.0 + micromark-util-types: 1.1.0 + + micromark-extension-gfm-footnote@1.1.2: + dependencies: + micromark-core-commonmark: 1.1.0 + micromark-factory-space: 1.1.0 + micromark-util-character: 1.2.0 + micromark-util-normalize-identifier: 1.1.0 + micromark-util-sanitize-uri: 1.2.0 + micromark-util-symbol: 1.1.0 + micromark-util-types: 1.1.0 + uvu: 0.5.6 + + micromark-extension-gfm-strikethrough@1.0.7: + dependencies: + micromark-util-chunked: 1.1.0 + micromark-util-classify-character: 1.1.0 + micromark-util-resolve-all: 1.1.0 + micromark-util-symbol: 1.1.0 + micromark-util-types: 1.1.0 + uvu: 0.5.6 + + micromark-extension-gfm-table@1.0.7: + dependencies: + micromark-factory-space: 1.1.0 + micromark-util-character: 1.2.0 + micromark-util-symbol: 1.1.0 + micromark-util-types: 1.1.0 + uvu: 0.5.6 + + micromark-extension-gfm-tagfilter@1.0.2: + dependencies: + micromark-util-types: 1.1.0 + + micromark-extension-gfm-task-list-item@1.0.5: + dependencies: + micromark-factory-space: 1.1.0 + micromark-util-character: 1.2.0 + micromark-util-symbol: 1.1.0 + micromark-util-types: 1.1.0 + uvu: 0.5.6 + + micromark-extension-gfm@2.0.3: + dependencies: + micromark-extension-gfm-autolink-literal: 1.0.5 + micromark-extension-gfm-footnote: 1.1.2 + micromark-extension-gfm-strikethrough: 1.0.7 + micromark-extension-gfm-table: 1.0.7 + micromark-extension-gfm-tagfilter: 1.0.2 + micromark-extension-gfm-task-list-item: 1.0.5 + micromark-util-combine-extensions: 1.1.0 + micromark-util-types: 1.1.0 + + micromark-extension-math@2.1.2: + dependencies: + '@types/katex': 0.16.7 + katex: 0.16.25 + micromark-factory-space: 1.1.0 + micromark-util-character: 1.2.0 + micromark-util-symbol: 1.1.0 + micromark-util-types: 1.1.0 + uvu: 0.5.6 + + micromark-factory-destination@1.1.0: + dependencies: + micromark-util-character: 1.2.0 + micromark-util-symbol: 1.1.0 + micromark-util-types: 1.1.0 + + micromark-factory-label@1.1.0: + dependencies: + micromark-util-character: 1.2.0 + micromark-util-symbol: 1.1.0 + micromark-util-types: 1.1.0 + uvu: 0.5.6 + + micromark-factory-space@1.1.0: + dependencies: + micromark-util-character: 1.2.0 + micromark-util-types: 1.1.0 + + micromark-factory-title@1.1.0: + dependencies: + micromark-factory-space: 1.1.0 + micromark-util-character: 1.2.0 + micromark-util-symbol: 1.1.0 + micromark-util-types: 1.1.0 + + micromark-factory-whitespace@1.1.0: + dependencies: + micromark-factory-space: 1.1.0 + micromark-util-character: 1.2.0 + micromark-util-symbol: 1.1.0 + micromark-util-types: 1.1.0 + + micromark-util-character@1.2.0: + dependencies: + micromark-util-symbol: 1.1.0 + micromark-util-types: 1.1.0 + + micromark-util-chunked@1.1.0: + dependencies: + micromark-util-symbol: 1.1.0 + + micromark-util-classify-character@1.1.0: + dependencies: + micromark-util-character: 1.2.0 + micromark-util-symbol: 1.1.0 + micromark-util-types: 1.1.0 + + micromark-util-combine-extensions@1.1.0: + dependencies: + micromark-util-chunked: 1.1.0 + micromark-util-types: 1.1.0 + + micromark-util-decode-numeric-character-reference@1.1.0: + dependencies: + micromark-util-symbol: 1.1.0 + + micromark-util-decode-string@1.1.0: + dependencies: + decode-named-character-reference: 1.2.0 + micromark-util-character: 1.2.0 + micromark-util-decode-numeric-character-reference: 1.1.0 + micromark-util-symbol: 1.1.0 + + micromark-util-encode@1.1.0: {} + + micromark-util-html-tag-name@1.2.0: {} + + micromark-util-normalize-identifier@1.1.0: + dependencies: + micromark-util-symbol: 1.1.0 + + micromark-util-resolve-all@1.1.0: + dependencies: + micromark-util-types: 1.1.0 + + micromark-util-sanitize-uri@1.2.0: + dependencies: + micromark-util-character: 1.2.0 + micromark-util-encode: 1.1.0 + micromark-util-symbol: 1.1.0 + + micromark-util-subtokenize@1.1.0: + dependencies: + micromark-util-chunked: 1.1.0 + micromark-util-symbol: 1.1.0 + micromark-util-types: 1.1.0 + uvu: 0.5.6 + + micromark-util-symbol@1.1.0: {} + + micromark-util-types@1.1.0: {} + + micromark@2.11.4: + dependencies: + debug: 4.4.3(supports-color@8.1.1) + parse-entities: 2.0.0 + transitivePeerDependencies: + - supports-color + + micromark@3.2.0: + dependencies: + '@types/debug': 4.1.12 + debug: 4.4.3(supports-color@8.1.1) + decode-named-character-reference: 1.2.0 + micromark-core-commonmark: 1.1.0 + micromark-factory-space: 1.1.0 + micromark-util-character: 1.2.0 + micromark-util-chunked: 1.1.0 + micromark-util-combine-extensions: 1.1.0 + micromark-util-decode-numeric-character-reference: 1.1.0 + micromark-util-encode: 1.1.0 + micromark-util-normalize-identifier: 1.1.0 + micromark-util-resolve-all: 1.1.0 + micromark-util-sanitize-uri: 1.2.0 + micromark-util-subtokenize: 1.1.0 + micromark-util-symbol: 1.1.0 + micromark-util-types: 1.1.0 + uvu: 0.5.6 + transitivePeerDependencies: + - supports-color + + micromatch@4.0.5: + dependencies: + braces: 3.0.3 + picomatch: 2.3.1 + + micromatch@4.0.8: + dependencies: + braces: 3.0.3 + picomatch: 2.3.1 + + mime-db@1.52.0: {} + + mime-db@1.54.0: {} + + mime-types@2.1.35: + dependencies: + mime-db: 1.52.0 + + mime@1.6.0: {} + + mimic-fn@2.1.0: {} + + mimic-fn@4.0.0: {} + + mimic-response@2.1.0: + optional: true + + mimic-response@3.1.0: {} + + min-indent@1.0.1: {} + + mini-css-extract-plugin@2.9.4(webpack@5.102.1): + dependencies: + schema-utils: 4.3.3 + tapable: 2.3.0 + webpack: 5.102.1 + + minimalistic-assert@1.0.1: {} + + minimatch@3.1.2: + dependencies: + brace-expansion: 1.1.12 + + minimatch@5.1.6: + dependencies: + brace-expansion: 2.0.2 + + minimatch@9.0.5: + dependencies: + brace-expansion: 2.0.2 + + minimist@1.2.8: {} + + minipass-collect@1.0.2: + dependencies: + minipass: 3.3.6 + optional: true + + minipass-fetch@1.4.1: + dependencies: + minipass: 3.3.6 + minipass-sized: 1.0.3 + minizlib: 2.1.2 + optionalDependencies: + encoding: 0.1.13 + optional: true + + minipass-flush@1.0.5: + dependencies: + minipass: 3.3.6 + optional: true + + minipass-pipeline@1.2.4: + dependencies: + minipass: 3.3.6 + optional: true + + minipass-sized@1.0.3: + dependencies: + minipass: 3.3.6 + optional: true + + minipass@3.3.6: + dependencies: + yallist: 4.0.0 + + minipass@5.0.0: {} + + minipass@7.1.2: {} + + minizlib@2.1.2: + dependencies: + minipass: 3.3.6 + yallist: 4.0.0 + + mj-context-menu@0.6.1: {} + + mkdirp-classic@0.5.3: {} + + mkdirp@0.5.6: + dependencies: + minimist: 1.2.8 + + mkdirp@1.0.4: {} + + moment-timezone@0.5.48: + dependencies: + moment: 2.30.1 + + moment@2.30.1: {} + + mongodb-connection-string-url@3.0.2: + dependencies: + '@types/whatwg-url': 11.0.5 + whatwg-url: 14.2.0 + optional: true + + mongodb@6.20.0(@aws-sdk/credential-providers@3.908.0)(socks@2.8.7): + dependencies: + '@mongodb-js/saslprep': 1.3.1 + bson: 6.10.4 + mongodb-connection-string-url: 3.0.2 + optionalDependencies: + '@aws-sdk/credential-providers': 3.908.0 + socks: 2.8.7 + optional: true + + mri@1.2.0: {} + + ms@2.0.0: {} + + ms@2.1.2: {} + + ms@2.1.3: {} + + multer@1.4.5-lts.2: + dependencies: + append-field: 1.0.0 + busboy: 1.6.0 + concat-stream: 1.6.2 + mkdirp: 0.5.6 + object-assign: 4.1.1 + type-is: 1.6.18 + xtend: 4.0.2 + + multicast-dns@7.2.5: + dependencies: + dns-packet: 5.6.1 + thunky: 1.1.0 + + mysql2@3.15.2: + dependencies: + aws-ssl-profiles: 1.1.2 + denque: 2.1.0 + generate-function: 2.3.1 + iconv-lite: 0.7.0 + long: 5.3.2 + lru.min: 1.1.2 + named-placeholders: 1.1.3 + seq-queue: 0.0.5 + sqlstring: 2.3.3 + + mz@2.7.0: + dependencies: + any-promise: 1.3.0 + object-assign: 4.1.1 + thenify-all: 1.6.0 + + named-placeholders@1.1.3: + dependencies: + lru-cache: 7.18.3 + + nan@2.23.0: + optional: true + + nanoclone@0.2.1: {} + + nanoid@3.3.11: {} + + napi-build-utils@2.0.0: {} + + natural-compare-lite@1.4.0: {} + + natural-compare@1.4.0: {} + + negotiator@0.6.3: {} + + negotiator@0.6.4: {} + + neo-async@2.6.2: {} + + no-case@3.0.4: + dependencies: + lower-case: 2.0.2 + tslib: 2.8.1 + + node-abi@3.78.0: + dependencies: + semver: 7.7.1 + + node-addon-api@7.1.1: {} + + node-cleanup@2.1.2: {} + + node-domexception@1.0.0: {} + + node-fetch@2.7.0(encoding@0.1.13): + dependencies: + whatwg-url: 5.0.0 + optionalDependencies: + encoding: 0.1.13 + + node-fetch@3.3.2: + dependencies: + data-uri-to-buffer: 4.0.1 + fetch-blob: 3.2.0 + formdata-polyfill: 4.0.10 + + node-forge@1.3.1: {} + + node-gyp-build@4.8.4: + optional: true + + node-gyp@8.4.1: + dependencies: + env-paths: 2.2.1 + glob: 10.5.0 + graceful-fs: 4.2.11 + make-fetch-happen: 9.1.0 + nopt: 5.0.0 + npmlog: 6.0.2 + rimraf: 3.0.2 + semver: 7.7.1 + tar: 6.2.1 + which: 2.0.2 + transitivePeerDependencies: + - bluebird + - supports-color + optional: true + + node-int64@0.4.0: {} + + node-releases@2.0.23: {} + + nodemon@2.0.22: + dependencies: + chokidar: 3.6.0 + debug: 3.2.7(supports-color@5.5.0) + ignore-by-default: 1.0.1 + minimatch: 3.1.2 + pstree.remy: 1.1.8 + semver: 7.7.1 + simple-update-notifier: 1.1.0 + supports-color: 5.5.0 + touch: 3.1.1 + undefsafe: 2.0.5 + + nopt@5.0.0: + dependencies: + abbrev: 1.1.1 + optional: true + + normalize-path@3.0.0: {} + + normalize-range@0.1.2: {} + + normalize-url@6.1.0: {} + + notistack@2.0.8(@emotion/react@11.14.0(@types/react@18.3.26)(react@18.3.1))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@18.3.26)(react@18.3.1))(@types/react@18.3.26)(react@18.3.1))(@mui/material@5.15.0(@emotion/react@11.14.0(@types/react@18.3.26)(react@18.3.1))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@18.3.26)(react@18.3.1))(@types/react@18.3.26)(react@18.3.1))(@types/react@18.3.26)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1): + dependencies: + '@mui/material': 5.15.0(@emotion/react@11.14.0(@types/react@18.3.26)(react@18.3.1))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@18.3.26)(react@18.3.1))(@types/react@18.3.26)(react@18.3.1))(@types/react@18.3.26)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + clsx: 1.2.1 + hoist-non-react-statics: 3.3.2 + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + optionalDependencies: + '@emotion/react': 11.14.0(@types/react@18.3.26)(react@18.3.1) + '@emotion/styled': 11.14.1(@emotion/react@11.14.0(@types/react@18.3.26)(react@18.3.1))(@types/react@18.3.26)(react@18.3.1) + + npm-run-path@1.0.0: + dependencies: + path-key: 1.0.0 + + npm-run-path@4.0.1: + dependencies: + path-key: 3.1.1 + + npm-run-path@5.3.0: + dependencies: + path-key: 4.0.0 + + npmlog@5.0.1: + dependencies: + are-we-there-yet: 2.0.0 + console-control-strings: 1.1.0 + gauge: 3.0.2 + set-blocking: 2.0.0 + optional: true + + npmlog@6.0.2: + dependencies: + are-we-there-yet: 3.0.1 + console-control-strings: 1.1.0 + gauge: 4.0.4 + set-blocking: 2.0.0 + optional: true + + nth-check@2.1.1: + dependencies: + boolbase: 1.0.0 + + nwsapi@2.2.22: {} + + object-assign@4.1.1: {} + + object-hash@3.0.0: {} + + object-inspect@1.13.4: {} + + object-is@1.1.6: + dependencies: + call-bind: 1.0.8 + define-properties: 1.2.1 + + object-keys@1.1.1: {} + + object.assign@4.1.7: + dependencies: + call-bind: 1.0.8 + call-bound: 1.0.4 + define-properties: 1.2.1 + es-object-atoms: 1.1.1 + has-symbols: 1.1.0 + object-keys: 1.1.1 + + object.entries@1.1.9: + dependencies: + call-bind: 1.0.8 + call-bound: 1.0.4 + define-properties: 1.2.1 + es-object-atoms: 1.1.1 + + object.fromentries@2.0.8: + dependencies: + call-bind: 1.0.8 + define-properties: 1.2.1 + es-abstract: 1.24.0 + es-object-atoms: 1.1.1 + + object.getownpropertydescriptors@2.1.8: + dependencies: + array.prototype.reduce: 1.0.8 + call-bind: 1.0.8 + define-properties: 1.2.1 + es-abstract: 1.24.0 + es-object-atoms: 1.1.1 + gopd: 1.2.0 + safe-array-concat: 1.1.3 + + object.groupby@1.0.3: + dependencies: + call-bind: 1.0.8 + define-properties: 1.2.1 + es-abstract: 1.24.0 + + object.values@1.2.1: + dependencies: + call-bind: 1.0.8 + call-bound: 1.0.4 + define-properties: 1.2.1 + es-object-atoms: 1.1.1 + + obuf@1.1.2: {} + + on-finished@2.4.1: + dependencies: + ee-first: 1.1.1 + + on-headers@1.1.0: {} + + once@1.4.0: + dependencies: + wrappy: 1.0.2 + + one-time@1.0.0: + dependencies: + fn.name: 1.1.0 + + onetime@5.1.2: + dependencies: + mimic-fn: 2.1.0 + + onetime@6.0.0: + dependencies: + mimic-fn: 4.0.0 + + open@8.4.2: + dependencies: + define-lazy-prop: 2.0.0 + is-docker: 2.2.1 + is-wsl: 2.2.0 + + optionator@0.8.3: + dependencies: + deep-is: 0.1.4 + fast-levenshtein: 2.0.6 + levn: 0.3.0 + prelude-ls: 1.1.2 + type-check: 0.3.2 + word-wrap: 1.2.5 + + optionator@0.9.4: + dependencies: + deep-is: 0.1.4 + fast-levenshtein: 2.0.6 + levn: 0.4.1 + prelude-ls: 1.2.1 + type-check: 0.4.0 + word-wrap: 1.2.5 + + ospath@1.2.2: {} + + own-keys@1.0.1: + dependencies: + get-intrinsic: 1.3.0 + object-keys: 1.1.1 + safe-push-apply: 1.0.0 + + p-limit@2.3.0: + dependencies: + p-try: 2.2.0 + + p-limit@3.1.0: + dependencies: + yocto-queue: 0.1.0 + + p-locate@3.0.0: + dependencies: + p-limit: 2.3.0 + + p-locate@4.1.0: + dependencies: + p-limit: 2.3.0 + + p-locate@5.0.0: + dependencies: + p-limit: 3.1.0 + + p-map@4.0.0: + dependencies: + aggregate-error: 3.1.0 + + p-retry@4.6.2: + dependencies: + '@types/retry': 0.12.0 + retry: 0.13.1 + + p-try@2.2.0: {} + + package-json-from-dist@1.0.1: {} + + param-case@3.0.4: + dependencies: + dot-case: 3.0.4 + tslib: 2.8.1 + + parent-module@1.0.1: + dependencies: + callsites: 3.1.0 + + parse-entities@2.0.0: + dependencies: + character-entities: 1.2.4 + character-entities-legacy: 1.1.4 + character-reference-invalid: 1.1.4 + is-alphanumerical: 1.0.4 + is-decimal: 1.0.4 + is-hexadecimal: 1.0.4 + + parse-json@5.2.0: + dependencies: + '@babel/code-frame': 7.27.1 + error-ex: 1.3.4 + json-parse-even-better-errors: 2.3.1 + lines-and-columns: 1.2.4 + + parse-srcset@1.0.2: {} + + parse5@6.0.1: {} + + parse5@7.3.0: + dependencies: + entities: 6.0.1 + + parseurl@1.3.3: {} + + pascal-case@3.1.2: + dependencies: + no-case: 3.0.4 + tslib: 2.8.1 + + path-exists@3.0.0: {} + + path-exists@4.0.0: {} + + path-key@1.0.0: {} + + path-key@3.1.1: {} + + path-key@4.0.0: {} + + path-parse@1.0.7: {} + + path-scurry@1.11.1: + dependencies: + lru-cache: 10.4.3 + minipass: 7.1.2 + + path-to-regexp@0.1.12: {} + + path-type@4.0.0: {} + + pause-stream@0.0.11: + dependencies: + through: 2.3.8 + + pend@1.2.0: {} + + perfect-scrollbar@1.5.6: {} + + performance-now@2.1.0: {} + + pg-cloudflare@1.2.7: + optional: true + + pg-connection-string@2.9.1: {} + + pg-int8@1.0.1: {} + + pg-pool@3.10.1(pg@8.16.3): + dependencies: + pg: 8.16.3 + + pg-protocol@1.10.3: {} + + pg-types@2.2.0: + dependencies: + pg-int8: 1.0.1 + postgres-array: 2.0.0 + postgres-bytea: 1.0.0 + postgres-date: 1.0.7 + postgres-interval: 1.2.0 + + pg@8.16.3: + dependencies: + pg-connection-string: 2.9.1 + pg-pool: 3.10.1(pg@8.16.3) + pg-protocol: 1.10.3 + pg-types: 2.2.0 + pgpass: 1.0.5 + optionalDependencies: + pg-cloudflare: 1.2.7 + + pgpass@1.0.5: + dependencies: + split2: 4.2.0 + + picocolors@0.2.1: {} + + picocolors@1.1.1: {} + + picomatch@2.3.1: {} + + picomatch@3.0.1: {} + + picomatch@4.0.3: {} + + pidtree@0.6.0: {} + + pify@2.3.0: {} + + pirates@4.0.7: {} + + pkg-dir@4.2.0: + dependencies: + find-up: 4.1.0 + + pkg-up@3.1.0: + dependencies: + find-up: 3.0.0 + + popmotion@9.3.6: + dependencies: + framesync: 5.3.0 + hey-listen: 1.0.8 + style-value-types: 4.1.4 + tslib: 2.8.1 + + possible-typed-array-names@1.1.0: {} + + postcss-attribute-case-insensitive@5.0.2(postcss@8.5.6): + dependencies: + postcss: 8.5.6 + postcss-selector-parser: 6.1.2 + + postcss-browser-comments@4.0.0(browserslist@4.26.3)(postcss@8.5.6): + dependencies: + browserslist: 4.26.3 + postcss: 8.5.6 + + postcss-calc@8.2.4(postcss@8.5.6): + dependencies: + postcss: 8.5.6 + postcss-selector-parser: 6.1.2 + postcss-value-parser: 4.2.0 + + postcss-clamp@4.1.0(postcss@8.5.6): + dependencies: + postcss: 8.5.6 + postcss-value-parser: 4.2.0 + + postcss-color-functional-notation@4.2.4(postcss@8.5.6): + dependencies: + postcss: 8.5.6 + postcss-value-parser: 4.2.0 + + postcss-color-hex-alpha@8.0.4(postcss@8.5.6): + dependencies: + postcss: 8.5.6 + postcss-value-parser: 4.2.0 + + postcss-color-rebeccapurple@7.1.1(postcss@8.5.6): + dependencies: + postcss: 8.5.6 + postcss-value-parser: 4.2.0 + + postcss-colormin@5.3.1(postcss@8.5.6): + dependencies: + browserslist: 4.26.3 + caniuse-api: 3.0.0 + colord: 2.9.3 + postcss: 8.5.6 + postcss-value-parser: 4.2.0 + + postcss-convert-values@5.1.3(postcss@8.5.6): + dependencies: + browserslist: 4.26.3 + postcss: 8.5.6 + postcss-value-parser: 4.2.0 + + postcss-custom-media@8.0.2(postcss@8.5.6): + dependencies: + postcss: 8.5.6 + postcss-value-parser: 4.2.0 + + postcss-custom-properties@12.1.11(postcss@8.5.6): + dependencies: + postcss: 8.5.6 + postcss-value-parser: 4.2.0 + + postcss-custom-selectors@6.0.3(postcss@8.5.6): + dependencies: + postcss: 8.5.6 + postcss-selector-parser: 6.1.2 + + postcss-dir-pseudo-class@6.0.5(postcss@8.5.6): + dependencies: + postcss: 8.5.6 + postcss-selector-parser: 6.1.2 + + postcss-discard-comments@5.1.2(postcss@8.5.6): + dependencies: + postcss: 8.5.6 + + postcss-discard-duplicates@5.1.0(postcss@8.5.6): + dependencies: + postcss: 8.5.6 + + postcss-discard-empty@5.1.1(postcss@8.5.6): + dependencies: + postcss: 8.5.6 + + postcss-discard-overridden@5.1.0(postcss@8.5.6): + dependencies: + postcss: 8.5.6 + + postcss-double-position-gradients@3.1.2(postcss@8.5.6): + dependencies: + '@csstools/postcss-progressive-custom-properties': 1.3.0(postcss@8.5.6) + postcss: 8.5.6 + postcss-value-parser: 4.2.0 + + postcss-env-function@4.0.6(postcss@8.5.6): + dependencies: + postcss: 8.5.6 + postcss-value-parser: 4.2.0 + + postcss-flexbugs-fixes@5.0.2(postcss@8.5.6): + dependencies: + postcss: 8.5.6 + + postcss-focus-visible@6.0.4(postcss@8.5.6): + dependencies: + postcss: 8.5.6 + postcss-selector-parser: 6.1.2 + + postcss-focus-within@5.0.4(postcss@8.5.6): + dependencies: + postcss: 8.5.6 + postcss-selector-parser: 6.1.2 + + postcss-font-variant@5.0.0(postcss@8.5.6): + dependencies: + postcss: 8.5.6 + + postcss-gap-properties@3.0.5(postcss@8.5.6): + dependencies: + postcss: 8.5.6 + + postcss-image-set-function@4.0.7(postcss@8.5.6): + dependencies: + postcss: 8.5.6 + postcss-value-parser: 4.2.0 + + postcss-import@15.1.0(postcss@8.5.6): + dependencies: + postcss: 8.5.6 + postcss-value-parser: 4.2.0 + read-cache: 1.0.0 + resolve: 1.22.10 + + postcss-initial@4.0.1(postcss@8.5.6): + dependencies: + postcss: 8.5.6 + + postcss-js@4.1.0(postcss@8.5.6): + dependencies: + camelcase-css: 2.0.1 + postcss: 8.5.6 + + postcss-lab-function@4.2.1(postcss@8.5.6): + dependencies: + '@csstools/postcss-progressive-custom-properties': 1.3.0(postcss@8.5.6) + postcss: 8.5.6 + postcss-value-parser: 4.2.0 + + postcss-load-config@6.0.1(jiti@1.21.7)(postcss@8.5.6)(yaml@2.8.1): + dependencies: + lilconfig: 3.1.3 + optionalDependencies: + jiti: 1.21.7 + postcss: 8.5.6 + yaml: 2.8.1 + + postcss-loader@6.2.1(postcss@8.5.6)(webpack@5.102.1): + dependencies: + cosmiconfig: 7.1.0 + klona: 2.0.6 + postcss: 8.5.6 + semver: 7.7.1 + webpack: 5.102.1 + + postcss-logical@5.0.4(postcss@8.5.6): + dependencies: + postcss: 8.5.6 + + postcss-media-minmax@5.0.0(postcss@8.5.6): + dependencies: + postcss: 8.5.6 + + postcss-merge-longhand@5.1.7(postcss@8.5.6): + dependencies: + postcss: 8.5.6 + postcss-value-parser: 4.2.0 + stylehacks: 5.1.1(postcss@8.5.6) + + postcss-merge-rules@5.1.4(postcss@8.5.6): + dependencies: + browserslist: 4.26.3 + caniuse-api: 3.0.0 + cssnano-utils: 3.1.0(postcss@8.5.6) + postcss: 8.5.6 + postcss-selector-parser: 6.1.2 + + postcss-minify-font-values@5.1.0(postcss@8.5.6): + dependencies: + postcss: 8.5.6 + postcss-value-parser: 4.2.0 + + postcss-minify-gradients@5.1.1(postcss@8.5.6): + dependencies: + colord: 2.9.3 + cssnano-utils: 3.1.0(postcss@8.5.6) + postcss: 8.5.6 + postcss-value-parser: 4.2.0 + + postcss-minify-params@5.1.4(postcss@8.5.6): + dependencies: + browserslist: 4.26.3 + cssnano-utils: 3.1.0(postcss@8.5.6) + postcss: 8.5.6 + postcss-value-parser: 4.2.0 + + postcss-minify-selectors@5.2.1(postcss@8.5.6): + dependencies: + postcss: 8.5.6 + postcss-selector-parser: 6.1.2 + + postcss-modules-extract-imports@3.1.0(postcss@8.5.6): + dependencies: + postcss: 8.5.6 + + postcss-modules-local-by-default@4.2.0(postcss@8.5.6): + dependencies: + icss-utils: 5.1.0(postcss@8.5.6) + postcss: 8.5.6 + postcss-selector-parser: 7.1.0 + postcss-value-parser: 4.2.0 + + postcss-modules-scope@3.2.1(postcss@8.5.6): + dependencies: + postcss: 8.5.6 + postcss-selector-parser: 7.1.0 + + postcss-modules-values@4.0.0(postcss@8.5.6): + dependencies: + icss-utils: 5.1.0(postcss@8.5.6) + postcss: 8.5.6 + + postcss-nested@6.2.0(postcss@8.5.6): + dependencies: + postcss: 8.5.6 + postcss-selector-parser: 6.1.2 + + postcss-nesting@10.2.0(postcss@8.5.6): + dependencies: + '@csstools/selector-specificity': 2.2.0(postcss-selector-parser@6.1.2) + postcss: 8.5.6 + postcss-selector-parser: 6.1.2 + + postcss-normalize-charset@5.1.0(postcss@8.5.6): + dependencies: + postcss: 8.5.6 + + postcss-normalize-display-values@5.1.0(postcss@8.5.6): + dependencies: + postcss: 8.5.6 + postcss-value-parser: 4.2.0 + + postcss-normalize-positions@5.1.1(postcss@8.5.6): + dependencies: + postcss: 8.5.6 + postcss-value-parser: 4.2.0 + + postcss-normalize-repeat-style@5.1.1(postcss@8.5.6): + dependencies: + postcss: 8.5.6 + postcss-value-parser: 4.2.0 + + postcss-normalize-string@5.1.0(postcss@8.5.6): + dependencies: + postcss: 8.5.6 + postcss-value-parser: 4.2.0 + + postcss-normalize-timing-functions@5.1.0(postcss@8.5.6): + dependencies: + postcss: 8.5.6 + postcss-value-parser: 4.2.0 + + postcss-normalize-unicode@5.1.1(postcss@8.5.6): + dependencies: + browserslist: 4.26.3 + postcss: 8.5.6 + postcss-value-parser: 4.2.0 + + postcss-normalize-url@5.1.0(postcss@8.5.6): + dependencies: + normalize-url: 6.1.0 + postcss: 8.5.6 + postcss-value-parser: 4.2.0 + + postcss-normalize-whitespace@5.1.1(postcss@8.5.6): + dependencies: + postcss: 8.5.6 + postcss-value-parser: 4.2.0 + + postcss-normalize@10.0.1(browserslist@4.26.3)(postcss@8.5.6): + dependencies: + '@csstools/normalize.css': 12.1.1 + browserslist: 4.26.3 + postcss: 8.5.6 + postcss-browser-comments: 4.0.0(browserslist@4.26.3)(postcss@8.5.6) + sanitize.css: 13.0.0 + + postcss-opacity-percentage@1.1.3(postcss@8.5.6): + dependencies: + postcss: 8.5.6 + + postcss-ordered-values@5.1.3(postcss@8.5.6): + dependencies: + cssnano-utils: 3.1.0(postcss@8.5.6) + postcss: 8.5.6 + postcss-value-parser: 4.2.0 + + postcss-overflow-shorthand@3.0.4(postcss@8.5.6): + dependencies: + postcss: 8.5.6 + postcss-value-parser: 4.2.0 + + postcss-page-break@3.0.4(postcss@8.5.6): + dependencies: + postcss: 8.5.6 + + postcss-place@7.0.5(postcss@8.5.6): + dependencies: + postcss: 8.5.6 + postcss-value-parser: 4.2.0 + + postcss-preset-env@7.8.3(postcss@8.5.6): + dependencies: + '@csstools/postcss-cascade-layers': 1.1.1(postcss@8.5.6) + '@csstools/postcss-color-function': 1.1.1(postcss@8.5.6) + '@csstools/postcss-font-format-keywords': 1.0.1(postcss@8.5.6) + '@csstools/postcss-hwb-function': 1.0.2(postcss@8.5.6) + '@csstools/postcss-ic-unit': 1.0.1(postcss@8.5.6) + '@csstools/postcss-is-pseudo-class': 2.0.7(postcss@8.5.6) + '@csstools/postcss-nested-calc': 1.0.0(postcss@8.5.6) + '@csstools/postcss-normalize-display-values': 1.0.1(postcss@8.5.6) + '@csstools/postcss-oklab-function': 1.1.1(postcss@8.5.6) + '@csstools/postcss-progressive-custom-properties': 1.3.0(postcss@8.5.6) + '@csstools/postcss-stepped-value-functions': 1.0.1(postcss@8.5.6) + '@csstools/postcss-text-decoration-shorthand': 1.0.0(postcss@8.5.6) + '@csstools/postcss-trigonometric-functions': 1.0.2(postcss@8.5.6) + '@csstools/postcss-unset-value': 1.0.2(postcss@8.5.6) + autoprefixer: 10.4.21(postcss@8.5.6) + browserslist: 4.26.3 + css-blank-pseudo: 3.0.3(postcss@8.5.6) + css-has-pseudo: 3.0.4(postcss@8.5.6) + css-prefers-color-scheme: 6.0.3(postcss@8.5.6) + cssdb: 7.11.2 + postcss: 8.5.6 + postcss-attribute-case-insensitive: 5.0.2(postcss@8.5.6) + postcss-clamp: 4.1.0(postcss@8.5.6) + postcss-color-functional-notation: 4.2.4(postcss@8.5.6) + postcss-color-hex-alpha: 8.0.4(postcss@8.5.6) + postcss-color-rebeccapurple: 7.1.1(postcss@8.5.6) + postcss-custom-media: 8.0.2(postcss@8.5.6) + postcss-custom-properties: 12.1.11(postcss@8.5.6) + postcss-custom-selectors: 6.0.3(postcss@8.5.6) + postcss-dir-pseudo-class: 6.0.5(postcss@8.5.6) + postcss-double-position-gradients: 3.1.2(postcss@8.5.6) + postcss-env-function: 4.0.6(postcss@8.5.6) + postcss-focus-visible: 6.0.4(postcss@8.5.6) + postcss-focus-within: 5.0.4(postcss@8.5.6) + postcss-font-variant: 5.0.0(postcss@8.5.6) + postcss-gap-properties: 3.0.5(postcss@8.5.6) + postcss-image-set-function: 4.0.7(postcss@8.5.6) + postcss-initial: 4.0.1(postcss@8.5.6) + postcss-lab-function: 4.2.1(postcss@8.5.6) + postcss-logical: 5.0.4(postcss@8.5.6) + postcss-media-minmax: 5.0.0(postcss@8.5.6) + postcss-nesting: 10.2.0(postcss@8.5.6) + postcss-opacity-percentage: 1.1.3(postcss@8.5.6) + postcss-overflow-shorthand: 3.0.4(postcss@8.5.6) + postcss-page-break: 3.0.4(postcss@8.5.6) + postcss-place: 7.0.5(postcss@8.5.6) + postcss-pseudo-class-any-link: 7.1.6(postcss@8.5.6) + postcss-replace-overflow-wrap: 4.0.0(postcss@8.5.6) + postcss-selector-not: 6.0.1(postcss@8.5.6) + postcss-value-parser: 4.2.0 + + postcss-pseudo-class-any-link@7.1.6(postcss@8.5.6): + dependencies: + postcss: 8.5.6 + postcss-selector-parser: 6.1.2 + + postcss-reduce-initial@5.1.2(postcss@8.5.6): + dependencies: + browserslist: 4.26.3 + caniuse-api: 3.0.0 + postcss: 8.5.6 + + postcss-reduce-transforms@5.1.0(postcss@8.5.6): + dependencies: + postcss: 8.5.6 + postcss-value-parser: 4.2.0 + + postcss-replace-overflow-wrap@4.0.0(postcss@8.5.6): + dependencies: + postcss: 8.5.6 + + postcss-selector-not@6.0.1(postcss@8.5.6): + dependencies: + postcss: 8.5.6 + postcss-selector-parser: 6.1.2 + + postcss-selector-parser@6.1.2: + dependencies: + cssesc: 3.0.0 + util-deprecate: 1.0.2 + + postcss-selector-parser@7.1.0: + dependencies: + cssesc: 3.0.0 + util-deprecate: 1.0.2 + + postcss-svgo@5.1.0(postcss@8.5.6): + dependencies: + postcss: 8.5.6 + postcss-value-parser: 4.2.0 + svgo: 2.8.0 + + postcss-unique-selectors@5.1.1(postcss@8.5.6): + dependencies: + postcss: 8.5.6 + postcss-selector-parser: 6.1.2 + + postcss-value-parser@4.2.0: {} + + postcss@7.0.39: + dependencies: + picocolors: 0.2.1 + source-map: 0.6.1 + + postcss@8.5.6: + dependencies: + nanoid: 3.3.11 + picocolors: 1.1.1 + source-map-js: 1.2.1 + + postgres-array@2.0.0: {} + + postgres-bytea@1.0.0: {} + + postgres-date@1.0.7: {} + + postgres-interval@1.2.0: + dependencies: + xtend: 4.0.2 + + prebuild-install@7.1.3: + dependencies: + detect-libc: 2.1.2 + expand-template: 2.0.3 + github-from-package: 0.0.0 + minimist: 1.2.8 + mkdirp-classic: 0.5.3 + napi-build-utils: 2.0.0 + node-abi: 3.78.0 + pump: 3.0.3 + rc: 1.2.8 + simple-get: 4.0.1 + tar-fs: 3.1.1 + tunnel-agent: 0.6.0 + transitivePeerDependencies: + - bare-abort-controller + - bare-buffer + - react-native-b4a + + prelude-ls@1.1.2: {} + + prelude-ls@1.2.1: {} + + prettier-linter-helpers@1.0.0: + dependencies: + fast-diff: 1.3.0 + + prettier@2.8.8: {} + + prettier@3.6.2: {} + + pretty-bytes@5.6.0: {} + + pretty-bytes@6.1.1: {} + + pretty-error@4.0.0: + dependencies: + lodash: 4.17.21 + renderkid: 3.0.0 + + pretty-format@27.5.1: + dependencies: + ansi-regex: 5.0.1 + ansi-styles: 5.2.0 + react-is: 17.0.2 + + pretty-format@28.1.3: + dependencies: + '@jest/schemas': 28.1.3 + ansi-regex: 5.0.1 + ansi-styles: 5.2.0 + react-is: 18.3.1 + + pretty-format@30.2.0: + dependencies: + '@jest/schemas': 30.0.5 + ansi-styles: 5.2.0 + react-is: 18.3.1 + + pretty-quick@3.3.1(prettier@2.8.8): + dependencies: + execa: 4.1.0 + find-up: 4.1.0 + ignore: 5.3.2 + mri: 1.2.0 + picocolors: 1.1.1 + picomatch: 3.0.1 + prettier: 2.8.8 + tslib: 2.8.1 + + pretty-quick@3.3.1(prettier@3.6.2): + dependencies: + execa: 4.1.0 + find-up: 4.1.0 + ignore: 5.3.2 + mri: 1.2.0 + picocolors: 1.1.1 + picomatch: 3.0.1 + prettier: 3.6.2 + tslib: 2.8.1 + + prism-react-renderer@1.3.5(react@18.3.1): + dependencies: + react: 18.3.1 + + prismjs@1.29.0: {} + + process-nextick-args@2.0.1: {} + + process@0.11.10: {} + + promise-inflight@1.0.1: + optional: true + + promise-retry@2.0.1: + dependencies: + err-code: 2.0.3 + retry: 0.12.0 + optional: true + + promise@7.3.1: + dependencies: + asap: 2.0.6 + + promise@8.3.0: + dependencies: + asap: 2.0.6 + + prompts@2.4.2: + dependencies: + kleur: 3.0.3 + sisteransi: 1.0.5 + + prop-types@15.8.1: + dependencies: + loose-envify: 1.4.0 + object-assign: 4.1.1 + react-is: 16.13.1 + + property-expr@2.0.6: {} + + property-information@5.6.0: + dependencies: + xtend: 4.0.2 + + property-information@6.5.0: {} + + protobufjs@7.4.0: + dependencies: + '@protobufjs/aspromise': 1.1.2 + '@protobufjs/base64': 1.1.2 + '@protobufjs/codegen': 2.0.4 + '@protobufjs/eventemitter': 1.1.0 + '@protobufjs/fetch': 1.1.0 + '@protobufjs/float': 1.0.2 + '@protobufjs/inquire': 1.1.0 + '@protobufjs/path': 1.1.2 + '@protobufjs/pool': 1.1.0 + '@protobufjs/utf8': 1.1.0 + '@types/node': 24.7.2 + long: 5.3.2 + + proxy-addr@2.0.7: + dependencies: + forwarded: 0.2.0 + ipaddr.js: 1.9.1 + + proxy-from-env@1.0.0: {} + + proxy-from-env@1.1.0: {} + + ps-tree@1.2.0: + dependencies: + event-stream: 3.3.4 + + pseudomap@1.0.2: {} + + psl@1.15.0: + dependencies: + punycode: 2.3.1 + + pstree.remy@1.1.8: {} + + pump@3.0.3: + dependencies: + end-of-stream: 1.4.5 + once: 1.4.0 + + punycode@2.3.1: {} + + pure-color@1.3.0: {} + + q@1.5.1: {} + + qs@6.13.0: + dependencies: + side-channel: 1.1.0 + + qs@6.14.0: + dependencies: + side-channel: 1.1.0 + + query-string@8.2.0: + dependencies: + decode-uri-component: 0.4.1 + filter-obj: 5.1.0 + split-on-first: 3.0.0 + + querystringify@2.2.0: {} + + queue-microtask@1.2.3: {} + + raf@3.4.1: + dependencies: + performance-now: 2.1.0 + + randombytes@2.1.0: + dependencies: + safe-buffer: 5.2.1 + + range-parser@1.2.1: {} + + raw-body@3.0.1: + dependencies: + bytes: 3.1.2 + http-errors: 2.0.0 + iconv-lite: 0.7.0 + unpipe: 1.0.0 + + rc@1.2.8: + dependencies: + deep-extend: 0.6.0 + ini: 1.3.8 + minimist: 1.2.8 + strip-json-comments: 2.0.1 + + react-app-polyfill@3.0.0: + dependencies: + core-js: 3.46.0 + object-assign: 4.1.1 + promise: 8.3.0 + raf: 3.4.1 + regenerator-runtime: 0.13.11 + whatwg-fetch: 3.6.20 + + react-base16-styling@0.6.0: + dependencies: + base16: 1.0.0 + lodash.curry: 4.1.1 + lodash.flow: 3.5.0 + pure-color: 1.3.0 + + react-code-blocks@0.1.6(css-to-react-native@3.2.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1): + dependencies: + '@babel/runtime': 7.28.4 + react: 18.3.1 + react-syntax-highlighter: 15.6.6(react@18.3.1) + styled-components: 6.4.0(css-to-react-native@3.2.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + tslib: 2.8.1 + transitivePeerDependencies: + - css-to-react-native + - react-dom + - react-native + + react-color@2.19.3(react@18.3.1): + dependencies: + '@icons/material': 0.2.4(react@18.3.1) + lodash: 4.17.21 + lodash-es: 4.17.21 + material-colors: 1.2.6 + prop-types: 15.8.1 + react: 18.3.1 + reactcss: 1.2.3(react@18.3.1) + tinycolor2: 1.6.0 + + react-datepicker@4.25.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1): + dependencies: + '@popperjs/core': 2.11.8 + classnames: 2.5.1 + date-fns: 2.30.0 + prop-types: 15.8.1 + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + react-onclickoutside: 6.13.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + react-popper: 2.3.0(@popperjs/core@2.11.8)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + + react-dev-utils@12.0.1(eslint@8.57.1)(typescript@5.9.3)(webpack@5.102.1): + dependencies: + '@babel/code-frame': 7.27.1 + address: 1.2.2 + browserslist: 4.26.3 + chalk: 4.1.2 + cross-spawn: 7.0.6 + detect-port-alt: 1.1.6 + escape-string-regexp: 4.0.0 + filesize: 8.0.7 + find-up: 5.0.0 + fork-ts-checker-webpack-plugin: 6.5.3(eslint@8.57.1)(typescript@5.9.3)(webpack@5.102.1) + global-modules: 2.0.0 + globby: 11.1.0 + gzip-size: 6.0.0 + immer: 9.0.21 + is-root: 2.1.0 + loader-utils: 3.3.1 + open: 8.4.2 + pkg-up: 3.1.0 + prompts: 2.4.2 + react-error-overlay: 6.1.0 + recursive-readdir: 2.2.3 + shell-quote: 1.8.3 + strip-ansi: 6.0.1 + text-table: 0.2.0 + webpack: 5.102.1 + optionalDependencies: + typescript: 5.9.3 + transitivePeerDependencies: + - eslint + - supports-color + - vue-template-compiler + + react-device-detect@1.17.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1): + dependencies: + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + ua-parser-js: 0.7.41 + + react-dom@18.3.1(react@18.3.1): + dependencies: + loose-envify: 1.4.0 + react: 18.3.1 + scheduler: 0.23.2 + + react-error-overlay@6.1.0: {} + + react-fast-compare@2.0.4: {} + + react-fast-compare@3.2.2: {} + + react-frame-component@5.2.7(prop-types@15.8.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1): + dependencies: + prop-types: 15.8.1 + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + + react-inspector@6.0.2(react@18.3.1): + dependencies: + react: 18.3.1 + + react-is@16.13.1: {} + + react-is@17.0.2: {} + + react-is@18.3.1: {} + + react-is@19.2.0: {} + + react-lifecycles-compat@3.0.4: {} + + react-markdown@8.0.7(@types/react@18.3.26)(react@18.3.1): + dependencies: + '@types/hast': 2.3.10 + '@types/prop-types': 15.7.15 + '@types/react': 18.3.26 + '@types/unist': 2.0.11 + comma-separated-tokens: 2.0.3 + hast-util-whitespace: 2.0.1 + prop-types: 15.8.1 + property-information: 6.5.0 + react: 18.3.1 + react-is: 18.3.1 + remark-parse: 10.0.2 + remark-rehype: 10.1.0 + space-separated-tokens: 2.0.2 + style-to-object: 0.4.4 + unified: 10.1.2 + unist-util-visit: 4.1.2 + vfile: 5.3.7 + transitivePeerDependencies: + - supports-color + + react-onclickoutside@6.13.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1): + dependencies: + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + + react-perfect-scrollbar@1.5.8(react-dom@18.3.1(react@18.3.1))(react@18.3.1): + dependencies: + perfect-scrollbar: 1.5.6 + prop-types: 15.8.1 + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + + react-popper@2.3.0(@popperjs/core@2.11.8)(react-dom@18.3.1(react@18.3.1))(react@18.3.1): + dependencies: + '@popperjs/core': 2.11.8 + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + react-fast-compare: 3.2.2 + warning: 4.0.3 + + react-property@2.0.0: {} + + react-redux@8.1.3(@types/react-dom@18.3.7(@types/react@18.3.26))(@types/react@18.3.26)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(redux@4.2.1): + dependencies: + '@babel/runtime': 7.28.4 + '@types/hoist-non-react-statics': 3.3.7(@types/react@18.3.26) + '@types/use-sync-external-store': 0.0.3 + hoist-non-react-statics: 3.3.2 + react: 18.3.1 + react-is: 18.3.1 + use-sync-external-store: 1.6.0(react@18.3.1) + optionalDependencies: + '@types/react': 18.3.26 + '@types/react-dom': 18.3.7(@types/react@18.3.26) + react-dom: 18.3.1(react@18.3.1) + redux: 4.2.1 + + react-refresh@0.11.0: {} + + react-refresh@0.14.2: {} + + react-refresh@0.17.0: {} + + react-router-dom@6.3.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1): + dependencies: + history: 5.3.0 + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + react-router: 6.3.0(react@18.3.1) + + react-router@6.3.0(react@18.3.1): + dependencies: + history: 5.3.0 + react: 18.3.1 + + react-scripts@5.0.1(@babel/plugin-syntax-flow@7.27.1(@babel/core@7.28.4))(@babel/plugin-transform-react-jsx@7.27.1(@babel/core@7.28.4))(@types/babel__core@7.20.5)(bufferutil@4.0.9)(canvas@2.11.2(encoding@0.1.13))(eslint@8.57.1)(react@18.3.1)(sass@1.93.2)(ts-node@10.9.2(@types/node@24.7.2)(typescript@5.9.3))(tslib@2.8.1)(type-fest@4.41.0)(typescript@5.9.3)(utf-8-validate@6.0.5)(yaml@2.8.1): + dependencies: + '@babel/core': 7.28.4 + '@pmmmwh/react-refresh-webpack-plugin': 0.5.17(react-refresh@0.11.0)(type-fest@4.41.0)(webpack-dev-server@4.15.2(bufferutil@4.0.9)(tslib@2.8.1)(utf-8-validate@6.0.5)(webpack@5.102.1))(webpack@5.102.1) + '@svgr/webpack': 5.5.0 + babel-jest: 27.5.1(@babel/core@7.28.4) + babel-loader: 8.4.1(@babel/core@7.28.4)(webpack@5.102.1) + babel-plugin-named-asset-import: 0.3.8(@babel/core@7.28.4) + babel-preset-react-app: 10.1.0 + bfj: 7.1.0 + browserslist: 4.26.3 + camelcase: 6.3.0 + case-sensitive-paths-webpack-plugin: 2.4.0 + css-loader: 6.11.0(webpack@5.102.1) + css-minimizer-webpack-plugin: 3.4.1(webpack@5.102.1) + dotenv: 10.0.0 + dotenv-expand: 5.1.0 + eslint: 8.57.1 + eslint-config-react-app: 7.0.1(@babel/plugin-syntax-flow@7.27.1(@babel/core@7.28.4))(@babel/plugin-transform-react-jsx@7.27.1(@babel/core@7.28.4))(eslint@8.57.1)(jest@27.5.1(bufferutil@4.0.9)(canvas@2.11.2(encoding@0.1.13))(ts-node@10.9.2(@types/node@24.7.2)(typescript@5.9.3))(utf-8-validate@6.0.5))(typescript@5.9.3) + eslint-webpack-plugin: 3.2.0(eslint@8.57.1)(webpack@5.102.1) + file-loader: 6.2.0(webpack@5.102.1) + fs-extra: 10.1.0 + html-webpack-plugin: 5.6.4(webpack@5.102.1) + identity-obj-proxy: 3.0.0 + jest: 27.5.1(bufferutil@4.0.9)(canvas@2.11.2(encoding@0.1.13))(ts-node@10.9.2(@types/node@24.7.2)(typescript@5.9.3))(utf-8-validate@6.0.5) + jest-resolve: 27.5.1 + jest-watch-typeahead: 1.1.0(jest@27.5.1(bufferutil@4.0.9)(canvas@2.11.2(encoding@0.1.13))(ts-node@10.9.2(@types/node@24.7.2)(typescript@5.9.3))(utf-8-validate@6.0.5)) + mini-css-extract-plugin: 2.9.4(webpack@5.102.1) + postcss: 8.5.6 + postcss-flexbugs-fixes: 5.0.2(postcss@8.5.6) + postcss-loader: 6.2.1(postcss@8.5.6)(webpack@5.102.1) + postcss-normalize: 10.0.1(browserslist@4.26.3)(postcss@8.5.6) + postcss-preset-env: 7.8.3(postcss@8.5.6) + prompts: 2.4.2 + react: 18.3.1 + react-app-polyfill: 3.0.0 + react-dev-utils: 12.0.1(eslint@8.57.1)(typescript@5.9.3)(webpack@5.102.1) + react-refresh: 0.11.0 + resolve: 1.22.10 + resolve-url-loader: 4.0.0 + sass-loader: 12.6.0(sass@1.93.2)(webpack@5.102.1) + semver: 7.7.1 + source-map-loader: 3.0.2(webpack@5.102.1) + style-loader: 3.3.4(webpack@5.102.1) + tailwindcss: 3.4.18(yaml@2.8.1) + terser-webpack-plugin: 5.3.14(webpack@5.102.1) + webpack: 5.102.1 + webpack-dev-server: 4.15.2(bufferutil@4.0.9)(tslib@2.8.1)(utf-8-validate@6.0.5)(webpack@5.102.1) + webpack-manifest-plugin: 4.1.1(webpack@5.102.1) + workbox-webpack-plugin: 6.6.0(@types/babel__core@7.20.5)(webpack@5.102.1) + optionalDependencies: + fsevents: 2.3.3 + typescript: 5.9.3 + transitivePeerDependencies: + - '@babel/plugin-syntax-flow' + - '@babel/plugin-transform-react-jsx' + - '@parcel/css' + - '@rspack/core' + - '@swc/core' + - '@types/babel__core' + - '@types/webpack' + - bufferutil + - canvas + - clean-css + - csso + - esbuild + - eslint-import-resolver-typescript + - eslint-import-resolver-webpack + - fibers + - node-notifier + - node-sass + - rework + - rework-visit + - sass + - sass-embedded + - sockjs-client + - supports-color + - ts-node + - tslib + - tsx + - type-fest + - uglify-js + - utf-8-validate + - vue-template-compiler + - webpack-cli + - webpack-hot-middleware + - webpack-plugin-serve + - yaml + + react-smooth@4.0.4(react-dom@18.3.1(react@18.3.1))(react@18.3.1): + dependencies: + fast-equals: 5.4.0 + prop-types: 15.8.1 + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + react-transition-group: 4.4.5(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + + react-syntax-highlighter@15.6.6(react@18.3.1): + dependencies: + '@babel/runtime': 7.28.4 + highlight.js: 10.7.3 + highlightjs-vue: 1.0.0 + lowlight: 1.20.0 + prismjs: 1.29.0 + react: 18.3.1 + refractor: 3.6.0 + + react-textarea-autosize@8.5.9(@types/react@18.3.26)(react@18.3.1): + dependencies: + '@babel/runtime': 7.28.4 + react: 18.3.1 + use-composed-ref: 1.4.0(@types/react@18.3.26)(react@18.3.1) + use-latest: 1.3.0(@types/react@18.3.26)(react@18.3.1) + transitivePeerDependencies: + - '@types/react' + + react-toastify@11.0.5(react-dom@18.3.1(react@18.3.1))(react@18.3.1): + dependencies: + clsx: 2.1.1 + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + + react-transition-group@4.4.5(react-dom@18.3.1(react@18.3.1))(react@18.3.1): + dependencies: + '@babel/runtime': 7.28.4 + dom-helpers: 5.2.1 + loose-envify: 1.4.0 + prop-types: 15.8.1 + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + + react@18.3.1: + dependencies: + loose-envify: 1.4.0 + + reactcss@1.2.3(react@18.3.1): + dependencies: + lodash: 4.17.21 + react: 18.3.1 + + reactflow@11.11.4(@types/react@18.3.26)(immer@11.1.4)(react-dom@18.3.1(react@18.3.1))(react@18.3.1): + dependencies: + '@reactflow/background': 11.3.14(@types/react@18.3.26)(immer@11.1.4)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@reactflow/controls': 11.2.14(@types/react@18.3.26)(immer@11.1.4)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@reactflow/core': 11.11.4(@types/react@18.3.26)(immer@11.1.4)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@reactflow/minimap': 11.7.14(@types/react@18.3.26)(immer@11.1.4)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@reactflow/node-resizer': 2.2.14(@types/react@18.3.26)(immer@11.1.4)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@reactflow/node-toolbar': 1.3.14(@types/react@18.3.26)(immer@11.1.4)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + transitivePeerDependencies: + - '@types/react' + - immer + + read-cache@1.0.0: + dependencies: + pify: 2.3.0 + + readable-stream@2.3.8: + dependencies: + core-util-is: 1.0.3 + inherits: 2.0.4 + isarray: 1.0.0 + process-nextick-args: 2.0.1 + safe-buffer: 5.1.2 + string_decoder: 1.1.1 + util-deprecate: 1.0.2 + + readable-stream@3.6.2: + dependencies: + inherits: 2.0.4 + string_decoder: 1.3.0 + util-deprecate: 1.0.2 + + readdir-glob@1.1.3: + dependencies: + minimatch: 5.1.6 + + readdirp@3.6.0: + dependencies: + picomatch: 2.3.1 + + readdirp@4.1.2: {} + + recharts-scale@0.4.5: + dependencies: + decimal.js-light: 2.5.1 + + recharts@2.15.4(react-dom@18.3.1(react@18.3.1))(react@18.3.1): + dependencies: + clsx: 2.1.1 + eventemitter3: 4.0.7 + lodash: 4.17.21 + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + react-is: 18.3.1 + react-smooth: 4.0.4(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + recharts-scale: 0.4.5 + tiny-invariant: 1.3.3 + victory-vendor: 36.9.2 + + rechoir@0.6.2: + dependencies: + resolve: 1.22.10 + + recursive-readdir@2.2.3: + dependencies: + minimatch: 3.1.2 + + redent@3.0.0: + dependencies: + indent-string: 4.0.0 + strip-indent: 3.0.0 + + redis-errors@1.2.0: + optional: true + + redis-parser@3.0.0: + dependencies: + redis-errors: 1.2.0 + optional: true + + redis@4.7.1: + dependencies: + '@redis/bloom': 1.2.0(@redis/client@1.6.1) + '@redis/client': 1.6.1 + '@redis/graph': 1.1.1(@redis/client@1.6.1) + '@redis/json': 1.0.7(@redis/client@1.6.1) + '@redis/search': 1.2.0(@redis/client@1.6.1) + '@redis/time-series': 1.1.0(@redis/client@1.6.1) + optional: true + + redux-thunk@3.1.0(redux@5.0.1): + dependencies: + redux: 5.0.1 + + redux@4.2.1: + dependencies: + '@babel/runtime': 7.28.4 + + redux@5.0.1: {} + + reflect-metadata@0.1.14: {} + + reflect.getprototypeof@1.0.10: + dependencies: + call-bind: 1.0.8 + define-properties: 1.2.1 + es-abstract: 1.24.0 + es-errors: 1.3.0 + es-object-atoms: 1.1.1 + get-intrinsic: 1.3.0 + get-proto: 1.0.1 + which-builtin-type: 1.2.1 + + refractor@3.6.0: + dependencies: + hastscript: 6.0.0 + parse-entities: 2.0.0 + prismjs: 1.29.0 + + regenerate-unicode-properties@10.2.2: + dependencies: + regenerate: 1.4.2 + + regenerate@1.4.2: {} + + regenerator-runtime@0.13.11: {} + + regex-parser@2.3.1: {} + + regexp.prototype.flags@1.5.4: + dependencies: + call-bind: 1.0.8 + define-properties: 1.2.1 + es-errors: 1.3.0 + get-proto: 1.0.1 + gopd: 1.2.0 + set-function-name: 2.0.2 + + regexpu-core@6.4.0: + dependencies: + regenerate: 1.4.2 + regenerate-unicode-properties: 10.2.2 + regjsgen: 0.8.0 + regjsparser: 0.13.0 + unicode-match-property-ecmascript: 2.0.0 + unicode-match-property-value-ecmascript: 2.2.1 + + regjsgen@0.8.0: {} + + regjsparser@0.13.0: + dependencies: + jsesc: 3.1.0 + + rehype-mathjax@4.0.3(bufferutil@4.0.9)(canvas@2.11.2(encoding@0.1.13))(utf-8-validate@6.0.5): + dependencies: + '@types/hast': 2.3.10 + '@types/mathjax': 0.0.37 + hast-util-from-dom: 4.2.0 + hast-util-to-text: 3.1.2 + jsdom: 20.0.3(bufferutil@4.0.9)(canvas@2.11.2(encoding@0.1.13))(utf-8-validate@6.0.5) + mathjax-full: 3.2.2 + unified: 10.1.2 + unist-util-visit: 4.1.2 + transitivePeerDependencies: + - bufferutil + - canvas + - supports-color + - utf-8-validate + + relateurl@0.2.7: {} + + remark-gfm@3.0.1: + dependencies: + '@types/mdast': 3.0.15 + mdast-util-gfm: 2.0.2 + micromark-extension-gfm: 2.0.3 + unified: 10.1.2 + transitivePeerDependencies: + - supports-color + + remark-math@5.1.1: + dependencies: + '@types/mdast': 3.0.15 + mdast-util-math: 2.0.2 + micromark-extension-math: 2.1.2 + unified: 10.1.2 + + remark-parse@10.0.2: + dependencies: + '@types/mdast': 3.0.15 + mdast-util-from-markdown: 1.3.1 + unified: 10.1.2 + transitivePeerDependencies: + - supports-color + + remark-rehype@10.1.0: + dependencies: + '@types/hast': 2.3.10 + '@types/mdast': 3.0.15 + mdast-util-to-hast: 12.3.0 + unified: 10.1.2 + + renderkid@3.0.0: + dependencies: + css-select: 4.3.0 + dom-converter: 0.2.0 + htmlparser2: 6.1.0 + lodash: 4.17.21 + strip-ansi: 6.0.1 + + request-progress@3.0.0: + dependencies: + throttleit: 1.0.1 + + require-directory@2.1.1: {} + + require-from-string@2.0.2: {} + + requires-port@1.0.0: {} + + reselect@4.1.8: {} + + reselect@5.1.1: {} + + resolve-cwd@3.0.0: + dependencies: + resolve-from: 5.0.0 + + resolve-from@4.0.0: {} + + resolve-from@5.0.0: {} + + resolve-url-loader@4.0.0: + dependencies: + adjust-sourcemap-loader: 4.0.0 + convert-source-map: 1.9.0 + loader-utils: 2.0.4 + postcss: 7.0.39 + source-map: 0.6.1 + + resolve.exports@1.1.1: {} + + resolve@1.22.10: + dependencies: + is-core-module: 2.16.1 + path-parse: 1.0.7 + supports-preserve-symlinks-flag: 1.0.0 + + resolve@2.0.0-next.5: + dependencies: + is-core-module: 2.16.1 + path-parse: 1.0.7 + supports-preserve-symlinks-flag: 1.0.0 + + restore-cursor@3.1.0: + dependencies: + onetime: 5.1.2 + signal-exit: 3.0.7 + + restore-cursor@4.0.0: + dependencies: + onetime: 5.1.2 + signal-exit: 3.0.7 + + retry@0.12.0: + optional: true + + retry@0.13.1: {} + + reusify@1.1.0: {} + + rfdc@1.4.1: {} + + rimraf@3.0.2: + dependencies: + glob: 10.5.0 + + rimraf@5.0.10: + dependencies: + glob: 10.5.0 + + rollup-plugin-terser@7.0.2(rollup@4.45.0): + dependencies: + '@babel/code-frame': 7.27.1 + jest-worker: 26.6.2 + rollup: 4.45.0 + serialize-javascript: 4.0.0 + terser: 5.44.0 + + rollup@4.45.0: + dependencies: + '@types/estree': 1.0.8 + optionalDependencies: + '@rollup/rollup-android-arm-eabi': 4.45.0 + '@rollup/rollup-android-arm64': 4.45.0 + '@rollup/rollup-darwin-arm64': 4.45.0 + '@rollup/rollup-darwin-x64': 4.45.0 + '@rollup/rollup-freebsd-arm64': 4.45.0 + '@rollup/rollup-freebsd-x64': 4.45.0 + '@rollup/rollup-linux-arm-gnueabihf': 4.45.0 + '@rollup/rollup-linux-arm-musleabihf': 4.45.0 + '@rollup/rollup-linux-arm64-gnu': 4.45.0 + '@rollup/rollup-linux-arm64-musl': 4.45.0 + '@rollup/rollup-linux-loongarch64-gnu': 4.45.0 + '@rollup/rollup-linux-powerpc64le-gnu': 4.45.0 + '@rollup/rollup-linux-riscv64-gnu': 4.45.0 + '@rollup/rollup-linux-riscv64-musl': 4.45.0 + '@rollup/rollup-linux-s390x-gnu': 4.45.0 + '@rollup/rollup-linux-x64-gnu': 4.45.0 + '@rollup/rollup-linux-x64-musl': 4.45.0 + '@rollup/rollup-win32-arm64-msvc': 4.45.0 + '@rollup/rollup-win32-ia32-msvc': 4.45.0 + '@rollup/rollup-win32-x64-msvc': 4.45.0 + fsevents: 2.3.3 + + run-applescript@5.0.0: + dependencies: + execa: 5.1.1 + + run-parallel@1.2.0: + dependencies: + queue-microtask: 1.2.3 + + run-script-os@1.1.6: {} + + rxjs@7.8.2: + dependencies: + tslib: 2.8.1 + + sade@1.8.1: + dependencies: + mri: 1.2.0 + + safe-array-concat@1.1.3: + dependencies: + call-bind: 1.0.8 + call-bound: 1.0.4 + get-intrinsic: 1.3.0 + has-symbols: 1.1.0 + isarray: 2.0.5 + + safe-buffer@5.1.2: {} + + safe-buffer@5.2.1: {} + + safe-push-apply@1.0.0: + dependencies: + es-errors: 1.3.0 + isarray: 2.0.5 + + safe-regex-test@1.1.0: + dependencies: + call-bound: 1.0.4 + es-errors: 1.3.0 + is-regex: 1.2.1 + + safe-stable-stringify@2.5.0: {} + + safer-buffer@2.1.2: {} + + sanitize-html@2.17.0: + dependencies: + deepmerge: 4.3.1 + escape-string-regexp: 4.0.0 + htmlparser2: 8.0.2 + is-plain-object: 5.0.0 + parse-srcset: 1.0.2 + postcss: 8.5.6 + + sanitize.css@13.0.0: {} + + sass-loader@12.6.0(sass@1.93.2)(webpack@5.102.1): + dependencies: + klona: 2.0.6 + neo-async: 2.6.2 + webpack: 5.102.1 + optionalDependencies: + sass: 1.93.2 + + sass@1.93.2: + dependencies: + chokidar: 4.0.3 + immutable: 5.1.3 + source-map-js: 1.2.1 + optionalDependencies: + '@parcel/watcher': 2.5.1 + + sax@1.2.4: {} + + saxes@5.0.1: + dependencies: + xmlchars: 2.2.0 + + saxes@6.0.0: + dependencies: + xmlchars: 2.2.0 + + scheduler@0.23.2: + dependencies: + loose-envify: 1.4.0 + + schema-utils@2.7.0: + dependencies: + '@types/json-schema': 7.0.15 + ajv: 6.12.6 + ajv-keywords: 3.5.2(ajv@6.12.6) + + schema-utils@2.7.1: + dependencies: + '@types/json-schema': 7.0.15 + ajv: 6.12.6 + ajv-keywords: 3.5.2(ajv@6.12.6) + + schema-utils@3.3.0: + dependencies: + '@types/json-schema': 7.0.15 + ajv: 6.12.6 + ajv-keywords: 3.5.2(ajv@6.12.6) + + schema-utils@4.3.3: + dependencies: + '@types/json-schema': 7.0.15 + ajv: 8.17.1 + ajv-formats: 2.1.1(ajv@8.17.1) + ajv-keywords: 5.1.0(ajv@8.17.1) + + select-hose@2.0.0: {} + + selfsigned@2.4.1: + dependencies: + '@types/node-forge': 1.3.14 + node-forge: 1.3.1 + + semver@7.7.1: {} + + send@0.19.0: + dependencies: + debug: 2.6.9 + depd: 2.0.0 + destroy: 1.2.0 + encodeurl: 1.0.2 + escape-html: 1.0.3 + etag: 1.8.1 + fresh: 0.5.2 + http-errors: 2.0.0 + mime: 1.6.0 + ms: 2.1.3 + on-finished: 2.4.1 + range-parser: 1.2.1 + statuses: 2.0.1 + transitivePeerDependencies: + - supports-color + + seq-queue@0.0.5: {} + + serialize-javascript@4.0.0: + dependencies: + randombytes: 2.1.0 + + serialize-javascript@6.0.2: + dependencies: + randombytes: 2.1.0 + + seroval-plugins@1.3.3(seroval@1.3.2): + dependencies: + seroval: 1.3.2 + + seroval@1.3.2: {} + + serve-index@1.9.1: + dependencies: + accepts: 1.3.8 + batch: 0.6.1 + debug: 2.6.9 + escape-html: 1.0.3 + http-errors: 1.6.3 + mime-types: 2.1.35 + parseurl: 1.3.3 + transitivePeerDependencies: + - supports-color + + serve-static@1.16.2: + dependencies: + encodeurl: 2.0.0 + escape-html: 1.0.3 + parseurl: 1.3.3 + send: 0.19.0 + transitivePeerDependencies: + - supports-color + + set-blocking@2.0.0: + optional: true + + set-function-length@1.2.2: + dependencies: + define-data-property: 1.1.4 + es-errors: 1.3.0 + function-bind: 1.1.2 + get-intrinsic: 1.3.0 + gopd: 1.2.0 + has-property-descriptors: 1.0.2 + + set-function-name@2.0.2: + dependencies: + define-data-property: 1.1.4 + es-errors: 1.3.0 + functions-have-names: 1.2.3 + has-property-descriptors: 1.0.2 + + set-proto@1.0.0: + dependencies: + dunder-proto: 1.0.1 + es-errors: 1.3.0 + es-object-atoms: 1.1.1 + + setimmediate@1.0.5: {} + + setprototypeof@1.1.0: {} + + setprototypeof@1.2.0: {} + + sha.js@2.4.12: + dependencies: + inherits: 2.0.4 + safe-buffer: 5.2.1 + to-buffer: 1.2.2 + + shebang-command@2.0.0: + dependencies: + shebang-regex: 3.0.0 + + shebang-regex@3.0.0: {} + + shell-exec@1.0.2: {} + + shell-quote@1.8.3: {} + + shelljs@0.8.5: + dependencies: + glob: 10.5.0 + interpret: 1.4.0 + rechoir: 0.6.2 + + shx@0.3.4: + dependencies: + minimist: 1.2.8 + shelljs: 0.8.5 + + side-channel-list@1.0.0: + dependencies: + es-errors: 1.3.0 + object-inspect: 1.13.4 + + side-channel-map@1.0.1: + dependencies: + call-bound: 1.0.4 + es-errors: 1.3.0 + get-intrinsic: 1.3.0 + object-inspect: 1.13.4 + + side-channel-weakmap@1.0.2: + dependencies: + call-bound: 1.0.4 + es-errors: 1.3.0 + get-intrinsic: 1.3.0 + object-inspect: 1.13.4 + side-channel-map: 1.0.1 + + side-channel@1.1.0: + dependencies: + es-errors: 1.3.0 + object-inspect: 1.13.4 + side-channel-list: 1.0.0 + side-channel-map: 1.0.1 + side-channel-weakmap: 1.0.2 + + signal-exit@3.0.7: {} + + signal-exit@4.1.0: {} + + simple-concat@1.0.1: {} + + simple-get@3.1.1: + dependencies: + decompress-response: 4.2.1 + once: 1.4.0 + simple-concat: 1.0.1 + optional: true + + simple-get@4.0.1: + dependencies: + decompress-response: 6.0.0 + once: 1.4.0 + simple-concat: 1.0.1 + + simple-update-notifier@1.1.0: + dependencies: + semver: 7.7.1 + + sisteransi@1.0.5: {} + + slash@3.0.0: {} + + slash@4.0.0: {} + + slice-ansi@3.0.0: + dependencies: + ansi-styles: 4.3.0 + astral-regex: 2.0.0 + is-fullwidth-code-point: 3.0.0 + + slice-ansi@4.0.0: + dependencies: + ansi-styles: 4.3.0 + astral-regex: 2.0.0 + is-fullwidth-code-point: 3.0.0 + + slice-ansi@5.0.0: + dependencies: + ansi-styles: 6.2.3 + is-fullwidth-code-point: 4.0.0 + + smart-buffer@4.2.0: + optional: true + + socket.io-adapter@2.5.5(bufferutil@4.0.9)(utf-8-validate@6.0.5): + dependencies: + debug: 4.3.7 + ws: 8.18.3(bufferutil@4.0.9)(utf-8-validate@6.0.5) + transitivePeerDependencies: + - bufferutil + - supports-color + - utf-8-validate + + socket.io-client@4.8.1(bufferutil@4.0.9)(utf-8-validate@6.0.5): + dependencies: + '@socket.io/component-emitter': 3.1.2 + debug: 4.3.7 + engine.io-client: 6.6.3(bufferutil@4.0.9)(utf-8-validate@6.0.5) + socket.io-parser: 4.2.4 + transitivePeerDependencies: + - bufferutil + - supports-color + - utf-8-validate + + socket.io-parser@4.2.4: + dependencies: + '@socket.io/component-emitter': 3.1.2 + debug: 4.3.7 + transitivePeerDependencies: + - supports-color + + socket.io@4.8.1(bufferutil@4.0.9)(utf-8-validate@6.0.5): + dependencies: + accepts: 1.3.8 + base64id: 2.0.0 + cors: 2.8.5 + debug: 4.3.7 + engine.io: 6.6.4(bufferutil@4.0.9)(utf-8-validate@6.0.5) + socket.io-adapter: 2.5.5(bufferutil@4.0.9)(utf-8-validate@6.0.5) + socket.io-parser: 4.2.4 + transitivePeerDependencies: + - bufferutil + - supports-color + - utf-8-validate + + sockjs@0.3.24: + dependencies: + faye-websocket: 0.11.4 + uuid: 8.3.2 + websocket-driver: 0.7.4 + + socks-proxy-agent@6.2.1: + dependencies: + agent-base: 6.0.2 + debug: 4.4.3(supports-color@8.1.1) + socks: 2.8.7 + transitivePeerDependencies: + - supports-color + optional: true + + socks@2.8.7: + dependencies: + ip-address: 10.0.1 + smart-buffer: 4.2.0 + optional: true + + solid-element@1.7.0(solid-js@1.9.7): + dependencies: + component-register: 0.8.8 + solid-js: 1.9.7 + + solid-js@1.9.7: + dependencies: + csstype: 3.1.3 + seroval: 1.3.2 + seroval-plugins: 1.3.3(seroval@1.3.2) + + source-list-map@2.0.1: {} + + source-map-js@1.2.1: {} + + source-map-loader@3.0.2(webpack@5.102.1): + dependencies: + abab: 2.0.6 + iconv-lite: 0.6.3 + source-map-js: 1.2.1 + webpack: 5.102.1 + + source-map-support@0.5.21: + dependencies: + buffer-from: 1.1.2 + source-map: 0.6.1 + + source-map@0.5.7: {} + + source-map@0.6.1: {} + + source-map@0.7.6: {} + + source-map@0.8.0-beta.0: + dependencies: + whatwg-url: 7.1.0 + + sourcemap-codec@1.4.8: {} + + space-separated-tokens@1.1.5: {} + + space-separated-tokens@2.0.2: {} + + sparse-bitfield@3.0.3: + dependencies: + memory-pager: 1.5.0 + optional: true + + spawn-command@0.0.2: {} + + spdy-transport@3.0.0: + dependencies: + debug: 4.4.3(supports-color@8.1.1) + detect-node: 2.1.0 + hpack.js: 2.1.6 + obuf: 1.1.2 + readable-stream: 3.6.2 + wbuf: 1.7.3 + transitivePeerDependencies: + - supports-color + + spdy@4.0.2: + dependencies: + debug: 4.4.3(supports-color@8.1.1) + handle-thing: 2.0.1 + http-deceiver: 1.2.7 + select-hose: 2.0.0 + spdy-transport: 3.0.0 + transitivePeerDependencies: + - supports-color + + speech-rule-engine@4.1.2: + dependencies: + '@xmldom/xmldom': 0.9.8 + commander: 13.1.0 + wicked-good-xpath: 1.3.0 + + split-on-first@3.0.0: {} + + split2@4.2.0: {} + + split@0.3.3: + dependencies: + through: 2.3.8 + + sprintf-js@1.0.3: {} + + sql-highlight@6.1.0: {} + + sqlite3@5.1.7: + dependencies: + bindings: 1.5.0 + node-addon-api: 7.1.1 + prebuild-install: 7.1.3 + tar: 6.2.1 + optionalDependencies: + node-gyp: 8.4.1 + transitivePeerDependencies: + - bare-abort-controller + - bare-buffer + - bluebird + - react-native-b4a + - supports-color + + sqlstring@2.3.3: {} + + sshpk@1.18.0: + dependencies: + asn1: 0.2.6 + assert-plus: 1.0.0 + bcrypt-pbkdf: 1.0.2 + dashdash: 1.14.1 + ecc-jsbn: 0.1.2 + getpass: 0.1.7 + jsbn: 0.1.1 + safer-buffer: 2.1.2 + tweetnacl: 0.14.5 + + ssri@8.0.1: + dependencies: + minipass: 3.3.6 + optional: true + + stable@0.1.8: {} + + stack-trace@0.0.10: {} + + stack-utils@2.0.6: + dependencies: + escape-string-regexp: 2.0.0 + + stackframe@1.3.4: {} + + standard-as-callback@2.1.0: + optional: true + + start-server-and-test@2.1.2: + dependencies: + arg: 5.0.2 + bluebird: 3.7.2 + check-more-types: 2.24.0 + debug: 4.4.3(supports-color@8.1.1) + execa: 5.1.1 + lazy-ass: 1.6.0 + ps-tree: 1.2.0 + wait-on: 8.0.5(debug@4.4.3) + transitivePeerDependencies: + - supports-color + + static-eval@2.0.2: + dependencies: + escodegen: 1.14.3 + + statuses@1.5.0: {} + + statuses@2.0.1: {} + + stop-iteration-iterator@1.1.0: + dependencies: + es-errors: 1.3.0 + internal-slot: 1.1.0 + + stream-combiner@0.0.4: + dependencies: + duplexer: 0.1.2 + + streamsearch@1.1.0: {} + + streamx@2.23.0: + dependencies: + events-universal: 1.0.1 + fast-fifo: 1.3.2 + text-decoder: 1.2.3 + transitivePeerDependencies: + - bare-abort-controller + - react-native-b4a + + string-argv@0.3.2: {} + + string-length@4.0.2: + dependencies: + char-regex: 1.0.2 + strip-ansi: 6.0.1 + + string-length@5.0.1: + dependencies: + char-regex: 2.0.2 + strip-ansi: 7.1.2 + + string-natural-compare@3.0.1: {} + + string-width@4.2.3: + dependencies: + emoji-regex: 8.0.0 + is-fullwidth-code-point: 3.0.0 + strip-ansi: 6.0.1 + + string-width@5.1.2: + dependencies: + eastasianwidth: 0.2.0 + emoji-regex: 9.2.2 + strip-ansi: 7.1.2 + + string.prototype.includes@2.0.1: + dependencies: + call-bind: 1.0.8 + define-properties: 1.2.1 + es-abstract: 1.24.0 + + string.prototype.matchall@4.0.12: + dependencies: + call-bind: 1.0.8 + call-bound: 1.0.4 + define-properties: 1.2.1 + es-abstract: 1.24.0 + es-errors: 1.3.0 + es-object-atoms: 1.1.1 + get-intrinsic: 1.3.0 + gopd: 1.2.0 + has-symbols: 1.1.0 + internal-slot: 1.1.0 + regexp.prototype.flags: 1.5.4 + set-function-name: 2.0.2 + side-channel: 1.1.0 + + string.prototype.repeat@1.0.0: + dependencies: + define-properties: 1.2.1 + es-abstract: 1.24.0 + + string.prototype.trim@1.2.10: + dependencies: + call-bind: 1.0.8 + call-bound: 1.0.4 + define-data-property: 1.1.4 + define-properties: 1.2.1 + es-abstract: 1.24.0 + es-object-atoms: 1.1.1 + has-property-descriptors: 1.0.2 + + string.prototype.trimend@1.0.9: + dependencies: + call-bind: 1.0.8 + call-bound: 1.0.4 + define-properties: 1.2.1 + es-object-atoms: 1.1.1 + + string.prototype.trimstart@1.0.8: + dependencies: + call-bind: 1.0.8 + define-properties: 1.2.1 + es-object-atoms: 1.1.1 + + string_decoder@1.1.1: + dependencies: + safe-buffer: 5.1.2 + + string_decoder@1.3.0: + dependencies: + safe-buffer: 5.2.1 + + stringify-object@3.3.0: + dependencies: + get-own-enumerable-property-symbols: 3.0.2 + is-obj: 1.0.1 + is-regexp: 1.0.0 + + strip-ansi@6.0.1: + dependencies: + ansi-regex: 5.0.1 + + strip-ansi@7.1.2: + dependencies: + ansi-regex: 6.2.2 + + strip-bom@3.0.0: {} + + strip-bom@4.0.0: {} + + strip-comments@2.0.1: {} + + strip-eof@1.0.0: {} + + strip-final-newline@2.0.0: {} + + strip-final-newline@3.0.0: {} + + strip-indent@3.0.0: + dependencies: + min-indent: 1.0.1 + + strip-json-comments@2.0.1: {} + + strip-json-comments@3.1.1: {} + + strnum@2.2.3: + optional: true + + style-loader@3.3.4(webpack@5.102.1): + dependencies: + webpack: 5.102.1 + + style-mod@4.1.2: {} + + style-to-js@1.1.3: + dependencies: + style-to-object: 0.4.1 + + style-to-object@0.4.1: + dependencies: + inline-style-parser: 0.1.1 + + style-to-object@0.4.4: + dependencies: + inline-style-parser: 0.1.1 + + style-value-types@4.1.4: + dependencies: + hey-listen: 1.0.8 + tslib: 2.8.1 + + styled-components@6.4.0(css-to-react-native@3.2.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1): + dependencies: + '@emotion/is-prop-valid': 1.4.0 + csstype: 3.2.3 + react: 18.3.1 + stylis: 4.3.6 + optionalDependencies: + css-to-react-native: 3.2.0 + react-dom: 18.3.1(react@18.3.1) + + stylehacks@5.1.1(postcss@8.5.6): + dependencies: + browserslist: 4.26.3 + postcss: 8.5.6 + postcss-selector-parser: 6.1.2 + + stylis@4.2.0: {} + + stylis@4.3.6: {} + + sucrase@3.35.0: + dependencies: + '@jridgewell/gen-mapping': 0.3.13 + commander: 4.1.1 + glob: 10.5.0 + lines-and-columns: 1.2.4 + mz: 2.7.0 + pirates: 4.0.7 + ts-interface-checker: 0.1.13 + + supports-color@5.5.0: + dependencies: + has-flag: 3.0.0 + + supports-color@7.2.0: + dependencies: + has-flag: 4.0.0 + + supports-color@8.1.1: + dependencies: + has-flag: 4.0.0 + + supports-hyperlinks@2.3.0: + dependencies: + has-flag: 4.0.0 + supports-color: 7.2.0 + + supports-preserve-symlinks-flag@1.0.0: {} + + svg-parser@2.0.4: {} + + svgo@1.3.2: + dependencies: + chalk: 2.4.2 + coa: 2.0.2 + css-select: 2.1.0 + css-select-base-adapter: 0.1.1 + css-tree: 1.0.0-alpha.37 + csso: 4.2.0 + js-yaml: 3.14.1 + mkdirp: 0.5.6 + object.values: 1.2.1 + sax: 1.2.4 + stable: 0.1.8 + unquote: 1.1.1 + util.promisify: 1.0.1 + + svgo@2.8.0: + dependencies: + '@trysound/sax': 0.2.0 + commander: 7.2.0 + css-select: 4.3.0 + css-tree: 1.1.3 + csso: 4.2.0 + picocolors: 1.1.1 + stable: 0.1.8 + + symbol-tree@3.2.4: {} + + tailwindcss@3.4.18(yaml@2.8.1): + dependencies: + '@alloc/quick-lru': 5.2.0 + arg: 5.0.2 + chokidar: 3.6.0 + didyoumean: 1.2.2 + dlv: 1.1.3 + fast-glob: 3.3.3 + glob-parent: 6.0.2 + is-glob: 4.0.3 + jiti: 1.21.7 + lilconfig: 3.1.3 + micromatch: 4.0.8 + normalize-path: 3.0.0 + object-hash: 3.0.0 + picocolors: 1.1.1 + postcss: 8.5.6 + postcss-import: 15.1.0(postcss@8.5.6) + postcss-js: 4.1.0(postcss@8.5.6) + postcss-load-config: 6.0.1(jiti@1.21.7)(postcss@8.5.6)(yaml@2.8.1) + postcss-nested: 6.2.0(postcss@8.5.6) + postcss-selector-parser: 6.1.2 + resolve: 1.22.10 + sucrase: 3.35.0 + transitivePeerDependencies: + - tsx + - yaml + + tapable@1.1.3: {} + + tapable@2.3.0: {} + + tar-fs@3.1.1: + dependencies: + pump: 3.0.3 + tar-stream: 3.1.7 + optionalDependencies: + bare-fs: 4.4.10 + bare-path: 3.0.0 + transitivePeerDependencies: + - bare-abort-controller + - bare-buffer + - react-native-b4a + + tar-stream@3.1.7: + dependencies: + b4a: 1.7.3 + fast-fifo: 1.3.2 + streamx: 2.23.0 + transitivePeerDependencies: + - bare-abort-controller + - react-native-b4a + + tar@6.2.1: + dependencies: + chownr: 2.0.0 + fs-minipass: 2.1.0 + minipass: 5.0.0 + minizlib: 2.1.2 + mkdirp: 1.0.4 + yallist: 4.0.0 + + temp-dir@2.0.0: {} + + tempy@0.6.0: + dependencies: + is-stream: 2.0.1 + temp-dir: 2.0.0 + type-fest: 0.16.0 + unique-string: 2.0.0 + + terminal-link@2.1.1: + dependencies: + ansi-escapes: 4.3.2 + supports-hyperlinks: 2.3.0 + + terser-webpack-plugin@5.3.14(webpack@5.102.1): + dependencies: + '@jridgewell/trace-mapping': 0.3.31 + jest-worker: 27.5.1 + schema-utils: 4.3.3 + serialize-javascript: 6.0.2 + terser: 5.44.0 + webpack: 5.102.1 + + terser@5.44.0: + dependencies: + '@jridgewell/source-map': 0.3.11 + acorn: 8.15.0 + commander: 2.20.3 + source-map-support: 0.5.21 + + test-exclude@6.0.0: + dependencies: + '@istanbuljs/schema': 0.1.3 + glob: 10.5.0 + minimatch: 3.1.2 + + text-decoder@1.2.3: + dependencies: + b4a: 1.7.3 + transitivePeerDependencies: + - react-native-b4a + + text-hex@1.0.0: {} + + text-table@0.2.0: {} + + thenify-all@1.6.0: + dependencies: + thenify: 3.3.1 + + thenify@3.3.1: + dependencies: + any-promise: 1.3.0 + + thingies@2.6.0(tslib@2.8.1): + dependencies: + tslib: 2.8.1 + + throat@6.0.2: {} + + throttleit@1.0.1: {} + + through@2.3.8: {} + + thunky@1.1.0: {} + + tiny-invariant@1.3.3: {} + + tiny-warning@1.0.3: {} + + tinycolor2@1.6.0: {} + + titleize@1.0.1: {} + + tldts-core@6.1.86: {} + + tldts@6.1.86: + dependencies: + tldts-core: 6.1.86 + + tmp@0.2.5: {} + + tmpl@1.0.5: {} + + to-buffer@1.2.2: + dependencies: + isarray: 2.0.5 + safe-buffer: 5.2.1 + typed-array-buffer: 1.0.3 + + to-regex-range@5.0.1: + dependencies: + is-number: 7.0.0 + + toidentifier@1.0.1: {} + + toposort@2.0.2: {} + + touch@3.1.1: {} + + tough-cookie@4.1.4: + dependencies: + psl: 1.15.0 + punycode: 2.3.1 + universalify: 0.2.0 + url-parse: 1.5.10 + + tough-cookie@5.1.2: + dependencies: + tldts: 6.1.86 + + tr46@0.0.3: {} + + tr46@1.0.1: + dependencies: + punycode: 2.3.1 + + tr46@2.1.0: + dependencies: + punycode: 2.3.1 + + tr46@3.0.0: + dependencies: + punycode: 2.3.1 + + tr46@5.1.1: + dependencies: + punycode: 2.3.1 + optional: true + + tree-dump@1.1.0(tslib@2.8.1): + dependencies: + tslib: 2.8.1 + + tree-kill@1.2.2: {} + + trim-lines@3.0.1: {} + + triple-beam@1.4.1: {} + + trough@2.2.0: {} + + tryer@1.0.1: {} + + ts-api-utils@1.4.3(typescript@5.9.3): + dependencies: + typescript: 5.9.3 + + ts-interface-checker@0.1.13: {} + + ts-node@10.9.2(@types/node@24.7.2)(typescript@5.9.3): + dependencies: + '@cspotcode/source-map-support': 0.8.1 + '@tsconfig/node10': 1.0.11 + '@tsconfig/node12': 1.0.11 + '@tsconfig/node14': 1.0.3 + '@tsconfig/node16': 1.0.4 + '@types/node': 24.7.2 + acorn: 8.15.0 + acorn-walk: 8.3.4 + arg: 4.1.3 + create-require: 1.1.1 + diff: 4.0.2 + make-error: 1.3.6 + typescript: 5.9.3 + v8-compile-cache-lib: 3.0.1 + yn: 3.1.1 + + tsc-watch@6.3.1(typescript@5.9.3): + dependencies: + cross-spawn: 7.0.6 + node-cleanup: 2.1.2 + ps-tree: 1.2.0 + string-argv: 0.3.2 + typescript: 5.9.3 + + tsconfck@3.1.6(typescript@5.9.3): + optionalDependencies: + typescript: 5.9.3 + + tsconfig-paths@3.15.0: + dependencies: + '@types/json5': 0.0.29 + json5: 2.2.3 + minimist: 1.2.8 + strip-bom: 3.0.0 + + tslib@1.14.1: {} + + tslib@2.8.1: {} + + tsutils@3.21.0(typescript@5.9.3): + dependencies: + tslib: 1.14.1 + typescript: 5.9.3 + + tunnel-agent@0.6.0: + dependencies: + safe-buffer: 5.2.1 + + turbo-darwin-64@2.5.8: + optional: true + + turbo-darwin-arm64@2.5.8: + optional: true + + turbo-linux-64@2.5.8: + optional: true + + turbo-linux-arm64@2.5.8: + optional: true + + turbo-windows-64@2.5.8: + optional: true + + turbo-windows-arm64@2.5.8: + optional: true + + turbo@2.5.8: + optionalDependencies: + turbo-darwin-64: 2.5.8 + turbo-darwin-arm64: 2.5.8 + turbo-linux-64: 2.5.8 + turbo-linux-arm64: 2.5.8 + turbo-windows-64: 2.5.8 + turbo-windows-arm64: 2.5.8 + + tweetnacl@0.14.5: {} + + type-check@0.3.2: + dependencies: + prelude-ls: 1.1.2 + + type-check@0.4.0: + dependencies: + prelude-ls: 1.2.1 + + type-detect@4.0.8: {} + + type-fest@0.16.0: {} + + type-fest@0.20.2: {} + + type-fest@0.21.3: {} + + type-fest@1.4.0: {} + + type-fest@2.19.0: {} + + type-fest@4.41.0: + optional: true + + type-is@1.6.18: + dependencies: + media-typer: 0.3.0 + mime-types: 2.1.35 + + typed-array-buffer@1.0.3: + dependencies: + call-bound: 1.0.4 + es-errors: 1.3.0 + is-typed-array: 1.1.15 + + typed-array-byte-length@1.0.3: + dependencies: + call-bind: 1.0.8 + for-each: 0.3.5 + gopd: 1.2.0 + has-proto: 1.2.0 + is-typed-array: 1.1.15 + + typed-array-byte-offset@1.0.4: + dependencies: + available-typed-arrays: 1.0.7 + call-bind: 1.0.8 + for-each: 0.3.5 + gopd: 1.2.0 + has-proto: 1.2.0 + is-typed-array: 1.1.15 + reflect.getprototypeof: 1.0.10 + + typed-array-length@1.0.7: + dependencies: + call-bind: 1.0.8 + for-each: 0.3.5 + gopd: 1.2.0 + is-typed-array: 1.1.15 + possible-typed-array-names: 1.1.0 + reflect.getprototypeof: 1.0.10 + + typedarray-to-buffer@3.1.5: + dependencies: + is-typedarray: 1.0.0 + + typedarray@0.0.6: {} + + typeorm@0.3.27(babel-plugin-macros@3.1.0)(ioredis@5.8.1)(mongodb@6.20.0(@aws-sdk/credential-providers@3.908.0)(socks@2.8.7))(mysql2@3.15.2)(pg@8.16.3)(redis@4.7.1)(reflect-metadata@0.1.14)(sqlite3@5.1.7)(ts-node@10.9.2(@types/node@24.7.2)(typescript@5.9.3)): + dependencies: + '@sqltools/formatter': 1.2.5 + ansis: 3.17.0 + app-root-path: 3.1.0 + buffer: 6.0.3 + dayjs: 1.11.18 + debug: 4.4.3(supports-color@8.1.1) + dedent: 1.7.0(babel-plugin-macros@3.1.0) + dotenv: 16.6.1 + glob: 10.5.0 + reflect-metadata: 0.1.14 + sha.js: 2.4.12 + sql-highlight: 6.1.0 + tslib: 2.8.1 + uuid: 11.1.0 + yargs: 17.7.2 + optionalDependencies: + ioredis: 5.8.1 + mongodb: 6.20.0(@aws-sdk/credential-providers@3.908.0)(socks@2.8.7) + mysql2: 3.15.2 + pg: 8.16.3 + redis: 4.7.1 + sqlite3: 5.1.7 + ts-node: 10.9.2(@types/node@24.7.2)(typescript@5.9.3) + transitivePeerDependencies: + - babel-plugin-macros + - supports-color + + typescript@5.9.3: {} + + ua-parser-js@0.7.41: {} + + ua-parser-js@1.0.41: {} + + unbox-primitive@1.1.0: + dependencies: + call-bound: 1.0.4 + has-bigints: 1.1.0 + has-symbols: 1.1.0 + which-boxed-primitive: 1.1.1 + + undefsafe@2.0.5: {} + + underscore@1.12.1: {} + + undici-types@7.14.0: {} + + unicode-canonical-property-names-ecmascript@2.0.1: {} + + unicode-match-property-ecmascript@2.0.0: + dependencies: + unicode-canonical-property-names-ecmascript: 2.0.1 + unicode-property-aliases-ecmascript: 2.2.0 + + unicode-match-property-value-ecmascript@2.2.1: {} + + unicode-property-aliases-ecmascript@2.2.0: {} + + unified@10.1.2: + dependencies: + '@types/unist': 2.0.11 + bail: 2.0.2 + extend: 3.0.2 + is-buffer: 2.0.5 + is-plain-obj: 4.1.0 + trough: 2.2.0 + vfile: 5.3.7 + + unique-filename@1.1.1: + dependencies: + unique-slug: 2.0.2 + optional: true + + unique-slug@2.0.2: + dependencies: + imurmurhash: 0.1.4 + optional: true + + unique-string@2.0.0: + dependencies: + crypto-random-string: 2.0.0 + + unist-util-find-after@4.0.1: + dependencies: + '@types/unist': 2.0.11 + unist-util-is: 5.2.1 + + unist-util-generated@2.0.1: {} + + unist-util-is@5.2.1: + dependencies: + '@types/unist': 2.0.11 + + unist-util-position@4.0.4: + dependencies: + '@types/unist': 2.0.11 + + unist-util-stringify-position@2.0.3: + dependencies: + '@types/unist': 2.0.11 + + unist-util-stringify-position@3.0.3: + dependencies: + '@types/unist': 2.0.11 + + unist-util-visit-parents@5.1.3: + dependencies: + '@types/unist': 2.0.11 + unist-util-is: 5.2.1 + + unist-util-visit@4.1.2: + dependencies: + '@types/unist': 2.0.11 + unist-util-is: 5.2.1 + unist-util-visit-parents: 5.1.3 + + universalify@0.2.0: {} + + universalify@2.0.1: {} + + unpipe@1.0.0: {} + + unquote@1.1.1: {} + + untildify@4.0.0: {} + + upath@1.2.0: {} + + update-browserslist-db@1.1.3(browserslist@4.26.3): + dependencies: + browserslist: 4.26.3 + escalade: 3.2.0 + picocolors: 1.1.1 + + uri-js@4.4.1: + dependencies: + punycode: 2.3.1 + + url-parse@1.5.10: + dependencies: + querystringify: 2.2.0 + requires-port: 1.0.0 + + use-composed-ref@1.4.0(@types/react@18.3.26)(react@18.3.1): + dependencies: + react: 18.3.1 + optionalDependencies: + '@types/react': 18.3.26 + + use-isomorphic-layout-effect@1.2.1(@types/react@18.3.26)(react@18.3.1): + dependencies: + react: 18.3.1 + optionalDependencies: + '@types/react': 18.3.26 + + use-latest@1.3.0(@types/react@18.3.26)(react@18.3.1): + dependencies: + react: 18.3.1 + use-isomorphic-layout-effect: 1.2.1(@types/react@18.3.26)(react@18.3.1) + optionalDependencies: + '@types/react': 18.3.26 + + use-sync-external-store@1.6.0(react@18.3.1): + dependencies: + react: 18.3.1 + + utf-8-validate@6.0.5: + dependencies: + node-gyp-build: 4.8.4 + optional: true + + util-deprecate@1.0.2: {} + + util.promisify@1.0.1: + dependencies: + define-properties: 1.2.1 + es-abstract: 1.24.0 + has-symbols: 1.1.0 + object.getownpropertydescriptors: 2.1.8 + + utila@0.4.0: {} + + utils-merge@1.0.1: {} + + uuid@11.1.0: {} + + uuid@8.3.2: {} + + uuid@9.0.1: {} + + uvu@0.5.6: + dependencies: + dequal: 2.0.3 + diff: 5.2.0 + kleur: 4.1.5 + sade: 1.8.1 + + v8-compile-cache-lib@3.0.1: {} + + v8-to-istanbul@8.1.1: + dependencies: + '@types/istanbul-lib-coverage': 2.0.6 + convert-source-map: 1.9.0 + source-map: 0.7.6 + + vary@1.1.2: {} + + verror@1.10.0: + dependencies: + assert-plus: 1.0.0 + core-util-is: 1.0.2 + extsprintf: 1.3.0 + + vfile-message@3.1.4: + dependencies: + '@types/unist': 2.0.11 + unist-util-stringify-position: 3.0.3 + + vfile@5.3.7: + dependencies: + '@types/unist': 2.0.11 + is-buffer: 2.0.5 + unist-util-stringify-position: 3.0.3 + vfile-message: 3.1.4 + + victory-vendor@36.9.2: + dependencies: + '@types/d3-array': 3.2.2 + '@types/d3-ease': 3.0.2 + '@types/d3-interpolate': 3.0.4 + '@types/d3-scale': 4.0.9 + '@types/d3-shape': 3.1.7 + '@types/d3-time': 3.0.4 + '@types/d3-timer': 3.0.2 + d3-array: 3.2.4 + d3-ease: 3.0.1 + d3-interpolate: 3.0.1 + d3-scale: 4.0.2 + d3-shape: 3.2.0 + d3-time: 3.1.0 + d3-timer: 3.0.1 + + vite-plugin-pwa@0.17.5(vite@5.4.20(@types/node@24.7.2)(sass@1.93.2)(terser@5.44.0))(workbox-build@6.6.0(@types/babel__core@7.20.5))(workbox-window@6.6.0): + dependencies: + debug: 4.4.3(supports-color@8.1.1) + fast-glob: 3.3.3 + pretty-bytes: 6.1.1 + vite: 5.4.20(@types/node@24.7.2)(sass@1.93.2)(terser@5.44.0) + workbox-build: 6.6.0(@types/babel__core@7.20.5) + workbox-window: 6.6.0 + transitivePeerDependencies: + - supports-color + + vite-plugin-react-js-support@1.0.7: + dependencies: + '@babel/core': 7.28.4 + '@babel/plugin-transform-react-jsx': 7.27.1(@babel/core@7.28.4) + transitivePeerDependencies: + - supports-color + + vite-tsconfig-paths@4.3.2(typescript@5.9.3)(vite@4.5.14(@types/node@24.7.2)(sass@1.93.2)(terser@5.44.0)): + dependencies: + debug: 4.4.3(supports-color@8.1.1) + globrex: 0.1.2 + tsconfck: 3.1.6(typescript@5.9.3) + optionalDependencies: + vite: 4.5.14(@types/node@24.7.2)(sass@1.93.2)(terser@5.44.0) + transitivePeerDependencies: + - supports-color + - typescript + + vite@4.5.14(@types/node@24.7.2)(sass@1.93.2)(terser@5.44.0): + dependencies: + esbuild: 0.27.1 + postcss: 8.5.6 + rollup: 4.45.0 + optionalDependencies: + '@types/node': 24.7.2 + fsevents: 2.3.3 + sass: 1.93.2 + terser: 5.44.0 + + vite@5.4.20(@types/node@24.7.2)(sass@1.93.2)(terser@5.44.0): + dependencies: + esbuild: 0.27.1 + postcss: 8.5.6 + rollup: 4.45.0 + optionalDependencies: + '@types/node': 24.7.2 + fsevents: 2.3.3 + sass: 1.93.2 + terser: 5.44.0 + + w3c-hr-time@1.0.2: + dependencies: + browser-process-hrtime: 1.0.0 + + w3c-keyname@2.2.8: {} + + w3c-xmlserializer@2.0.0: + dependencies: + xml-name-validator: 3.0.0 + + w3c-xmlserializer@4.0.0: + dependencies: + xml-name-validator: 4.0.0 + + wait-on@8.0.5(debug@4.4.3): + dependencies: + axios: 1.12.0(debug@4.4.3) + joi: 18.0.1 + lodash: 4.17.21 + minimist: 1.2.8 + rxjs: 7.8.2 + transitivePeerDependencies: + - debug + + walker@1.0.8: + dependencies: + makeerror: 1.0.12 + + warning@4.0.3: + dependencies: + loose-envify: 1.4.0 + + watchpack@2.4.4: + dependencies: + glob-to-regexp: 0.4.1 + graceful-fs: 4.2.11 + + wbuf@1.7.3: + dependencies: + minimalistic-assert: 1.0.1 + + web-namespaces@2.0.1: {} + + web-streams-polyfill@3.3.3: {} + + webidl-conversions@3.0.1: {} + + webidl-conversions@4.0.2: {} + + webidl-conversions@5.0.0: {} + + webidl-conversions@6.1.0: {} + + webidl-conversions@7.0.0: {} + + webpack-dev-middleware@7.4.2(tslib@2.8.1)(webpack@5.102.1): + dependencies: + colorette: 2.0.20 + memfs: 4.57.1(tslib@2.8.1) + mime-types: 2.1.35 + on-finished: 2.4.1 + range-parser: 1.2.1 + schema-utils: 4.3.3 + optionalDependencies: + webpack: 5.102.1 + transitivePeerDependencies: + - tslib + + webpack-dev-server@4.15.2(bufferutil@4.0.9)(tslib@2.8.1)(utf-8-validate@6.0.5)(webpack@5.102.1): + dependencies: + '@types/bonjour': 3.5.13 + '@types/connect-history-api-fallback': 1.5.4 + '@types/express': 4.17.23 + '@types/serve-index': 1.9.4 + '@types/serve-static': 1.15.9 + '@types/sockjs': 0.3.36 + '@types/ws': 8.18.1 + ansi-html-community: 0.0.8 + bonjour-service: 1.3.0 + chokidar: 3.6.0 + colorette: 2.0.20 + compression: 1.8.1 + connect-history-api-fallback: 2.0.0 + default-gateway: 6.0.3 + express: 4.21.2 + graceful-fs: 4.2.11 + html-entities: 2.6.0 + http-proxy-middleware: 3.0.3 + ipaddr.js: 2.2.0 + launch-editor: 2.11.1 + open: 8.4.2 + p-retry: 4.6.2 + rimraf: 3.0.2 + schema-utils: 4.3.3 + selfsigned: 2.4.1 + serve-index: 1.9.1 + sockjs: 0.3.24 + spdy: 4.0.2 + webpack-dev-middleware: 7.4.2(tslib@2.8.1)(webpack@5.102.1) + ws: 8.18.3(bufferutil@4.0.9)(utf-8-validate@6.0.5) + optionalDependencies: + webpack: 5.102.1 + transitivePeerDependencies: + - bufferutil + - supports-color + - tslib + - utf-8-validate + + webpack-manifest-plugin@4.1.1(webpack@5.102.1): + dependencies: + tapable: 2.3.0 + webpack: 5.102.1 + webpack-sources: 2.3.1 + + webpack-sources@1.4.3: + dependencies: + source-list-map: 2.0.1 + source-map: 0.6.1 + + webpack-sources@2.3.1: + dependencies: + source-list-map: 2.0.1 + source-map: 0.6.1 + + webpack-sources@3.3.3: {} + + webpack@5.102.1: + dependencies: + '@types/eslint-scope': 3.7.7 + '@types/estree': 1.0.8 + '@types/json-schema': 7.0.15 + '@webassemblyjs/ast': 1.14.1 + '@webassemblyjs/wasm-edit': 1.14.1 + '@webassemblyjs/wasm-parser': 1.14.1 + acorn: 8.15.0 + acorn-import-phases: 1.0.4(acorn@8.15.0) + browserslist: 4.26.3 + chrome-trace-event: 1.0.4 + enhanced-resolve: 5.18.3 + es-module-lexer: 1.7.0 + eslint-scope: 5.1.1 + events: 3.3.0 + glob-to-regexp: 0.4.1 + graceful-fs: 4.2.11 + json-parse-even-better-errors: 2.3.1 + loader-runner: 4.3.1 + mime-types: 2.1.35 + neo-async: 2.6.2 + schema-utils: 4.3.3 + tapable: 2.3.0 + terser-webpack-plugin: 5.3.14(webpack@5.102.1) + watchpack: 2.4.4 + webpack-sources: 3.3.3 + transitivePeerDependencies: + - '@swc/core' + - esbuild + - uglify-js + + websocket-driver@0.7.4: + dependencies: + http-parser-js: 0.5.10 + safe-buffer: 5.2.1 + websocket-extensions: 0.1.4 + + websocket-extensions@0.1.4: {} + + whatwg-encoding@1.0.5: + dependencies: + iconv-lite: 0.4.24 + + whatwg-encoding@2.0.0: + dependencies: + iconv-lite: 0.6.3 + + whatwg-fetch@3.6.20: {} + + whatwg-mimetype@2.3.0: {} + + whatwg-mimetype@3.0.0: {} + + whatwg-url@11.0.0: + dependencies: + tr46: 3.0.0 + webidl-conversions: 7.0.0 + + whatwg-url@14.2.0: + dependencies: + tr46: 5.1.1 + webidl-conversions: 7.0.0 + optional: true + + whatwg-url@5.0.0: + dependencies: + tr46: 0.0.3 + webidl-conversions: 3.0.1 + + whatwg-url@7.1.0: + dependencies: + lodash.sortby: 4.7.0 + tr46: 1.0.1 + webidl-conversions: 4.0.2 + + whatwg-url@8.7.0: + dependencies: + lodash: 4.17.21 + tr46: 2.1.0 + webidl-conversions: 6.1.0 + + which-boxed-primitive@1.1.1: + dependencies: + is-bigint: 1.1.0 + is-boolean-object: 1.2.2 + is-number-object: 1.1.1 + is-string: 1.1.1 + is-symbol: 1.1.1 + + which-builtin-type@1.2.1: + dependencies: + call-bound: 1.0.4 + function.prototype.name: 1.1.8 + has-tostringtag: 1.0.2 + is-async-function: 2.1.1 + is-date-object: 1.1.0 + is-finalizationregistry: 1.1.1 + is-generator-function: 1.1.2 + is-regex: 1.2.1 + is-weakref: 1.1.1 + isarray: 2.0.5 + which-boxed-primitive: 1.1.1 + which-collection: 1.0.2 + which-typed-array: 1.1.19 + + which-collection@1.0.2: + dependencies: + is-map: 2.0.3 + is-set: 2.0.3 + is-weakmap: 2.0.2 + is-weakset: 2.0.4 + + which-typed-array@1.1.19: + dependencies: + available-typed-arrays: 1.0.7 + call-bind: 1.0.8 + call-bound: 1.0.4 + for-each: 0.3.5 + get-proto: 1.0.1 + gopd: 1.2.0 + has-tostringtag: 1.0.2 + + which@1.3.1: + dependencies: + isexe: 2.0.0 + + which@2.0.2: + dependencies: + isexe: 2.0.0 + + wicked-good-xpath@1.3.0: {} + + wide-align@1.1.5: + dependencies: + string-width: 4.2.3 + optional: true + + widest-line@4.0.1: + dependencies: + string-width: 5.1.2 + + winston-daily-rotate-file@5.0.0(winston@3.18.3): + dependencies: + file-stream-rotator: 0.6.1 + object-hash: 3.0.0 + triple-beam: 1.4.1 + winston: 3.18.3 + winston-transport: 4.9.0 + + winston-transport@4.9.0: + dependencies: + logform: 2.7.0 + readable-stream: 3.6.2 + triple-beam: 1.4.1 + + winston@3.18.3: + dependencies: + '@colors/colors': 1.6.0 + '@dabh/diagnostics': 2.0.8 + async: 3.2.6 + is-stream: 2.0.1 + logform: 2.7.0 + one-time: 1.0.0 + readable-stream: 3.6.2 + safe-stable-stringify: 2.5.0 + stack-trace: 0.0.10 + triple-beam: 1.4.1 + winston-transport: 4.9.0 + + word-wrap@1.2.5: {} + + workbox-background-sync@6.6.0: + dependencies: + idb: 7.1.1 + workbox-core: 6.6.0 + + workbox-broadcast-update@6.6.0: + dependencies: + workbox-core: 6.6.0 + + workbox-build@6.6.0(@types/babel__core@7.20.5): + dependencies: + '@apideck/better-ajv-errors': 0.3.6(ajv@8.17.1) + '@babel/core': 7.28.4 + '@babel/preset-env': 7.28.3(@babel/core@7.28.4) + '@babel/runtime': 7.28.4 + '@rollup/plugin-babel': 5.3.1(@babel/core@7.28.4)(@types/babel__core@7.20.5)(rollup@4.45.0) + '@rollup/plugin-node-resolve': 11.2.1(rollup@4.45.0) + '@rollup/plugin-replace': 2.4.2(rollup@4.45.0) + '@surma/rollup-plugin-off-main-thread': 2.2.3 + ajv: 8.17.1 + common-tags: 1.8.2 + fast-json-stable-stringify: 2.1.0 + fs-extra: 9.1.0 + glob: 10.5.0 + lodash: 4.17.21 + pretty-bytes: 5.6.0 + rollup: 4.45.0 + rollup-plugin-terser: 7.0.2(rollup@4.45.0) + source-map: 0.8.0-beta.0 + stringify-object: 3.3.0 + strip-comments: 2.0.1 + tempy: 0.6.0 + upath: 1.2.0 + workbox-background-sync: 6.6.0 + workbox-broadcast-update: 6.6.0 + workbox-cacheable-response: 6.6.0 + workbox-core: 6.6.0 + workbox-expiration: 6.6.0 + workbox-google-analytics: 6.6.0 + workbox-navigation-preload: 6.6.0 + workbox-precaching: 6.6.0 + workbox-range-requests: 6.6.0 + workbox-recipes: 6.6.0 + workbox-routing: 6.6.0 + workbox-strategies: 6.6.0 + workbox-streams: 6.6.0 + workbox-sw: 6.6.0 + workbox-window: 6.6.0 + transitivePeerDependencies: + - '@types/babel__core' + - supports-color + + workbox-cacheable-response@6.6.0: + dependencies: + workbox-core: 6.6.0 + + workbox-core@6.6.0: {} + + workbox-expiration@6.6.0: + dependencies: + idb: 7.1.1 + workbox-core: 6.6.0 + + workbox-google-analytics@6.6.0: + dependencies: + workbox-background-sync: 6.6.0 + workbox-core: 6.6.0 + workbox-routing: 6.6.0 + workbox-strategies: 6.6.0 + + workbox-navigation-preload@6.6.0: + dependencies: + workbox-core: 6.6.0 + + workbox-precaching@6.6.0: + dependencies: + workbox-core: 6.6.0 + workbox-routing: 6.6.0 + workbox-strategies: 6.6.0 + + workbox-range-requests@6.6.0: + dependencies: + workbox-core: 6.6.0 + + workbox-recipes@6.6.0: + dependencies: + workbox-cacheable-response: 6.6.0 + workbox-core: 6.6.0 + workbox-expiration: 6.6.0 + workbox-precaching: 6.6.0 + workbox-routing: 6.6.0 + workbox-strategies: 6.6.0 + + workbox-routing@6.6.0: + dependencies: + workbox-core: 6.6.0 + + workbox-strategies@6.6.0: + dependencies: + workbox-core: 6.6.0 + + workbox-streams@6.6.0: + dependencies: + workbox-core: 6.6.0 + workbox-routing: 6.6.0 + + workbox-sw@6.6.0: {} + + workbox-webpack-plugin@6.6.0(@types/babel__core@7.20.5)(webpack@5.102.1): + dependencies: + fast-json-stable-stringify: 2.1.0 + pretty-bytes: 5.6.0 + upath: 1.2.0 + webpack: 5.102.1 + webpack-sources: 1.4.3 + workbox-build: 6.6.0(@types/babel__core@7.20.5) + transitivePeerDependencies: + - '@types/babel__core' + - supports-color + + workbox-window@6.6.0: + dependencies: + '@types/trusted-types': 2.0.7 + workbox-core: 6.6.0 + + wrap-ansi@6.2.0: + dependencies: + ansi-styles: 4.3.0 + string-width: 4.2.3 + strip-ansi: 6.0.1 + + wrap-ansi@7.0.0: + dependencies: + ansi-styles: 4.3.0 + string-width: 4.2.3 + strip-ansi: 6.0.1 + + wrap-ansi@8.1.0: + dependencies: + ansi-styles: 6.2.3 + string-width: 5.1.2 + strip-ansi: 7.1.2 + + wrappy@1.0.2: {} + + write-file-atomic@3.0.3: + dependencies: + imurmurhash: 0.1.4 + is-typedarray: 1.0.0 + signal-exit: 3.0.7 + typedarray-to-buffer: 3.1.5 + + ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@6.0.5): + optionalDependencies: + bufferutil: 4.0.9 + utf-8-validate: 6.0.5 + + xdg-default-browser@2.1.0: + dependencies: + execa: 0.2.2 + titleize: 1.0.1 + + xml-name-validator@3.0.0: {} + + xml-name-validator@4.0.0: {} + + xmlchars@2.2.0: {} + + xmlhttprequest-ssl@2.1.2: {} + + xtend@4.0.2: {} + + y18n@5.0.8: {} + + yallist@2.1.2: {} + + yallist@3.1.1: {} + + yallist@4.0.0: {} + + yaml@1.10.2: {} + + yaml@2.3.1: {} + + yaml@2.8.1: + optional: true + + yargs-parser@20.2.9: {} + + yargs-parser@21.1.1: {} + + yargs@16.2.0: + dependencies: + cliui: 7.0.4 + escalade: 3.2.0 + get-caller-file: 2.0.5 + require-directory: 2.1.1 + string-width: 4.2.3 + y18n: 5.0.8 + yargs-parser: 20.2.9 + + yargs@17.7.2: + dependencies: + cliui: 8.0.1 + escalade: 3.2.0 + get-caller-file: 2.0.5 + require-directory: 2.1.1 + string-width: 4.2.3 + y18n: 5.0.8 + yargs-parser: 21.1.1 + + yauzl@2.10.0: + dependencies: + buffer-crc32: 0.2.13 + fd-slicer: 1.1.0 + + yn@3.1.1: {} + + yocto-queue@0.1.0: {} + + yup@0.32.11: + dependencies: + '@babel/runtime': 7.28.4 + '@types/lodash': 4.17.20 + lodash: 4.17.21 + lodash-es: 4.17.21 + nanoclone: 0.2.1 + property-expr: 2.0.6 + toposort: 2.0.2 + + zip-stream@5.0.2: + dependencies: + archiver-utils: 4.0.1 + compress-commons: 5.0.3 + readable-stream: 3.6.2 + + zod@3.25.76: {} + + zustand@4.5.7(@types/react@18.3.26)(immer@11.1.4)(react@18.3.1): + dependencies: + use-sync-external-store: 1.6.0(react@18.3.1) + optionalDependencies: + '@types/react': 18.3.26 + immer: 11.1.4 + react: 18.3.1 + + zwitch@2.0.4: {} From 66f096df76a745a044fad611d1a9a3059c49d4e8 Mon Sep 17 00:00:00 2001 From: wwanarif Date: Mon, 20 Apr 2026 02:17:30 +0000 Subject: [PATCH 3/9] initial push evaluation on development compose Signed-off-by: wwanarif --- .gitignore | 1 + .../docker-compose.local.yml | 247 +++++ .../local-nginx/nginx.conf.template | 210 ++++ studio-backend/app/main.py | 5 +- studio-backend/app/requirements.txt | 3 +- .../app/routers/evaluation_router.py | 53 + studio-backend/app/routers/sandbox_router.py | 16 + .../app/services/dashboard_service.py | 18 +- studio-eval/Dockerfile | 20 + studio-eval/app/__init__.py | 0 studio-eval/app/compat.py | 46 + studio-eval/app/db/__init__.py | 0 studio-eval/app/db/database.py | 27 + studio-eval/app/db/schema.py | 92 ++ studio-eval/app/main.py | 76 ++ studio-eval/app/models/__init__.py | 0 studio-eval/app/models/eval_models.py | 215 +++++ studio-eval/app/routers/__init__.py | 0 studio-eval/app/routers/eval_router.py | 145 +++ studio-eval/app/services/__init__.py | 0 studio-eval/app/services/dataset_service.py | 163 ++++ studio-eval/app/services/eval_service.py | 284 ++++++ studio-eval/app/services/ollama_service.py | 199 ++++ .../app/services/response_collector.py | 155 +++ studio-eval/requirements.txt | 9 + studio-eval/tests/test_eval_service.py | 15 + studio-eval/tests/test_ollama_service.py | 118 +++ studio-eval/tests/test_timeout_config.py | 42 + studio-frontend/.env.development | 6 - studio-frontend/packages/server/nodemon.json | 5 +- .../server/src/routes/evaluation/index.ts | 69 +- .../server/src/services/chatflows/index.ts | 98 +- .../server/src/services/finetuning/index.ts | 29 +- .../packages/ui/src/api/evaluation.js | 3 + .../src/views/evaluation/CreateRunModal.jsx | 205 +++- .../ui/src/views/evaluation/DatasetTab.jsx | 39 +- .../ui/src/views/evaluation/ExecutionTab.jsx | 269 +++++- .../src/views/evaluation/RunDetailsModal.jsx | 777 +++++++++++++++ .../packages/ui/src/views/tracer/index.jsx | 913 ++++++++++++++---- studio-frontend/packages/ui/vite.config.js | 12 +- studio-frontend/turbo.json | 16 + 41 files changed, 4268 insertions(+), 332 deletions(-) create mode 100644 setup-scripts/setup-development/docker-compose.local.yml create mode 100644 setup-scripts/setup-development/local-nginx/nginx.conf.template create mode 100644 studio-backend/app/routers/evaluation_router.py create mode 100644 studio-eval/Dockerfile create mode 100644 studio-eval/app/__init__.py create mode 100644 studio-eval/app/compat.py create mode 100644 studio-eval/app/db/__init__.py create mode 100644 studio-eval/app/db/database.py create mode 100644 studio-eval/app/db/schema.py create mode 100644 studio-eval/app/main.py create mode 100644 studio-eval/app/models/__init__.py create mode 100644 studio-eval/app/models/eval_models.py create mode 100644 studio-eval/app/routers/__init__.py create mode 100644 studio-eval/app/routers/eval_router.py create mode 100644 studio-eval/app/services/__init__.py create mode 100644 studio-eval/app/services/dataset_service.py create mode 100644 studio-eval/app/services/eval_service.py create mode 100644 studio-eval/app/services/ollama_service.py create mode 100644 studio-eval/app/services/response_collector.py create mode 100644 studio-eval/requirements.txt create mode 100644 studio-eval/tests/test_eval_service.py create mode 100644 studio-eval/tests/test_ollama_service.py create mode 100644 studio-eval/tests/test_timeout_config.py delete mode 100644 studio-frontend/.env.development create mode 100644 studio-frontend/packages/ui/src/views/evaluation/RunDetailsModal.jsx diff --git a/.gitignore b/.gitignore index cbbafb0..b4ce61a 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,7 @@ test-results/ **.log **/report.html +.env docker-compose **/node_modules/ **.venv/ diff --git a/setup-scripts/setup-development/docker-compose.local.yml b/setup-scripts/setup-development/docker-compose.local.yml new file mode 100644 index 0000000..9a00b4f --- /dev/null +++ b/setup-scripts/setup-development/docker-compose.local.yml @@ -0,0 +1,247 @@ +# Local development compose — simulates K8s studio namespace +# Sandbox deploy can target a real cluster when studio-backend has a working +# kubeconfig and any cluster-side dependencies it calls are reachable. +# Run: docker compose -f docker-compose.local.yml up -d + +services: + + studio-frontend: + image: studio-frontend:local + build: + context: ../../studio-frontend + target: builder + args: + http_proxy: ${http_proxy} + https_proxy: ${https_proxy} + no_proxy: ${no_proxy} + container_name: studio-frontend + ports: + - "3000:3000" + - "8088:8088" + volumes: + - ../../studio-frontend:/usr/src + - ./.env:/usr/src/packages/server/.env:ro + - ./.env:/usr/src/packages/ui/.env:ro + - ../../sample-workflows:/usr/src/sample-workflows:ro + - node_modules:/usr/src/node_modules + - pnpm-store:/usr/src/.pnpm-store + - /usr/src/packages/ui/build + - finetuning-output:/tmp/finetuning/output + command: ["sh", "/usr/src/docker-entrypoint.dev.sh"] + environment: + - http_proxy=${http_proxy} + - https_proxy=${https_proxy} + - no_proxy=${no_proxy},studio-backend,studio-eval,mysql,ollama,finetuning-server,local-nginx,.svc.cluster.local,.cluster.local + - STUDIO_SERVER_URL=${STUDIO_SERVER_URL:-http://studio-backend:5000} + - PREPARE_DOC_REDIS_PREP_DNS=${PREPARE_DOC_REDIS_PREP_DNS:-local-nginx} + - CHOKIDAR_USEPOLLING=1 + - FINETUNING_SERVICE_DNS=${FINETUNING_SERVICE_DNS:-finetuning-server:8015} + - FINETUNING_RAY_DNS=${FINETUNING_RAY_DNS:-finetuning-server:8265} + - DATABASE_TYPE=mysql + - DATABASE_HOST=${MYSQL_HOST:-mysql} + - DATABASE_PORT=${MYSQL_PORT:-3306} + - DATABASE_NAME=${MYSQL_FLOWISE_DB:-flowise} + - DATABASE_USER=${MYSQL_USER:-root} + - DATABASE_PASSWORD=${MYSQL_PASSWORD:-password} + ulimits: + nofile: + soft: 65536 + hard: 65536 + stdin_open: true + tty: true + networks: + - studio-local + depends_on: + - studio-backend + restart: unless-stopped + + studio-backend: + build: + context: ../../studio-backend + args: + http_proxy: ${http_proxy} + https_proxy: ${https_proxy} + no_proxy: ${no_proxy} + container_name: studio-backend + ports: + - "5001:5000" + volumes: + - ../../studio-backend/app:/usr/src/app + - ${HOME}/.kube:/root/.kube:ro + command: ["uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "5000", "--reload"] + environment: + - EVAL_SERVICE_DNS=${EVAL_SERVICE_DNS:-studio-eval:8000} + - GRAFANA_DNS=${GRAFANA_DNS:-grafana:3000} + - DISABLE_GRAFANA_DASHBOARD_SYNC=${DISABLE_GRAFANA_DASHBOARD_SYNC:-true} + - CLICKHOUSE_DNS=${CLICKHOUSE_DNS:-clickhouse:9000} + - APP_FRONTEND_IMAGE=${APP_FRONTEND_IMAGE:-opea/app-frontend:latest} + - APP_BACKEND_IMAGE=${APP_BACKEND_IMAGE:-opea/app-backend:latest} + - SBX_HTTP_PROXY=${http_proxy} + - SBX_NO_PROXY=${no_proxy},studio-eval,mysql,ollama,studio-frontend,finetuning-server,grafana,clickhouse,.svc.cluster.local,.cluster.local + - http_proxy=${http_proxy} + - https_proxy=${https_proxy} + - no_proxy=${no_proxy},studio-eval,mysql,ollama,studio-frontend,finetuning-server,grafana,clickhouse,.svc.cluster.local,.cluster.local + networks: + - studio-local + depends_on: + mysql: + condition: service_healthy + restart: unless-stopped + + studio-eval: + build: + context: ../../studio-eval + args: + http_proxy: ${http_proxy} + https_proxy: ${https_proxy} + no_proxy: ${no_proxy} + container_name: studio-eval + ports: + - "8000:8000" + volumes: + - ../../studio-eval/app:/app/app + command: ["uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "8000", "--reload"] + environment: + - OLLAMA_DNS=${OLLAMA_DNS:-ollama:11434} + - MYSQL_HOST=${MYSQL_HOST:-mysql} + - MYSQL_USER=${MYSQL_USER:-root} + - MYSQL_PASSWORD=${MYSQL_PASSWORD:-password} + - MYSQL_DB=${MYSQL_EVAL_DB:-eval} + - APP_BACKEND_URL_TEMPLATE=${APP_BACKEND_URL_TEMPLATE:-http://local-nginx/v1/app-backend?ns={sandbox_id}} + - http_proxy=${http_proxy} + - https_proxy=${https_proxy} + - no_proxy=${no_proxy},host.docker.internal,localhost,127.0.0.1,ollama,mysql,studio-backend,studio-frontend,finetuning-server,.svc.cluster.local,.cluster.local + extra_hosts: + - "host.docker.internal:host-gateway" + networks: + - studio-local + depends_on: + mysql: + condition: service_healthy + ollama: + condition: service_started + restart: unless-stopped + + ollama: + image: ollama/ollama:latest + container_name: ollama + ports: + - "11434:11434" + volumes: + - ollama-models:/root/.ollama + environment: + - http_proxy=${http_proxy} + - https_proxy=${https_proxy} + - no_proxy=${no_proxy},studio-eval,studio-backend,studio-frontend,mysql,finetuning-server + - HTTP_PROXY=${http_proxy} + - HTTPS_PROXY=${https_proxy} + - NO_PROXY=${no_proxy},studio-eval,studio-backend,studio-frontend,mysql,finetuning-server + - OLLAMA_INSECURE=true + networks: + - studio-local + # Pull models after startup (OLLAMA_HOST must be overridden for docker exec to work): + # docker exec -e OLLAMA_HOST=http://127.0.0.1:11434 ollama ollama pull nomic-embed-text + # docker exec -e OLLAMA_HOST=http://127.0.0.1:11434 ollama ollama pull llama3 + restart: unless-stopped + + mysql: + image: mysql:8.0 + container_name: mysql + ports: + - "3306:3306" + environment: + - MYSQL_ROOT_PASSWORD=${MYSQL_ROOT_PASSWORD:-password} + - MYSQL_DATABASE=${MYSQL_FLOWISE_DB:-flowise} + volumes: + - mysql-data:/var/lib/mysql + configs: + - source: mysql-init-sql + target: /docker-entrypoint-initdb.d/01-create-eval-db.sql + networks: + - studio-local + healthcheck: + test: ["CMD", "mysql", "-uroot", "-ppassword", "-e", "SELECT 1"] + interval: 10s + timeout: 5s + retries: 10 + start_period: 60s + restart: unless-stopped + + # Local equivalent of studio-nginx in studio-manifest.yaml. + # Routes /?ns= to app-frontend in the deployed K8s sandbox, + # and everything else to studio-frontend. + # Access the studio via http://localhost:30010 (not localhost:3000) so that + # the ?ns= routing works correctly when opening sandbox app-frontends. + local-nginx: + image: nginx:1.27.1 + container_name: local-nginx + ports: + - "30010:80" + volumes: + - ./local-nginx/nginx.conf.template:/tmp/nginx.conf.template:ro + # Mirror the studio-manifest init-container approach: envsubst only *_DNS and + # K8S_DNS_RESOLVER so that nginx's own $namespace, $host, etc. are preserved. + command: + - /bin/sh + - -c + - >- + envsubst '$$STUDIO_FRONTEND_DNS $$APP_FRONTEND_DNS $$APP_BACKEND_DNS + $$GRAFANA_DNS $$APP_CHATHISTORY_DNS $$PREPARE_DOC_REDIS_PREP_DNS + $$STUDIO_BACKEND_DNS $$K8S_DNS_RESOLVER' + < /tmp/nginx.conf.template > /etc/nginx/conf.d/default.conf && + nginx -g 'daemon off;' + environment: + # Local docker service names for studio-layer services + - STUDIO_FRONTEND_DNS=studio-frontend:8088 + - STUDIO_BACKEND_DNS=studio-backend:5000 + - GRAFANA_DNS=${GRAFANA_DNS:-grafana:3000} + # K8s cluster DNS names — $namespace is left as a literal nginx variable + # ($$namespace in compose becomes $namespace in the container, which + # envsubst leaves untouched so nginx resolves it per-request) + - APP_FRONTEND_DNS=app-frontend.$$namespace.svc.cluster.local:5275 + - APP_BACKEND_DNS=app-backend.$$namespace.svc.cluster.local:8899 + - APP_CHATHISTORY_DNS=chathistory-mongo.$$namespace.svc.cluster.local:6012 + - PREPARE_DOC_REDIS_PREP_DNS=opea-prepare-doc-redis-prep-0.$$namespace.svc.cluster.local:6007 + # CoreDNS ClusterIP of your K8s cluster — required for nginx to resolve + # *.svc.cluster.local names at request time. + # Common values: 10.96.0.10 (kubeadm), 10.43.0.10 (k3s) + - K8S_DNS_RESOLVER=${K8S_DNS_RESOLVER:?K8S_DNS_RESOLVER must be set to the CoreDNS ClusterIP (e.g. 10.96.0.10)} + networks: + - studio-local + depends_on: + - studio-frontend + restart: unless-stopped + + finetuning-server: + image: opea/finetuning:latest + container_name: finetuning-server + user: "0:0" + ipc: host + ports: + - "8015:8015" + - "8265:8265" + environment: + - http_proxy=${http_proxy} + - https_proxy=${https_proxy} + - NO_PROXY=${no_proxy} + volumes: + - finetuning-output:/home/user/comps/finetuning/src/output + networks: + - studio-local + restart: unless-stopped + +networks: + studio-local: + driver: bridge + +configs: + mysql-init-sql: + content: | + CREATE DATABASE IF NOT EXISTS ${MYSQL_EVAL_DB:-eval}; + +volumes: + ollama-models: + mysql-data: + node_modules: + pnpm-store: + finetuning-output: diff --git a/setup-scripts/setup-development/local-nginx/nginx.conf.template b/setup-scripts/setup-development/local-nginx/nginx.conf.template new file mode 100644 index 0000000..bb857c5 --- /dev/null +++ b/setup-scripts/setup-development/local-nginx/nginx.conf.template @@ -0,0 +1,210 @@ +# Copyright (C) 2024 Intel Corporation +# SPDX-License-Identifier: Apache-2.0 +# +# Local-compose equivalent of the studio-nginx config in studio-manifest.yaml. +# Mirrors the same ?ns= routing logic so that sandbox app-frontend is reachable +# from the browser when running docker-compose.local.yml. +# +# Access the studio via http://localhost:30010 (not localhost:3000 directly) +# so that the nginx can route /?ns= to the K8s sandbox app-frontend. +# +# Required env var: +# K8S_DNS_RESOLVER — CoreDNS ClusterIP of your K8s cluster (e.g. 10.96.0.10 +# for kubeadm or 10.43.0.10 for k3s). Needed so nginx +# can resolve app-frontend..svc.cluster.local +# at request time. + +server { + listen 80; + listen [::]:80; + + proxy_connect_timeout 600; + proxy_send_timeout 600; + proxy_read_timeout 600; + send_timeout 600; + + client_max_body_size 10G; + + # K8s CoreDNS — lets nginx resolve *.svc.cluster.local names at request time. + resolver ${K8S_DNS_RESOLVER} valid=30s; + resolver_timeout 5s; + + # Location block for studio-frontend and app-frontend + location / { + # Initialize the default variable for namespace + set $namespace ""; + + # Check if the Referer contains '?ns=' and extract the ns argument + if ($http_referer ~* "([&?]ns=([^&]+))") { + set $namespace $2; # Capture the value of 'ns' + } + + # If 'ns' is not in the Referer, fallback to 'arg_ns' + if ($namespace = "") { + set $namespace $arg_ns; # Use the value from the 'arg_ns' + } + + # When no namespace — pass straight through to studio-frontend unchanged + if ($namespace = "") { + proxy_pass http://${STUDIO_FRONTEND_DNS}; + break; + } + + # Namespace present — route to sandbox app-frontend with no-cache headers + add_header Cache-Control "no-cache, no-store, must-revalidate"; + add_header Pragma "no-cache"; + add_header Expires "0"; + + set $app_frontend http://${APP_FRONTEND_DNS}; + rewrite ^/(.*)$ /$1?ns=$namespace break; + proxy_pass $app_frontend; + + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + } + + # Location block for app-backend + location /v1/app-backend { + # Initialize the variable for namespace + set $namespace ""; + if ($http_referer ~* "([&?]ns=([^&]+))") { + set $namespace $2; # Capture the value of 'ns' + } + + # If 'ns' is not in the Referer, fallback to 'arg_ns' + if ($namespace = "") { + set $namespace $arg_ns; + } + + # Rewrite the request to include the namespace + rewrite ^/(.*)$ /$1?ns=$namespace break; + + # Proxy to the desired service using the namespace + proxy_pass http://${APP_BACKEND_DNS}; + + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + + # Disable buffering for SSE + proxy_buffering off; + proxy_cache off; + proxy_http_version 1.1; + proxy_set_header Connection ''; + chunked_transfer_encoding off; + } + + # Location block for opea_service@prepare_doc_redis_prep + location /v1/dataprep { + # Initialize the variable for namespace + set $namespace ""; + if ($http_referer ~* "([&?]ns=([^&]+))") { + set $namespace $2; # Capture the value of 'ns' + } + + # If 'ns' is not in the Referer, fallback to 'arg_ns' + # (used by server-side requests from studio-frontend Express) + if ($namespace = "") { + set $namespace $arg_ns; + } + + # Rewrite the request to include the namespace + rewrite ^/(.*)$ /$1?ns=$namespace break; + + # Proxy to the desired service using the namespace + proxy_pass http://${PREPARE_DOC_REDIS_PREP_DNS}; + + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + } + + # Additional location block for Grafana static assets + location /grafana { + set $grafana_upstream http://${GRAFANA_DNS}; + proxy_pass $grafana_upstream; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + } + + # Proxy Grafana Live WebSocket connections + location /grafana/api/live/ { + set $grafana_upstream http://${GRAFANA_DNS}; + proxy_pass $grafana_upstream; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + + # Ensure WebSocket upgrade headers + proxy_http_version 1.1; + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection "upgrade"; + } + + # WebSocket connection for fine-tuning job monitoring and downloads + # Routes directly to studio-frontend's socket.io handlers + location /socket.io { + proxy_pass http://${STUDIO_FRONTEND_DNS}; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + + # Ensure WebSocket upgrade headers + proxy_http_version 1.1; + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection "upgrade"; + + # Disable buffering for WebSocket + proxy_buffering off; + proxy_cache off; + } + + location /studio-backend { + proxy_pass http://${STUDIO_BACKEND_DNS}; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + } + + location /studio-backend/ws { + proxy_pass http://${STUDIO_BACKEND_DNS}/studio-backend/ws; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + + # Ensure WebSocket upgrade headers + proxy_http_version 1.1; + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection "upgrade"; + } + + # Location block for chathistory service + location /v1/chathistory { + # Initialize the variable for namespace + set $namespace ""; + if ($http_referer ~* "([&?]ns=([^&]+))") { + set $namespace $2; # Capture the value of 'ns' + } + + # Rewrite the request to include the namespace + rewrite ^/(.*)$ /$1?ns=$namespace break; + + # Proxy to the desired service using the namespace + proxy_pass http://${APP_CHATHISTORY_DNS}; + + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + } +} diff --git a/studio-backend/app/main.py b/studio-backend/app/main.py index 85c291b..e129e37 100644 --- a/studio-backend/app/main.py +++ b/studio-backend/app/main.py @@ -32,11 +32,12 @@ def read_health(): allow_headers=["*"], ) -from .routers import user_router, download_router, sandbox_router, llmtraces_router, debuglog_router, clickdeploy_router +from .routers import user_router, download_router, sandbox_router, llmtraces_router, debuglog_router, clickdeploy_router, evaluation_router app.include_router(user_router.router, prefix="/studio-backend") app.include_router(download_router.router, prefix="/studio-backend") app.include_router(sandbox_router.router, prefix="/studio-backend") app.include_router(llmtraces_router.router, prefix="/studio-backend") app.include_router(debuglog_router.router, prefix="/studio-backend") -app.include_router(clickdeploy_router.router, prefix="/studio-backend") \ No newline at end of file +app.include_router(clickdeploy_router.router, prefix="/studio-backend") +app.include_router(evaluation_router.router, prefix="/studio-backend") \ No newline at end of file diff --git a/studio-backend/app/requirements.txt b/studio-backend/app/requirements.txt index fea8e19..65b56d3 100644 --- a/studio-backend/app/requirements.txt +++ b/studio-backend/app/requirements.txt @@ -7,4 +7,5 @@ pydantic==1.10.18 starlette==0.49.1 websockets==10.3 clickhouse-driver==0.2.9 -paramiko==3.5.1 \ No newline at end of file +paramiko==3.5.1 +httpx==0.27.2 \ No newline at end of file diff --git a/studio-backend/app/routers/evaluation_router.py b/studio-backend/app/routers/evaluation_router.py new file mode 100644 index 0000000..0ea782d --- /dev/null +++ b/studio-backend/app/routers/evaluation_router.py @@ -0,0 +1,53 @@ +import os + +import httpx +from fastapi import APIRouter, HTTPException, Request +from fastapi.responses import Response + +EVAL_SERVICE_DNS = os.getenv( + "EVAL_SERVICE_DNS", + "studio-eval-service.studio.svc.cluster.local:8000", +) + +router = APIRouter() + + +@router.api_route("/evaluation/{path:path}", methods=["GET", "POST", "PUT", "PATCH", "DELETE", "OPTIONS", "HEAD"]) +async def proxy_evaluation(path: str, request: Request): + target_url = f"http://{EVAL_SERVICE_DNS}/eval/{path}" + query = request.url.query + if query: + target_url = f"{target_url}?{query}" + + headers = dict(request.headers) + # Remove hop-by-hop headers that must not be forwarded + for hop in ("host", "transfer-encoding", "connection"): + headers.pop(hop, None) + + body = await request.body() + + try: + async with httpx.AsyncClient(timeout=60.0) as client: + upstream = await client.request( + method=request.method, + url=target_url, + headers=headers, + content=body, + ) + except httpx.ConnectError as exc: + raise HTTPException(status_code=502, detail=f"Cannot reach eval service: {exc}") + except httpx.TimeoutException as exc: + raise HTTPException(status_code=504, detail=f"Eval service timed out: {exc}") + + # Strip hop-by-hop headers from the upstream response before forwarding + excluded = {"transfer-encoding", "connection", "keep-alive", "te", "trailers", "upgrade"} + response_headers = { + k: v for k, v in upstream.headers.items() if k.lower() not in excluded + } + + return Response( + content=upstream.content, + status_code=upstream.status_code, + headers=response_headers, + media_type=upstream.headers.get("content-type"), + ) diff --git a/studio-backend/app/routers/sandbox_router.py b/studio-backend/app/routers/sandbox_router.py index 5bdf4d7..136a067 100644 --- a/studio-backend/app/routers/sandbox_router.py +++ b/studio-backend/app/routers/sandbox_router.py @@ -36,6 +36,22 @@ async def delete_sandbox(request: WorkflowId): return response +@router.get("/sandbox/list") +async def list_sandboxes(): + core_v1_api = client.CoreV1Api() + try: + namespaces = core_v1_api.list_namespace() + except Exception as e: + raise HTTPException(status_code=500, detail=str(e)) + + sandboxes = [ + {"id": ns.metadata.name, "status": ns.status.phase} + for ns in namespaces.items + if ns.metadata.name.startswith("sandbox-") + ] + return {"sandboxes": sandboxes} + + @router.websocket("/ws/sandbox-status") async def check_sandbox_status(websocket: WebSocket): print('checking sandbox status') diff --git a/studio-backend/app/services/dashboard_service.py b/studio-backend/app/services/dashboard_service.py index 8535429..2f4fb0c 100644 --- a/studio-backend/app/services/dashboard_service.py +++ b/studio-backend/app/services/dashboard_service.py @@ -8,8 +8,17 @@ # Read environment variables template_file_path = os.path.join(os.path.dirname(__file__), '..', 'templates', 'grafana-dashboards', 'sandbox-dashboard.json') + +def _is_grafana_disabled() -> bool: + return os.getenv("DISABLE_GRAFANA_DASHBOARD_SYNC", "false").lower() in {"1", "true", "yes", "on"} + def import_grafana_dashboards(namespace_name): + if _is_grafana_disabled(): + msg = "Grafana dashboard sync disabled by DISABLE_GRAFANA_DASHBOARD_SYNC" + print(msg) + return msg + print("Getting post_url") grafana_url = os.getenv("GRAFANA_DNS", "localhost:30007") post_url = f"http://{grafana_url}/grafana/api/dashboards/db" @@ -32,16 +41,21 @@ def import_grafana_dashboards(namespace_name): print(f"Importing dashboard {namespace_name}") print(f"post_url: {post_url}") + response = None try: response = requests.post(post_url, headers=headers, data=json.dumps(dashboard_json)) print(f"Response Status Code: {response.status_code}") print(f"Response Text: {response.text}") except Exception as e: print(f"Exception occurred: {e}") - - return response.text + + return response.text if response is not None else "Grafana dashboard import skipped due to request error" def delete_dashboard(namespace_name): + if _is_grafana_disabled(): + print("Grafana dashboard delete skipped by DISABLE_GRAFANA_DASHBOARD_SYNC") + return + grafana_url = os.getenv("GRAFANA_DNS", "localhost:30007") post_url = f"http://{grafana_url}/grafana/api/dashboards/uid/{namespace_name.replace('sandbox-','')}" auth_str = f"admin:prom-operator" diff --git a/studio-eval/Dockerfile b/studio-eval/Dockerfile new file mode 100644 index 0000000..2a41de8 --- /dev/null +++ b/studio-eval/Dockerfile @@ -0,0 +1,20 @@ +FROM python:3.11-slim + +ARG http_proxy +ARG https_proxy +ARG no_proxy + +ENV http_proxy=${http_proxy} +ENV https_proxy=${https_proxy} +ENV no_proxy=${no_proxy} + +WORKDIR /app + +COPY requirements.txt . +RUN pip install --no-cache-dir -r requirements.txt + +COPY app/ ./app/ + +EXPOSE 8000 + +CMD ["uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "8000"] diff --git a/studio-eval/app/__init__.py b/studio-eval/app/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/studio-eval/app/compat.py b/studio-eval/app/compat.py new file mode 100644 index 0000000..eae1b90 --- /dev/null +++ b/studio-eval/app/compat.py @@ -0,0 +1,46 @@ +"""Compatibility shims for third-party libraries. + +This service uses Pydantic v1, while some third-party packages imported at +runtime expect small parts of the Pydantic v2 API surface. Keep the shim local +and minimal so the rest of the app can continue using explicit Pydantic v1 +syntax. +""" + +import pydantic +from pydantic import root_validator, validator + + +def ensure_pydantic_v2_apis() -> None: + """Provide a small subset of Pydantic v2 APIs on top of Pydantic v1. + + DeepEval currently imports ``field_validator`` and ``model_validator`` from + ``pydantic``. Those names do not exist in Pydantic v1, so add compatible + wrappers before importing DeepEval. + """ + + if not hasattr(pydantic, "field_validator"): + def field_validator(*fields, mode="after", **kwargs): + pre = mode == "before" + return validator(*fields, pre=pre, allow_reuse=True, **kwargs) + + pydantic.field_validator = field_validator # type: ignore[attr-defined] + + if not hasattr(pydantic, "model_validator"): + def model_validator(*, mode="after", **kwargs): + pre = mode == "before" + + def decorator(func): + return root_validator(pre=pre, allow_reuse=True, **kwargs)(func) + + return decorator + + pydantic.model_validator = model_validator # type: ignore[attr-defined] + + if not hasattr(pydantic, "ConfigDict"): + pydantic.ConfigDict = dict # type: ignore[attr-defined] + + if not hasattr(pydantic.BaseModel, "model_dump"): + pydantic.BaseModel.model_dump = pydantic.BaseModel.dict # type: ignore[attr-defined] + + if not hasattr(pydantic.BaseModel, "model_copy"): + pydantic.BaseModel.model_copy = pydantic.BaseModel.copy # type: ignore[attr-defined] diff --git a/studio-eval/app/db/__init__.py b/studio-eval/app/db/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/studio-eval/app/db/database.py b/studio-eval/app/db/database.py new file mode 100644 index 0000000..a8a8180 --- /dev/null +++ b/studio-eval/app/db/database.py @@ -0,0 +1,27 @@ +import os +from sqlalchemy import create_engine +from sqlalchemy.ext.declarative import declarative_base +from sqlalchemy.orm import sessionmaker + +MYSQL_HOST = os.getenv("MYSQL_HOST", "localhost") +MYSQL_PORT = os.getenv("MYSQL_PORT", "3306") +MYSQL_USER = os.getenv("MYSQL_USER", "root") +MYSQL_PASSWORD = os.getenv("MYSQL_PASSWORD", "") +MYSQL_DB = os.getenv("MYSQL_DB", "eval") + +DATABASE_URL = ( + f"mysql+pymysql://{MYSQL_USER}:{MYSQL_PASSWORD}" + f"@{MYSQL_HOST}:{MYSQL_PORT}/{MYSQL_DB}" +) + +engine = create_engine(DATABASE_URL, pool_pre_ping=True) +SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine) +Base = declarative_base() + + +def get_db(): + db = SessionLocal() + try: + yield db + finally: + db.close() diff --git a/studio-eval/app/db/schema.py b/studio-eval/app/db/schema.py new file mode 100644 index 0000000..a2461a6 --- /dev/null +++ b/studio-eval/app/db/schema.py @@ -0,0 +1,92 @@ +from datetime import datetime +import uuid + +from sqlalchemy import ( + Boolean, + Column, + DateTime, + Float, + ForeignKey, + Integer, + JSON, + String, + Text, +) +from sqlalchemy.orm import relationship + +from app.db.database import Base + + +class EvalDataset(Base): + __tablename__ = "eval_datasets" + + id = Column(Integer, primary_key=True, index=True) + name = Column(String(255), nullable=False) + description = Column(Text, nullable=True) + created_at = Column(DateTime, default=datetime.utcnow) + updated_at = Column(DateTime, default=datetime.utcnow, onupdate=datetime.utcnow) + + entries = relationship( + "EvalDatasetEntry", + back_populates="dataset", + cascade="all, delete-orphan", + ) + runs = relationship("EvalRun", back_populates="dataset") + + +class EvalDatasetEntry(Base): + __tablename__ = "eval_dataset_entries" + + id = Column(Integer, primary_key=True, index=True) + dataset_id = Column(Integer, ForeignKey("eval_datasets.id"), nullable=False) + input = Column(Text, nullable=False) + expected_output = Column(Text, nullable=True) + context = Column(JSON, nullable=True) # list[str] + created_at = Column(DateTime, default=datetime.utcnow) + + dataset = relationship("EvalDataset", back_populates="entries") + results = relationship("EvalRunResult", back_populates="entry") + + +class EvalRun(Base): + __tablename__ = "eval_runs" + + id = Column(String(36), primary_key=True, index=True, default=lambda: str(uuid.uuid4())) + name = Column(String(255), nullable=False) + dataset_id = Column(Integer, ForeignKey("eval_datasets.id"), nullable=False) + sandbox_id = Column(String(255), nullable=False) + model_name = Column(String(255), nullable=False) + metrics = Column(JSON, nullable=False) # list[str] + status = Column(String(50), default="pending") # pending/running/completed/failed + created_at = Column(DateTime, default=datetime.utcnow) + completed_at = Column(DateTime, nullable=True) + error = Column(Text, nullable=True) + # App-backend request parameters for RAG evaluation + system_prompt = Column(Text, nullable=True) + temperature = Column(Float, nullable=True) + max_tokens = Column(Integer, nullable=True) + request_model = Column(String(255), nullable=True) + configuration_snapshot = Column(JSON, nullable=True) + + dataset = relationship("EvalDataset", back_populates="runs") + results = relationship( + "EvalRunResult", + back_populates="run", + cascade="all, delete-orphan", + ) + + +class EvalRunResult(Base): + __tablename__ = "eval_run_results" + + id = Column(Integer, primary_key=True, index=True) + run_id = Column(String(36), ForeignKey("eval_runs.id"), nullable=False) + entry_id = Column(Integer, ForeignKey("eval_dataset_entries.id"), nullable=False) + actual_output = Column(Text, nullable=True) + metric_scores = Column(JSON, nullable=True) # {metric_name: {score, passed, reason}} + passed = Column(Boolean, nullable=True) + reason = Column(Text, nullable=True) + created_at = Column(DateTime, default=datetime.utcnow) + + run = relationship("EvalRun", back_populates="results") + entry = relationship("EvalDatasetEntry", back_populates="results") diff --git a/studio-eval/app/main.py b/studio-eval/app/main.py new file mode 100644 index 0000000..4803cc4 --- /dev/null +++ b/studio-eval/app/main.py @@ -0,0 +1,76 @@ +import logging +import time + +from fastapi import FastAPI +from sqlalchemy import inspect, text +from sqlalchemy.exc import OperationalError + +from app.db.database import Base, engine +from app.routers.eval_router import router as eval_router + +logging.basicConfig( + level=logging.INFO, + format="%(asctime)s %(levelname)s %(name)s — %(message)s", +) +logger = logging.getLogger(__name__) + + +def _migrate_eval_runs_table() -> None: + inspector = inspect(engine) + if "eval_runs" not in inspector.get_table_names(): + return + + columns = inspector.get_columns("eval_runs") + id_col = next((c for c in columns if c["name"] == "id"), None) + if id_col and "int" in str(id_col["type"]).lower(): + logger.info("Detected schema change: migrating eval_runs table from Integer to UUID id…") + Base.metadata.drop_all(bind=engine) + logger.info("Dropped old tables") + return + + column_names = {column["name"] for column in columns} + if "configuration_snapshot" not in column_names: + logger.info("Adding missing configuration_snapshot column to eval_runs…") + with engine.begin() as connection: + connection.execute( + text("ALTER TABLE eval_runs ADD COLUMN configuration_snapshot JSON NULL") + ) + +app = FastAPI( + title="Studio Eval Service", + description=( + "Evaluation service for GenAI Studio: golden dataset management, " + "DeepEval-based metric evaluation, and Ollama model management." + ), + version="1.0.0", +) + + +@app.on_event("startup") +async def startup() -> None: + logger.info("Creating database tables (if not present)…") + # Import schema module so that all ORM models register with Base.metadata + from app.db import schema # noqa: F401 + + retries = 10 + delay = 3 + for attempt in range(1, retries + 1): + try: + _migrate_eval_runs_table() + Base.metadata.create_all(bind=engine) + logger.info("Database tables ready") + return + except OperationalError as e: + if attempt == retries: + logger.error("Could not connect to MySQL after %d attempts: %s", retries, e) + raise + logger.warning("MySQL not ready (attempt %d/%d), retrying in %ds…", attempt, retries, delay) + time.sleep(delay) + + +app.include_router(eval_router) + + +@app.get("/health", tags=["health"]) +def health_check(): + return {"status": "ok"} diff --git a/studio-eval/app/models/__init__.py b/studio-eval/app/models/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/studio-eval/app/models/eval_models.py b/studio-eval/app/models/eval_models.py new file mode 100644 index 0000000..71d32e0 --- /dev/null +++ b/studio-eval/app/models/eval_models.py @@ -0,0 +1,215 @@ +from datetime import datetime +from typing import Any, Dict, List, Optional, Set + +from pydantic import BaseModel, validator + +# --------------------------------------------------------------------------- +# Supported DeepEval metrics +# --------------------------------------------------------------------------- + +SUPPORTED_METRICS: Set[str] = { + "AnswerRelevancy", + "Faithfulness", + "Hallucination", +} + +# --------------------------------------------------------------------------- +# Dataset Entry +# --------------------------------------------------------------------------- + + +class EvalDatasetEntryCreate(BaseModel): + input: str + expected_output: Optional[str] = None + context: Optional[List[str]] = None + + +class EvalDatasetEntryResponse(BaseModel): + id: int + dataset_id: int + input: str + expected_output: Optional[str] = None + context: Optional[List[str]] = None + created_at: datetime + + class Config: + orm_mode = True + + +# --------------------------------------------------------------------------- +# Dataset +# --------------------------------------------------------------------------- + + +class EvalDatasetCreate(BaseModel): + name: str + description: Optional[str] = None + entries: Optional[List[EvalDatasetEntryCreate]] = [] + + +class EvalDatasetResponse(BaseModel): + id: int + name: str + description: Optional[str] = None + created_at: datetime + updated_at: datetime + entries: List[EvalDatasetEntryResponse] = [] + + class Config: + orm_mode = True + + +class EvalDatasetSummaryResponse(BaseModel): + id: int + name: str + description: Optional[str] = None + created_at: datetime + updated_at: datetime + entry_count: int = 0 + + class Config: + orm_mode = True + + +# --------------------------------------------------------------------------- +# Synthesis request +# --------------------------------------------------------------------------- + + +class SynthesizeRequest(BaseModel): + name: str + description: Optional[str] = None + contexts: List[List[str]] + model_name: str + num_goldens_per_context: int = 2 + + @validator("contexts") + def contexts_not_empty(cls, v): + if not v: + raise ValueError("contexts must not be empty") + return v + + @validator("num_goldens_per_context") + def goldens_positive(cls, v): + if v < 1: + raise ValueError("num_goldens_per_context must be >= 1") + return v + + +# --------------------------------------------------------------------------- +# Eval Run +# --------------------------------------------------------------------------- + + +class EvalRunCreate(BaseModel): + name: str + dataset_id: int + sandbox_id: str + model_name: str + metrics: List[str] + configuration_snapshot: Optional[Dict[str, Any]] = None + # Optional app-backend request parameters. + # Context for Faithfulness / AnswerRelevancy is always taken from the + # dataset entries so that RAG evaluation works even when the backend + # does not return retrieval context in its response. + system_prompt: Optional[str] = "You are a helpful assistant" + temperature: Optional[float] = 0.4 + max_tokens: Optional[int] = 100 + request_model: Optional[str] = "NA" + + @validator("metrics") + def validate_metrics(cls, v): + if not v: + raise ValueError("At least one metric must be specified") + invalid = set(v) - SUPPORTED_METRICS + if invalid: + raise ValueError( + f"Unsupported metrics: {sorted(invalid)}. " + f"Supported: {sorted(SUPPORTED_METRICS)}" + ) + return v + + +class MetricScoreDetail(BaseModel): + score: Optional[float] = None + passed: Optional[bool] = None + reason: Optional[str] = None + + +class EvalRunResultResponse(BaseModel): + id: int + run_id: str + entry_id: int + actual_output: Optional[str] = None + metric_scores: Optional[Dict[str, Any]] = None + passed: Optional[bool] = None + reason: Optional[str] = None + created_at: datetime + entry: Optional[EvalDatasetEntryResponse] = None + + class Config: + orm_mode = True + + +class EvalRunSummaryResponse(BaseModel): + id: str + name: str + dataset_id: int + sandbox_id: str + model_name: str + metrics: List[str] + status: str + created_at: datetime + completed_at: Optional[datetime] = None + error: Optional[str] = None + system_prompt: Optional[str] = None + temperature: Optional[float] = None + max_tokens: Optional[int] = None + request_model: Optional[str] = None + configuration_snapshot: Optional[Dict[str, Any]] = None + + class Config: + orm_mode = True + + +class EvalRunResponse(BaseModel): + id: str + name: str + dataset_id: int + sandbox_id: str + model_name: str + metrics: List[str] + status: str + created_at: datetime + completed_at: Optional[datetime] = None + error: Optional[str] = None + system_prompt: Optional[str] = None + temperature: Optional[float] = None + max_tokens: Optional[int] = None + request_model: Optional[str] = None + configuration_snapshot: Optional[Dict[str, Any]] = None + results: List[EvalRunResultResponse] = [] + + class Config: + orm_mode = True + + +# --------------------------------------------------------------------------- +# Ollama model management +# --------------------------------------------------------------------------- + + +class OllamaModelInfo(BaseModel): + name: str + size: Optional[int] = None + digest: Optional[str] = None + + +class ModelPullRequest(BaseModel): + model_name: str + + @validator("model_name") + def model_name_not_empty(cls, v): + if not v.strip(): + raise ValueError("model_name must not be empty") + return v.strip() diff --git a/studio-eval/app/routers/__init__.py b/studio-eval/app/routers/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/studio-eval/app/routers/eval_router.py b/studio-eval/app/routers/eval_router.py new file mode 100644 index 0000000..e248994 --- /dev/null +++ b/studio-eval/app/routers/eval_router.py @@ -0,0 +1,145 @@ +import logging +from typing import List + +from fastapi import APIRouter, BackgroundTasks, Depends, HTTPException +from sqlalchemy.orm import Session + +from app.db.database import get_db +from app.models.eval_models import ( + EvalDatasetCreate, + EvalDatasetResponse, + EvalDatasetSummaryResponse, + EvalRunCreate, + EvalRunResponse, + EvalRunSummaryResponse, + ModelPullRequest, + OllamaModelInfo, + SynthesizeRequest, +) +from app.services import dataset_service, eval_service +from app.services.ollama_service import ensure_model, list_models + +logger = logging.getLogger(__name__) + +router = APIRouter(prefix="/eval", tags=["eval"]) + + +# --------------------------------------------------------------------------- +# Eval runs +# --------------------------------------------------------------------------- + + +@router.post("/runs", response_model=EvalRunSummaryResponse, status_code=202) +async def create_eval_run( + payload: EvalRunCreate, + background_tasks: BackgroundTasks, + db: Session = Depends(get_db), +): + """Kick off an eval run in the background and return immediately.""" + run = eval_service.create_eval_run(db, payload) + background_tasks.add_task(eval_service._run_eval_job, run.id) + return run + + +@router.get("/runs", response_model=List[EvalRunSummaryResponse]) +def list_eval_runs(db: Session = Depends(get_db)): + return eval_service.list_runs(db) + + +@router.get("/runs/{run_id}", response_model=EvalRunResponse) +def get_eval_run(run_id: str, db: Session = Depends(get_db)): + run = eval_service.get_run(db, run_id) + if not run: + raise HTTPException(status_code=404, detail=f"Eval run {run_id} not found") + return run + + +@router.delete("/runs/{run_id}", status_code=204) +def delete_eval_run(run_id: str, db: Session = Depends(get_db)): + if not eval_service.delete_run(db, run_id): + raise HTTPException(status_code=404, detail=f"Eval run {run_id} not found") + + +# --------------------------------------------------------------------------- +# Datasets +# Note: /datasets/synthesize MUST be registered before /datasets/{dataset_id} +# so FastAPI does not treat "synthesize" as an integer dataset_id. +# --------------------------------------------------------------------------- + + +@router.post("/datasets", response_model=EvalDatasetResponse, status_code=201) +def create_dataset(payload: EvalDatasetCreate, db: Session = Depends(get_db)): + return dataset_service.create_dataset(db, payload) + + +@router.post("/datasets/synthesize", response_model=EvalDatasetResponse, status_code=201) +async def synthesize_dataset( + payload: SynthesizeRequest, + db: Session = Depends(get_db), +): + """Generate a golden dataset with the DeepEval Synthesizer (blocking).""" + try: + return await dataset_service.synthesize_dataset(db, payload) + except Exception as exc: + logger.error("Dataset synthesis failed: %s", exc) + raise HTTPException(status_code=500, detail=str(exc)) + + +@router.get("/datasets", response_model=List[EvalDatasetSummaryResponse]) +def list_datasets(db: Session = Depends(get_db)): + datasets = dataset_service.list_datasets(db) + return [ + EvalDatasetSummaryResponse( + id=ds.id, + name=ds.name, + description=ds.description, + created_at=ds.created_at, + updated_at=ds.updated_at, + entry_count=len(ds.entries), + ) + for ds in datasets + ] + + +@router.get("/datasets/{dataset_id}", response_model=EvalDatasetResponse) +def get_dataset(dataset_id: int, db: Session = Depends(get_db)): + dataset = dataset_service.get_dataset(db, dataset_id) + if not dataset: + raise HTTPException(status_code=404, detail=f"Dataset {dataset_id} not found") + return dataset + + +@router.delete("/datasets/{dataset_id}", status_code=204) +def delete_dataset(dataset_id: int, db: Session = Depends(get_db)): + if not dataset_service.delete_dataset(db, dataset_id): + raise HTTPException(status_code=404, detail=f"Dataset {dataset_id} not found") + + +# --------------------------------------------------------------------------- +# Ollama model management +# --------------------------------------------------------------------------- + + +@router.get("/models", response_model=List[OllamaModelInfo]) +async def list_ollama_models(): + """List all models currently available in Ollama.""" + try: + models = await list_models() + return [ + OllamaModelInfo( + name=m.get("name", ""), + size=m.get("size"), + digest=m.get("digest"), + ) + for m in models + ] + except Exception as exc: + logger.error("Failed to reach Ollama: %s", exc) + raise HTTPException(status_code=502, detail=f"Failed to reach Ollama: {exc}") + + +@router.post("/models/pull", status_code=202) +async def pull_model(payload: ModelPullRequest, background_tasks: BackgroundTasks): + """Trigger an async pull of an Ollama model.""" + background_tasks.add_task(ensure_model, payload.model_name) + return {"message": f"Pulling model '{payload.model_name}' in the background"} diff --git a/studio-eval/app/services/__init__.py b/studio-eval/app/services/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/studio-eval/app/services/dataset_service.py b/studio-eval/app/services/dataset_service.py new file mode 100644 index 0000000..61345f5 --- /dev/null +++ b/studio-eval/app/services/dataset_service.py @@ -0,0 +1,163 @@ +import asyncio +import json +import logging +from typing import List, Optional + +import pandas as pd +from sqlalchemy.orm import Session + +from app.db.schema import EvalDataset, EvalDatasetEntry +from app.models.eval_models import EvalDatasetCreate, SynthesizeRequest +from app.services.ollama_service import OllamaJudge, ensure_model + +logger = logging.getLogger(__name__) + + +def _normalize_context(context) -> Optional[List[str]]: + if context is None: + return None + + if isinstance(context, str): + stripped = context.strip() + if not stripped: + return None + try: + loaded = json.loads(stripped) + except json.JSONDecodeError: + return [stripped] + return _normalize_context(loaded) + + if isinstance(context, (list, tuple, set)): + normalized = [] + for item in context: + if item is None: + continue + if isinstance(item, str): + stripped = item.strip() + if stripped: + normalized.append(stripped) + else: + normalized.append(str(item)) + return normalized or None + + return [str(context)] + + +# --------------------------------------------------------------------------- +# CRUD helpers +# --------------------------------------------------------------------------- + + +def create_dataset(db: Session, payload: EvalDatasetCreate) -> EvalDataset: + dataset = EvalDataset(name=payload.name, description=payload.description) + db.add(dataset) + db.flush() # obtain the generated id before inserting entries + + for entry_data in payload.entries or []: + db.add( + EvalDatasetEntry( + dataset_id=dataset.id, + input=entry_data.input, + expected_output=entry_data.expected_output, + context=_normalize_context(entry_data.context), + ) + ) + + db.commit() + db.refresh(dataset) + return dataset + + +def get_dataset(db: Session, dataset_id: int) -> Optional[EvalDataset]: + return db.query(EvalDataset).filter(EvalDataset.id == dataset_id).first() + + +def list_datasets(db: Session) -> List[EvalDataset]: + return db.query(EvalDataset).all() + + +def delete_dataset(db: Session, dataset_id: int) -> bool: + dataset = get_dataset(db, dataset_id) + if not dataset: + return False + db.delete(dataset) + db.commit() + return True + + +# --------------------------------------------------------------------------- +# DeepEval Synthesizer → pandas → MySQL +# --------------------------------------------------------------------------- + + +def _run_synthesizer(judge: OllamaJudge, contexts: List[List[str]], max_per_context: int): + """Blocking call to DeepEval Synthesizer — run via asyncio.to_thread.""" + from deepeval.synthesizer import Synthesizer # noqa: PLC0415 + + synthesizer = Synthesizer(model=judge) + return synthesizer.generate_goldens_from_contexts( + contexts=contexts, + max_goldens_per_context=max_per_context, + ) + + +async def synthesize_dataset(db: Session, payload: SynthesizeRequest) -> EvalDataset: + """Generate a golden dataset with DeepEval Synthesizer and persist it. + + Steps: + 1. ensure_model — pull the judge model if absent. + 2. Run Synthesizer in a thread (blocking LLM calls). + 3. Normalise goldens with pandas (drop rows with null input). + 4. Persist EvalDataset + EvalDatasetEntry rows. + """ + await ensure_model(payload.model_name) + judge = OllamaJudge(model_name=payload.model_name) + + logger.info( + "Synthesizing dataset '%s': %d contexts, %d goldens each", + payload.name, + len(payload.contexts), + payload.num_goldens_per_context, + ) + + goldens = await asyncio.to_thread( + _run_synthesizer, + judge, + payload.contexts, + payload.num_goldens_per_context, + ) + + # Normalise with pandas + records = [ + { + "input": getattr(g, "input", None), + "expected_output": getattr(g, "expected_output", None), + "context": getattr(g, "context", None), + } + for g in goldens + ] + + df = pd.DataFrame(records) + df = df.dropna(subset=["input"]).reset_index(drop=True) + + if df.empty: + raise ValueError("Synthesizer returned no valid goldens (all inputs were null)") + + dataset = EvalDataset(name=payload.name, description=payload.description) + db.add(dataset) + db.flush() + + for _, row in df.iterrows(): + db.add( + EvalDatasetEntry( + dataset_id=dataset.id, + input=row["input"], + expected_output=row.get("expected_output"), + context=_normalize_context(row.get("context")), + ) + ) + + db.commit() + db.refresh(dataset) + logger.info("Synthesized dataset '%s' with %d entries", dataset.name, len(df)) + return dataset diff --git a/studio-eval/app/services/eval_service.py b/studio-eval/app/services/eval_service.py new file mode 100644 index 0000000..9f69aaa --- /dev/null +++ b/studio-eval/app/services/eval_service.py @@ -0,0 +1,284 @@ +import asyncio +import json +import logging +from datetime import datetime +from typing import Dict, List, Optional + +from sqlalchemy.orm import Session + +from app.compat import ensure_pydantic_v2_apis +from app.db.database import SessionLocal +from app.db.schema import EvalDatasetEntry, EvalRun, EvalRunResult +from app.models.eval_models import EvalRunCreate +from app.services.ollama_service import OllamaJudge, ensure_model +from app.services.response_collector import ( # noqa: PLC0415 + DEFAULT_SYSTEM_PROMPT, + collect_response, +) + +logger = logging.getLogger(__name__) + +# Lazy import cache — avoids importing deepeval at module load time. +_METRIC_CLASSES: Optional[dict] = None + + +def _normalize_context(context) -> List[str]: + if context is None: + return [] + + if isinstance(context, str): + stripped = context.strip() + if not stripped: + return [] + try: + loaded = json.loads(stripped) + except json.JSONDecodeError: + return [stripped] + return _normalize_context(loaded) + + if isinstance(context, (list, tuple, set)): + normalized: List[str] = [] + for item in context: + if item is None: + continue + if isinstance(item, str): + stripped = item.strip() + if stripped: + normalized.append(stripped) + else: + normalized.append(str(item)) + return normalized + + return [str(context)] + + +def _format_exception(exc: Exception) -> str: + message = str(exc).strip() + return message or exc.__class__.__name__ + + +def _get_metric_classes() -> dict: + global _METRIC_CLASSES + if _METRIC_CLASSES is None: + ensure_pydantic_v2_apis() + from deepeval.metrics import ( # noqa: PLC0415 + AnswerRelevancyMetric, + FaithfulnessMetric, + HallucinationMetric, + ) + + _METRIC_CLASSES = { + "AnswerRelevancy": AnswerRelevancyMetric, + "Faithfulness": FaithfulnessMetric, + "Hallucination": HallucinationMetric, + } + return _METRIC_CLASSES + + +# --------------------------------------------------------------------------- +# CRUD helpers +# --------------------------------------------------------------------------- + + +def create_eval_run(db: Session, payload: EvalRunCreate) -> EvalRun: + run = EvalRun( + name=payload.name, + dataset_id=payload.dataset_id, + sandbox_id=payload.sandbox_id, + model_name=payload.model_name, + metrics=payload.metrics, + status="pending", + system_prompt=payload.system_prompt, + temperature=payload.temperature, + max_tokens=payload.max_tokens, + request_model=payload.request_model, + configuration_snapshot=payload.configuration_snapshot, + ) + db.add(run) + db.commit() + db.refresh(run) + return run + + +def get_run(db: Session, run_id: str) -> Optional[EvalRun]: + from sqlalchemy.orm import joinedload + return ( + db.query(EvalRun) + .filter(EvalRun.id == run_id) + .options(joinedload(EvalRun.results).joinedload(EvalRunResult.entry)) + .first() + ) + + +def list_runs(db: Session) -> List[EvalRun]: + return db.query(EvalRun).all() + + +def delete_run(db: Session, run_id: str) -> bool: + run = get_run(db, run_id) + if not run: + return False + db.delete(run) + db.commit() + return True + + +# --------------------------------------------------------------------------- +# Background job +# --------------------------------------------------------------------------- + + +def _measure_metric(metric, test_case) -> None: + """Thin wrapper so asyncio.to_thread can call metric.measure.""" + metric.measure(test_case) + + +async def _run_eval_job(run_id: str) -> None: # noqa: C901 + """Background task that drives a full eval run. + + Lifecycle: + pending → running → completed | failed + """ + db: Session = SessionLocal() + try: + run: Optional[EvalRun] = ( + db.query(EvalRun).filter(EvalRun.id == run_id).first() + ) + if not run: + logger.error("Eval run %s not found in DB", run_id) + return + + run.status = "running" + db.commit() + + # --- ensure judge model ----------------------------------------------- + await ensure_model(run.model_name) + judge = OllamaJudge(model_name=run.model_name) + + metric_classes = _get_metric_classes() + + # --- load entries ------------------------------------------------------- + entries: List[EvalDatasetEntry] = ( + db.query(EvalDatasetEntry) + .filter(EvalDatasetEntry.dataset_id == run.dataset_id) + .all() + ) + + if not entries: + run.status = "failed" + run.error = "Dataset has no entries" + run.completed_at = datetime.utcnow() + db.commit() + return + + # --- process each entry ------------------------------------------------ + for entry in entries: + actual_output: Optional[str] = None + metric_scores: Dict[str, dict] = {} + all_passed = True + reasons: List[str] = [] + normalized_context = _normalize_context(entry.context) + + try: + actual_output = await collect_response( + sandbox_id=run.sandbox_id, + user_input=entry.input, + system_prompt=run.system_prompt or DEFAULT_SYSTEM_PROMPT, + temperature=run.temperature if run.temperature is not None else 0.4, + max_tokens=run.max_tokens if run.max_tokens is not None else 100, + request_model=run.request_model or "NA", + ) + actual_output = actual_output or "" + + from deepeval.test_case import LLMTestCase # noqa: PLC0415 + + test_case = LLMTestCase( + input=entry.input, + actual_output=actual_output, + expected_output=entry.expected_output or "", + context=normalized_context, + retrieval_context=normalized_context, + ) + + for metric_name in run.metrics: + cls = metric_classes.get(metric_name) + if cls is None: + logger.warning("Unknown metric '%s' — skipping", metric_name) + continue + try: + metric = cls(model=judge, threshold=0.5) + # Run blocking measure() in a thread pool to avoid + # blocking the event loop during LLM generation. + await asyncio.to_thread(_measure_metric, metric, test_case) + + score = getattr(metric, "score", None) + passed = getattr(metric, "success", None) + if passed is None and score is not None: + passed = score >= 0.5 + reason = getattr(metric, "reason", None) + + metric_scores[metric_name] = { + "score": score, + "passed": passed, + "reason": reason, + } + if not passed: + all_passed = False + if reason: + reasons.append(f"{metric_name}: {reason}") + + except Exception as exc: + logger.error( + "Metric '%s' failed for entry %d: %s", + metric_name, + entry.id, + _format_exception(exc), + ) + failure_reason = _format_exception(exc) + metric_scores[metric_name] = { + "score": None, + "passed": False, + "reason": failure_reason, + } + all_passed = False + reasons.append(f"{metric_name}: {failure_reason}") + + except Exception as exc: + logger.error( + "Error processing entry %d: %s", + entry.id, + _format_exception(exc), + ) + all_passed = False + reasons.append(_format_exception(exc)) + + db.add( + EvalRunResult( + run_id=run.id, + entry_id=entry.id, + actual_output=actual_output, + metric_scores=metric_scores or None, + passed=all_passed, + reason="; ".join(reasons) if reasons else None, + ) + ) + db.commit() + + run.status = "completed" + run.completed_at = datetime.utcnow() + db.commit() + logger.info("Eval run %s completed successfully", run_id) + + except Exception as exc: + logger.error("Eval run %s failed with unhandled error: %s", run_id, exc) + try: + run = db.query(EvalRun).filter(EvalRun.id == run_id).first() + if run: + run.status = "failed" + run.error = str(exc) + run.completed_at = datetime.utcnow() + db.commit() + except Exception as inner: + logger.error("Could not persist failed status for run %s: %s", run_id, inner) + finally: + db.close() diff --git a/studio-eval/app/services/ollama_service.py b/studio-eval/app/services/ollama_service.py new file mode 100644 index 0000000..a1fdc77 --- /dev/null +++ b/studio-eval/app/services/ollama_service.py @@ -0,0 +1,199 @@ +import json +import logging +import os +from typing import Any + +import httpx + +logger = logging.getLogger(__name__) + + +def _parse_timeout(env_name: str, default: str): + raw_value = os.getenv(env_name, default) + if raw_value is None: + return None + + normalized = raw_value.strip().lower() + if normalized in {"", "0", "none", "false", "off", "disable", "disabled"}: + return None + + parsed = float(raw_value) + return None if parsed <= 0 else parsed + +OLLAMA_DNS = os.getenv("OLLAMA_DNS", "ollama.ollama.svc.cluster.local:11434") +OLLAMA_BASE_URL = f"http://{OLLAMA_DNS}" +OLLAMA_PULL_TIMEOUT = _parse_timeout("OLLAMA_PULL_TIMEOUT", "600") +OLLAMA_GENERATE_TIMEOUT = _parse_timeout("OLLAMA_GENERATE_TIMEOUT", "0") + + +def _schema_to_ollama_format(schema) -> Any: + return "json" + + +def _load_structured_response(raw_response: str) -> Any: + content = raw_response.strip() + + if content.startswith("```"): + lines = content.splitlines() + if lines and lines[0].startswith("```"): + lines = lines[1:] + if lines and lines[-1].strip().startswith("```"): + lines = lines[:-1] + content = "\n".join(lines).strip() + + try: + return json.loads(content) + except json.JSONDecodeError: + json_start_candidates = [ + index for index in (content.find("{"), content.find("[")) if index != -1 + ] + json_end_candidates = [ + index for index in (content.rfind("}"), content.rfind("]")) if index != -1 + ] + if not json_start_candidates or not json_end_candidates: + raise + + json_start = min(json_start_candidates) + json_end = max(json_end_candidates) + 1 + return json.loads(content[json_start:json_end]) + + +def _parse_structured_response(schema, raw_response: str) -> Any: + payload = _load_structured_response(raw_response) + + if hasattr(schema, "model_validate"): + return schema.model_validate(payload) + if hasattr(schema, "parse_obj"): + return schema.parse_obj(payload) + if callable(schema): + if isinstance(payload, dict): + return schema(**payload) + return schema(payload) + return payload + + +# --------------------------------------------------------------------------- +# Async helpers — used by router and background jobs +# --------------------------------------------------------------------------- + + +async def list_models() -> list: + """Return the list of locally available Ollama models (GET /api/tags).""" + async with httpx.AsyncClient(timeout=30.0) as client: + resp = await client.get(f"{OLLAMA_BASE_URL}/api/tags") + resp.raise_for_status() + return resp.json().get("models", []) + + +async def is_model_available(model_name: str) -> bool: + """True if *model_name* is already present in Ollama.""" + try: + models = await list_models() + target_base = model_name.split(":")[0] + for m in models: + name = m.get("name", "") + if name == model_name or name.split(":")[0] == target_base: + return True + return False + except Exception: + return False + + +async def pull_model(model_name: str) -> None: + """Pull *model_name* from the Ollama registry; blocks until complete.""" + logger.info("Pulling Ollama model: %s", model_name) + async with httpx.AsyncClient(timeout=OLLAMA_PULL_TIMEOUT) as client: + resp = await client.post( + f"{OLLAMA_BASE_URL}/api/pull", + json={"name": model_name, "stream": False}, + ) + resp.raise_for_status() + logger.info("Model pulled successfully: %s", model_name) + + +async def ensure_model(model_name: str) -> None: + """Ensure *model_name* is available in Ollama, pulling it if absent.""" + if not await is_model_available(model_name): + logger.info("Model '%s' not found locally — pulling...", model_name) + await pull_model(model_name) + else: + logger.debug("Model '%s' already available", model_name) + + +# --------------------------------------------------------------------------- +# OllamaJudge — DeepEvalBaseLLM wrapper +# --------------------------------------------------------------------------- + +try: + from deepeval.models import DeepEvalBaseLLM # deepeval >= 0.21 +except ImportError: + from deepeval.models.base_model import DeepEvalBaseLLM # older deepeval + + +class OllamaJudge(DeepEvalBaseLLM): + """Custom DeepEval LLM that routes all generation requests to Ollama. + + Both synchronous (``generate``) and asynchronous (``a_generate``) paths + are implemented so that DeepEval can pick the appropriate one depending on + whether ``metric.measure()`` or ``metric.a_measure()`` is called. + + When *schema* is provided (structured-output request), the Ollama API is + asked to return JSON via ``"format": "json"``. + """ + + def __init__(self, model_name: str) -> None: + self._model_name = model_name + self._base_url = OLLAMA_BASE_URL + + # -- DeepEvalBaseLLM contract ------------------------------------------- + + def get_model_name(self) -> str: + return self._model_name + + def load_model(self): + # Model lives in Ollama; nothing to load locally. + return self + + def generate(self, prompt: str, schema=None) -> Any: + """Synchronous generation — used by ``metric.measure()``.""" + payload = { + "model": self._model_name, + "prompt": prompt, + "stream": False, + } + if schema is not None: + payload["format"] = _schema_to_ollama_format(schema) + + with httpx.Client(timeout=OLLAMA_GENERATE_TIMEOUT) as client: + resp = client.post(f"{self._base_url}/api/generate", json=payload) + resp.raise_for_status() + response_text = resp.json()["response"] + if schema is None: + return response_text + try: + return _parse_structured_response(schema, response_text) + except Exception as exc: + raise TypeError("Structured response parsing failed") from exc + + async def a_generate(self, prompt: str, schema=None) -> Any: + """Async generation — used by ``metric.a_measure()``.""" + payload = { + "model": self._model_name, + "prompt": prompt, + "stream": False, + } + if schema is not None: + payload["format"] = _schema_to_ollama_format(schema) + + async with httpx.AsyncClient(timeout=OLLAMA_GENERATE_TIMEOUT) as client: + resp = await client.post( + f"{self._base_url}/api/generate", json=payload + ) + resp.raise_for_status() + response_text = resp.json()["response"] + if schema is None: + return response_text + try: + return _parse_structured_response(schema, response_text) + except Exception as exc: + raise TypeError("Structured response parsing failed") from exc diff --git a/studio-eval/app/services/response_collector.py b/studio-eval/app/services/response_collector.py new file mode 100644 index 0000000..3fd5d4c --- /dev/null +++ b/studio-eval/app/services/response_collector.py @@ -0,0 +1,155 @@ +import json +import logging +import os +from typing import Optional + +import httpx + +logger = logging.getLogger(__name__) + + +def _parse_timeout(env_name: str, default: str): + raw_value = os.getenv(env_name, default) + if raw_value is None: + return None + + normalized = raw_value.strip().lower() + if normalized in {"", "0", "none", "false", "off", "disable", "disabled"}: + return None + + parsed = float(raw_value) + return None if parsed <= 0 else parsed + +_APP_BACKEND_PORT = 8899 +_CHAT_PATH = "/v1/chat/completions" +_TIMEOUT = _parse_timeout("APP_BACKEND_TIMEOUT", "0") +_APP_BACKEND_URL_TEMPLATE = os.getenv( + "APP_BACKEND_URL_TEMPLATE", + f"http://app-backend.{{sandbox_id}}.svc.cluster.local:{_APP_BACKEND_PORT}{_CHAT_PATH}", +) + +DEFAULT_SYSTEM_PROMPT = "You are a helpful assistant" + + +def _app_backend_url(sandbox_id: str) -> str: + return _APP_BACKEND_URL_TEMPLATE.format(sandbox_id=sandbox_id) + + +# --------------------------------------------------------------------------- +# SSE parsing helpers +# --------------------------------------------------------------------------- + +def _parse_sse_token(payload: str) -> Optional[str]: + """Extract the text token from a single SSE data payload.""" + if payload.strip() == "[DONE]": + return None + try: + chunk = json.loads(payload) + except json.JSONDecodeError: + return payload if payload else None + if not isinstance(chunk, dict): + return None + choices = chunk.get("choices", []) + if not choices: + return None + delta = choices[0].get("delta", {}) + return delta.get("content") or choices[0].get("text", "") or None + + +def _extract_sse_answer(body: str) -> Optional[str]: + """Parse a full SSE response body and concatenate all content tokens.""" + parts = [] + for raw_line in body.splitlines(): + line = raw_line.rstrip("\r") + if not line or not line.startswith("data:"): + continue + payload = line[6:] # skip "data: " (preserve leading space in token) + if payload.strip() == "[DONE]": + break + token = _parse_sse_token(payload) + if token is not None: + parts.append(token) + return "".join(parts).strip() if parts else None + + +def _parse_non_streaming(data: dict) -> Optional[str]: + """Extract the assistant reply from a non-streaming JSON response.""" + choices = data.get("choices") or [] + if choices: + choice = choices[0] + content = choice.get("message", {}).get("content") or choice.get("text") + if content: + return content + for key in ("answer", "response", "content", "actual_output"): + if key in data and isinstance(data[key], str) and data[key].strip(): + return data[key] + return None + + +# --------------------------------------------------------------------------- +# Public API +# --------------------------------------------------------------------------- + +async def collect_response( + sandbox_id: str, + user_input: str, + system_prompt: str = DEFAULT_SYSTEM_PROMPT, + temperature: float = 0.4, + max_tokens: int = 100, + request_model: str = "NA", +) -> Optional[str]: + """POST a chat message to the app-backend in *sandbox_id* and return the + assistant reply, or ``None`` if the response contains no usable content. + + Supports both streaming SSE (``text/event-stream``) and plain JSON + non-streaming responses. Raises ``httpx.HTTPError`` on non-2xx status. + + Parameters + ---------- + sandbox_id: Target sandbox namespace. + user_input: The user message to send. + system_prompt: System message prepended to the chat messages list. + temperature: Sampling temperature forwarded to the model. + max_tokens: Maximum number of tokens to generate. + request_model: Model identifier forwarded in the request body. + """ + url = _app_backend_url(sandbox_id) + + messages = [] + if system_prompt.strip(): + messages.append({"role": "system", "content": system_prompt}) + messages.append({"role": "user", "content": user_input}) + + payload = { + "messages": messages, + "model": request_model, + "temperature": temperature, + "max_tokens": max_tokens, + "stream": True, + } + + logger.debug("Collecting response: sandbox=%s input=%.80s", sandbox_id, user_input) + + async with httpx.AsyncClient(timeout=_TIMEOUT) as client: + async with client.stream("POST", url, json=payload) as resp: + resp.raise_for_status() + body = (await resp.aread()).decode("utf-8") + content_type = resp.headers.get("content-type", "") + + if "text/event-stream" in content_type or body.lstrip().startswith("data:"): + answer = _extract_sse_answer(body) + if answer: + return answer + logger.warning("SSE parsing returned empty answer for sandbox=%s", sandbox_id) + return None + + try: + data = json.loads(body) + answer = _parse_non_streaming(data) + if answer: + return answer + logger.warning("app-backend returned no usable content for sandbox=%s", sandbox_id) + return None + except json.JSONDecodeError: + # Last resort: try SSE parse on a non-event-stream content-type body + return _extract_sse_answer(body) diff --git a/studio-eval/requirements.txt b/studio-eval/requirements.txt new file mode 100644 index 0000000..74006c1 --- /dev/null +++ b/studio-eval/requirements.txt @@ -0,0 +1,9 @@ +fastapi>=0.100.0,<0.200.0 +uvicorn[standard]>=0.23.0 +deepeval +pandas>=2.0.0 +sqlalchemy>=2.0.0 +pymysql>=1.1.0 +httpx>=0.24.0 +pydantic==1.10.13 +cryptography>=41.0.0 diff --git a/studio-eval/tests/test_eval_service.py b/studio-eval/tests/test_eval_service.py new file mode 100644 index 0000000..5e26c8f --- /dev/null +++ b/studio-eval/tests/test_eval_service.py @@ -0,0 +1,15 @@ +from app.services import eval_service + + +def test_normalize_context_handles_json_string_list(): + context = '["alpha", "beta"]' + + assert eval_service._normalize_context(context) == ["alpha", "beta"] + + +def test_normalize_context_handles_plain_string(): + assert eval_service._normalize_context("single context") == ["single context"] + + +def test_format_exception_uses_class_name_when_message_empty(): + assert eval_service._format_exception(AssertionError()) == "AssertionError" \ No newline at end of file diff --git a/studio-eval/tests/test_ollama_service.py b/studio-eval/tests/test_ollama_service.py new file mode 100644 index 0000000..2538f18 --- /dev/null +++ b/studio-eval/tests/test_ollama_service.py @@ -0,0 +1,118 @@ +import importlib +import sys +from types import ModuleType + +from pydantic import BaseModel + + +def _load_ollama_service(monkeypatch): + deepeval_module = ModuleType("deepeval") + deepeval_models_module = ModuleType("deepeval.models") + deepeval_base_model_module = ModuleType("deepeval.models.base_model") + + class DeepEvalBaseLLM: + pass + + deepeval_models_module.DeepEvalBaseLLM = DeepEvalBaseLLM + deepeval_base_model_module.DeepEvalBaseLLM = DeepEvalBaseLLM + + monkeypatch.setitem(sys.modules, "deepeval", deepeval_module) + monkeypatch.setitem(sys.modules, "deepeval.models", deepeval_models_module) + monkeypatch.setitem( + sys.modules, + "deepeval.models.base_model", + deepeval_base_model_module, + ) + sys.modules.pop("app.services.ollama_service", None) + return importlib.import_module("app.services.ollama_service") + + +class ClaimsSchema(BaseModel): + claims: list[str] + + +class StatementsSchema(BaseModel): + statements: list[str] + + +def test_generate_returns_schema_instance(monkeypatch): + ollama_service = _load_ollama_service(monkeypatch) + captured = {} + + class FakeResponse: + def raise_for_status(self): + return None + + def json(self): + return {"response": '{"claims": ["alpha", "beta"]}'} + + class FakeClient: + def __init__(self, *args, **kwargs): + pass + + def __enter__(self): + return self + + def __exit__(self, exc_type, exc, tb): + return False + + def post(self, url, json): + captured["url"] = url + captured["json"] = json + return FakeResponse() + + monkeypatch.setattr(ollama_service.httpx, "Client", FakeClient) + + judge = ollama_service.OllamaJudge(model_name="judge-model") + result = judge.generate("prompt", schema=ClaimsSchema) + + assert isinstance(result, ClaimsSchema) + assert result.claims == ["alpha", "beta"] + assert captured["json"]["format"] == "json" + + +def test_parse_structured_response_handles_fenced_json(monkeypatch): + ollama_service = _load_ollama_service(monkeypatch) + + result = ollama_service._parse_structured_response( + StatementsSchema, + '```json\n{"statements": ["first statement"]}\n```', + ) + + assert isinstance(result, StatementsSchema) + assert result.statements == ["first statement"] + + +def test_generate_raises_type_error_on_invalid_structured_payload(monkeypatch): + ollama_service = _load_ollama_service(monkeypatch) + + class FakeResponse: + def raise_for_status(self): + return None + + def json(self): + return {"response": 'not-json'} + + class FakeClient: + def __init__(self, *args, **kwargs): + pass + + def __enter__(self): + return self + + def __exit__(self, exc_type, exc, tb): + return False + + def post(self, url, json): + return FakeResponse() + + monkeypatch.setattr(ollama_service.httpx, "Client", FakeClient) + + judge = ollama_service.OllamaJudge(model_name="judge-model") + + try: + judge.generate("prompt", schema=ClaimsSchema) + except TypeError as exc: + assert str(exc) == "Structured response parsing failed" + else: + raise AssertionError("Expected TypeError for invalid structured payload") \ No newline at end of file diff --git a/studio-eval/tests/test_timeout_config.py b/studio-eval/tests/test_timeout_config.py new file mode 100644 index 0000000..9466403 --- /dev/null +++ b/studio-eval/tests/test_timeout_config.py @@ -0,0 +1,42 @@ +import importlib +import os +import sys + + +def _reload_module(module_name: str): + sys.modules.pop(module_name, None) + return importlib.import_module(module_name) + + +def test_response_collector_timeout_disabled_by_default(monkeypatch): + monkeypatch.delenv("APP_BACKEND_TIMEOUT", raising=False) + + response_collector = _reload_module("app.services.response_collector") + + assert response_collector._TIMEOUT is None + + +def test_response_collector_timeout_can_be_enabled(monkeypatch): + monkeypatch.setenv("APP_BACKEND_TIMEOUT", "300") + + response_collector = _reload_module("app.services.response_collector") + + assert response_collector._TIMEOUT == 300.0 + + +def test_ollama_generate_timeout_disabled_by_default(monkeypatch): + monkeypatch.delenv("OLLAMA_GENERATE_TIMEOUT", raising=False) + monkeypatch.setenv("OLLAMA_PULL_TIMEOUT", "600") + + ollama_service = _reload_module("app.services.ollama_service") + + assert ollama_service.OLLAMA_GENERATE_TIMEOUT is None + + +def test_ollama_generate_timeout_can_be_enabled(monkeypatch): + monkeypatch.setenv("OLLAMA_GENERATE_TIMEOUT", "240") + monkeypatch.setenv("OLLAMA_PULL_TIMEOUT", "600") + + ollama_service = _reload_module("app.services.ollama_service") + + assert ollama_service.OLLAMA_GENERATE_TIMEOUT == 240.0 \ No newline at end of file diff --git a/studio-frontend/.env.development b/studio-frontend/.env.development deleted file mode 100644 index 0a6398e..0000000 --- a/studio-frontend/.env.development +++ /dev/null @@ -1,6 +0,0 @@ -NODE_TLS_REJECT_UNAUTHORIZED=0 -VITE_DISABLE_KEYCLOAK=true -NODE_ENV=development -VITE_HOST=0.0.0.0 -VITE_PORT=8088 -FINETUNING_HOST= # Command to get your host ip: ip route get 1.1.1.1 | awk '{print $7}' \ No newline at end of file diff --git a/studio-frontend/packages/server/nodemon.json b/studio-frontend/packages/server/nodemon.json index 6897d08..4d162dc 100644 --- a/studio-frontend/packages/server/nodemon.json +++ b/studio-frontend/packages/server/nodemon.json @@ -1,6 +1,7 @@ { "ignore": ["**/*.spec.ts", ".git", "node_modules"], "watch": ["src"], - "exec": "ts-node --transpile-only src/index.ts", - "ext": "ts" + "exec": "pnpm build && pnpm start", + "ext": "ts", + "legacyWatch": true } diff --git a/studio-frontend/packages/server/src/routes/evaluation/index.ts b/studio-frontend/packages/server/src/routes/evaluation/index.ts index 2fbbfc4..e819626 100644 --- a/studio-frontend/packages/server/src/routes/evaluation/index.ts +++ b/studio-frontend/packages/server/src/routes/evaluation/index.ts @@ -4,7 +4,22 @@ import { Request, Response, NextFunction } from 'express' const router: express.Router = express.Router() -const STUDIO_SERVER_URL = process.env.STUDIO_SERVER_URL || 'http://studio-backend.studio.svc.cluster.local:5000' +const getStudioServerUrl = () => process.env.STUDIO_SERVER_URL || 'http://studio-backend.studio.svc.cluster.local:5000' + +// URL template for fetching dataprep file list from a sandbox. +// Builds the dataprep GET URL for a given sandboxId. +// Uses PREPARE_DOC_REDIS_PREP_DNS env var (also used by studio-nginx for K8s proxying). +const getDataprepUrl = (sandboxId: string): string => { + const dns = + process.env.PREPARE_DOC_REDIS_PREP_DNS || + 'opea-prepare-doc-redis-prep-0.$namespace.svc.cluster.local:6007' + if (dns.includes('$namespace')) { + // K8s direct: replace $namespace placeholder, prepend scheme, append path + return `http://${dns.replace('$namespace', sandboxId)}/v1/dataprep/get` + } + // Proxy mode (local-nginx): DNS is a host only — pass sandbox via ?ns= query param + return `http://${dns}/v1/dataprep/get?ns=${sandboxId}` +} /** * Generic proxy helper – forwards a request to studio-backend and pipes the response back. @@ -17,7 +32,7 @@ const proxy = async ( next: NextFunction ) => { try { - const url = `${STUDIO_SERVER_URL}/${targetPath}` + const url = `${getStudioServerUrl()}/${targetPath}` const response = await axios({ method, url, @@ -53,6 +68,56 @@ router.post('/models/pull', (req: Request, res: Response, next: NextFunction) => proxy('post', 'studio-backend/evaluation/models/pull', req, res, next) ) +// POST /api/v1/evaluation/sandbox-files +// Body: { sandbox_id: string } +// Uses the same path as sandbox app-frontend: app-nginx -> /v1/dataprep/get +router.post('/sandbox-files', async (req: Request, res: Response, next: NextFunction) => { + try { + const sandboxId = String(req.body?.sandbox_id || '') + + if (!sandboxId) { + return res.status(400).json({ message: 'sandbox_id is required' }) + } + + const isValidNamespace = /^[a-z0-9-]+$/.test(sandboxId) + if (!isValidNamespace) { + return res.status(400).json({ message: 'Invalid sandbox_id' }) + } + + const url = getDataprepUrl(sandboxId) + console.log(`[sandbox-files] Calling: POST ${url}`) + let response: any + try { + response = await axios.post( + url, + {}, + { + headers: { 'Content-Type': 'application/json' }, + timeout: 15_000, + proxy: false // bypass corporate proxy; Docker bridge DNS resolves service names + } + ) + } catch (axiosErr: any) { + const status = axiosErr?.response?.status + const msg = axiosErr?.message + const body = axiosErr?.response?.data + console.error(`[sandbox-files] Request failed: status=${status} msg=${msg}`, body ?? '') + if (status) { + return res.status(status).json({ message: `Dataprep service returned ${status}`, detail: body }) + } + return res.status(502).json({ message: `Cannot reach dataprep service: ${msg}`, url }) + } + + const data = response.data + console.log(`[sandbox-files] Response:`, JSON.stringify(data).slice(0, 200)) + const files = Array.isArray(data) ? data : Array.isArray(data?.files) ? data.files : [] + + return res.status(200).json({ files }) + } catch (error: unknown) { + next(error) + } +}) + // ── Datasets ────────────────────────────────────────────────────────────────── router.get('/datasets', (req: Request, res: Response, next: NextFunction) => proxy('get', 'studio-backend/evaluation/datasets', req, res, next) diff --git a/studio-frontend/packages/server/src/services/chatflows/index.ts b/studio-frontend/packages/server/src/services/chatflows/index.ts index a6d5acc..e230089 100644 --- a/studio-frontend/packages/server/src/services/chatflows/index.ts +++ b/studio-frontend/packages/server/src/services/chatflows/index.ts @@ -1,4 +1,6 @@ import { StatusCodes } from 'http-status-codes' +import fs from 'fs/promises' +import path from 'path' import { ChatflowType, IReactFlowObject } from '../../Interface' import { ChatFlow } from '../../database/entities/ChatFlow' import { ChatMessage } from '../../database/entities/ChatMessage' @@ -29,7 +31,32 @@ const getGithubAxiosConfig = (): AxiosRequestConfig => { } } -const STUDIO_SERVER_URL = process.env.STUDIO_SERVER_URL || 'http://studio-backend.studio.svc.cluster.local:5000' +const SAMPLE_WORKFLOWS_DIR = process.env.SAMPLE_WORKFLOWS_DIR || path.resolve(process.cwd(), '..', '..', 'sample-workflows') + +const getStudioServerUrl = () => process.env.STUDIO_SERVER_URL || 'http://studio-backend.studio.svc.cluster.local:5000' + +const loadLocalSampleChatflows = async (userid: string, type?: ChatflowType): Promise[]> => { + const files = (await fs.readdir(SAMPLE_WORKFLOWS_DIR)) + .filter((fileName) => fileName.endsWith('.json')) + .sort() + + const chatflows: Partial[] = [] + + for (const fileName of files) { + const filePath = path.join(SAMPLE_WORKFLOWS_DIR, fileName) + const parsedFlowData = JSON.parse(await fs.readFile(filePath, 'utf8')) + chatflows.push({ + userid, + name: fileName.replace('.json', ''), + flowData: JSON.stringify(parsedFlowData), + type: type || 'OPEA', + deployed: false, + isPublic: false + }) + } + + return chatflows +} const deleteChatflow = async (chatflowId: string): Promise => { try { @@ -111,39 +138,44 @@ const getAllChatflowsbyUserId = async (userid: string, type?: ChatflowType): Pro const importSampleChatflowsbyUserId = async (userid: string, type?: ChatflowType): Promise => { try { const axiosConfig = getGithubAxiosConfig() - - console.log('Importing sample chatflows for user:', userid); - - const response = await axios.get( - 'https://api.github.com/repos/opea-project/GenAIStudio/contents/sample-workflows', - axiosConfig - ); - - console.log('Response from GitHub:', response.data); - - const files = response.data.filter((item: any) => item.type === 'file'); - - const chatflows: Partial[] = []; - for (const file of files) { - const fileResponse = await axios.get(file.download_url, axiosConfig); - const parsedFlowData = fileResponse.data; - const newChatflow: Partial = { - userid: userid, - name: file.name.replace('.json', ''), - flowData: JSON.stringify(parsedFlowData), - type: 'OPEA', - deployed: false, - isPublic: false - }; - chatflows.push(newChatflow); + + console.log('Importing sample chatflows for user:', userid) + + let chatflows: Partial[] = [] + + try { + chatflows = await loadLocalSampleChatflows(userid, type) + logger.info(`[server]: Loaded ${chatflows.length} sample chatflows from local directory ${SAMPLE_WORKFLOWS_DIR}`) + } catch (localError) { + logger.warn(`[server]: Falling back to GitHub sample workflows: ${getErrorMessage(localError)}`) + + const response = await axios.get( + 'https://api.github.com/repos/opea-project/GenAIStudio/contents/sample-workflows', + axiosConfig + ) + + const files = response.data.filter((item: any) => item.type === 'file') + for (const file of files) { + const fileResponse = await axios.get(file.download_url, axiosConfig) + const parsedFlowData = fileResponse.data + chatflows.push({ + userid, + name: file.name.replace('.json', ''), + flowData: JSON.stringify(parsedFlowData), + type: type || 'OPEA', + deployed: false, + isPublic: false + }) + } } - const insertResponse = await importChatflows(chatflows); - return insertResponse; + + const insertResponse = await importChatflows(chatflows) + return insertResponse } catch (error) { throw new InternalFlowiseError( StatusCodes.INTERNAL_SERVER_ERROR, `Error: chatflowsService.importSampleChatflowsbyUserId - ${getErrorMessage(error)}` - ); + ) } } @@ -349,7 +381,7 @@ const deployChatflowSandboxService = async (chatflowId: string) => { console.log('deployChatflowSandboxService', chatflowId) try { const chatflow = await generatePipelineJson(chatflowId) - const studioServerUrl = STUDIO_SERVER_URL + const studioServerUrl = getStudioServerUrl() const deploySandboxEndpoint = 'studio-backend/deploy-sandbox' console.log('chatflow', JSON.stringify(chatflow)) console.log('studioServerUrl', studioServerUrl) @@ -373,7 +405,7 @@ const deployChatflowSandboxService = async (chatflowId: string) => { const stopChatflowSandboxService = async (chatflowId: string) => { console.log('stopChatflowSandboxService', chatflowId) try { - const studioServerUrl = STUDIO_SERVER_URL + const studioServerUrl = getStudioServerUrl() const deleteSandboxEndpoint = 'studio-backend/delete-sandbox' console.log('studioServerUrl', studioServerUrl) console.log('deleteSandboxEndpoint', deleteSandboxEndpoint) @@ -397,7 +429,7 @@ const buildDeploymentPackageService = async (chatflowId: string, deploymentConfi console.log('buildDeploymentPackageService', chatflowId, deploymentConfig) try { const chatflow = await generatePipelineJson(chatflowId) - const studioServerUrl = STUDIO_SERVER_URL + const studioServerUrl = getStudioServerUrl() const buildDeploymentPackageEndpoint = 'studio-backend/download-zip' console.log('chatflow', JSON.stringify(chatflow)) console.log('studioServerUrl', studioServerUrl) @@ -423,7 +455,7 @@ const oneClickDeploymentService = async (chatflowId: string, deploymentConfig: R console.log('oneClickDeploymentService', chatflowId, deploymentConfig) try { const chatflow = await generatePipelineJson(chatflowId) - const studioServerUrl = STUDIO_SERVER_URL + const studioServerUrl = getStudioServerUrl() const endpoint = 'studio-backend/click-deployment' // console.log('chatflow', JSON.stringify(chatflow)) // console.log('studioServerUrl', studioServerUrl) diff --git a/studio-frontend/packages/server/src/services/finetuning/index.ts b/studio-frontend/packages/server/src/services/finetuning/index.ts index da74ff9..c5445a7 100644 --- a/studio-frontend/packages/server/src/services/finetuning/index.ts +++ b/studio-frontend/packages/server/src/services/finetuning/index.ts @@ -19,8 +19,26 @@ declare function clearTimeout(id: any): void const execAsync = promisify(exec) -const FINETUNING_SERVICE_URL = process.env.FINETUNING_HOST ? `http://${process.env.FINETUNING_HOST}:8015` : 'undefined' +const normalizeFinetuningUrl = (rawValue: string | undefined) => { + if (!rawValue || rawValue.trim() === '') return undefined + + const withScheme = /^https?:\/\//i.test(rawValue) ? rawValue : `http://${rawValue}` + const url = new URL(withScheme) + + url.pathname = '' + url.search = '' + url.hash = '' + + return url +} + +const finetuningServiceUrl = normalizeFinetuningUrl(process.env.FINETUNING_SERVICE_DNS) +const finetuningRayUrl = normalizeFinetuningUrl(process.env.FINETUNING_RAY_DNS) + +const FINETUNING_SERVICE_URL = finetuningServiceUrl?.toString().replace(/\/$/, '') || 'undefined' +const FINETUNING_RAY_URL = finetuningRayUrl?.toString().replace(/\/$/, '') || 'undefined' console.debug('finetuningService - FINETUNING_SERVICE_URL', FINETUNING_SERVICE_URL) +console.debug('finetuningService - FINETUNING_RAY_URL', FINETUNING_RAY_URL) // Create an axios client with keep-alive to reduce connection churn const agentOptions = { keepAlive: true, maxSockets: 20 } @@ -865,20 +883,19 @@ const downloadFineTuningOutput = async (jobId: string): Promise = /** * Get logs for a fine-tuning job by querying the Ray head node HTTP API. * It will call: http:///api/jobs//logs - * Environment: set RAY_HEAD_NODE to the host:port of the Ray head (e.g. "ray-head.example.com:8265"). + * Environment: FINETUNING_SERVICE_DNS and FINETUNING_RAY_DNS determine the + * finetuning service and Ray API endpoints. */ const getFineTuningJobLogs = async ( fineTuningJobId: string, options: { ray_job_id?: string } = {} ) => { try { - const rayHost = process.env.FINETUNING_HOST ? `${process.env.FINETUNING_HOST}:8265` : 'undefined' - // If caller provided an explicit ray_job_id, use it. Otherwise attempt to discover the Ray submission id let submissionId: string | undefined = options.ray_job_id // Query Ray /api/jobs/ and select entries where entrypoint contains the FT id (jq-like) - const listUrl = `http://${rayHost}/api/jobs/` + const listUrl = `${FINETUNING_RAY_URL}/api/jobs/` console.debug('finetuningService.getFineTuningJobLogs - listUrl:', listUrl) try { const listResp = await axios.get(listUrl, { timeout: 20000 }) @@ -916,7 +933,7 @@ const getFineTuningJobLogs = async ( } // Construct logs URL with optional tail and fetch logs - const url = `http://${rayHost}/api/jobs/${encodeURIComponent(String(submissionId))}/logs` + const url = `${FINETUNING_RAY_URL}/api/jobs/${encodeURIComponent(String(submissionId))}/logs` const resp = await axios.get(url, { timeout: 30000 }) // Normalize logs response so newlines are preserved and objects/arrays are readable try { diff --git a/studio-frontend/packages/ui/src/api/evaluation.js b/studio-frontend/packages/ui/src/api/evaluation.js index 9effd5b..38e13fc 100644 --- a/studio-frontend/packages/ui/src/api/evaluation.js +++ b/studio-frontend/packages/ui/src/api/evaluation.js @@ -37,6 +37,9 @@ const evaluationApi = { // Pull/register a new judge model pullModel: (payload) => client.post('/evaluation/models/pull', payload), + // Check files in sandbox Data Management (dataprep) + getSandboxDataManagementFiles: (payload) => client.post('/evaluation/sandbox-files', payload), + // Delete an evaluation run deleteRun: (runId) => client.delete(`/evaluation/runs/${runId}`), diff --git a/studio-frontend/packages/ui/src/views/evaluation/CreateRunModal.jsx b/studio-frontend/packages/ui/src/views/evaluation/CreateRunModal.jsx index 7454c76..eb20ce2 100644 --- a/studio-frontend/packages/ui/src/views/evaluation/CreateRunModal.jsx +++ b/studio-frontend/packages/ui/src/views/evaluation/CreateRunModal.jsx @@ -7,41 +7,141 @@ import { Button, Checkbox, CircularProgress, + Collapse, Dialog, DialogActions, DialogContent, DialogTitle, + Divider, FormControl, FormControlLabel, FormGroup, FormLabel, IconButton, + InputAdornment, InputLabel, MenuItem, Select, Stack, + TextField, Typography } from '@mui/material' -import { useTheme } from '@mui/material/styles' // icons -import { IconX } from '@tabler/icons-react' +import { IconX, IconChevronDown, IconChevronUp } from '@tabler/icons-react' // API import evaluationApi from '@/api/evaluation' +import chatflowsApi from '@/api/chatflows' const METRICS = [ 'AnswerRelevancy', 'Faithfulness', - 'ContextualPrecision', - 'ContextualRecall', - 'ContextualRelevancy', 'Hallucination' ] -const CreateRunModal = ({ open, onClose, onRunCreated }) => { - const theme = useTheme() +const sanitizeWorkflowNodes = (nodes = []) => + nodes + .map((node) => { + const inputs = Object.fromEntries( + Object.entries(node?.data?.inputs || {}).filter( + ([, value]) => + value !== '' && + value !== null && + value !== undefined && + !String(value).startsWith('{{') + ) + ) + + return { + id: node?.id, + data: { + label: node?.data?.label, + inputs, + inputParams: (node?.data?.inputParams || []).map((param) => ({ + name: param?.name, + label: param?.label + })) + } + } + }) + .filter((node) => Object.keys(node?.data?.inputs || {}).length > 0) + +const hasDataprepNode = (nodes = []) => + nodes.some((node) => String(node?.data?.name || '').startsWith('opea_service@prepare_doc_redis_prep')) + +const buildRunName = ({ sandboxId, datasetId, modelName }) => { + const sandboxLabel = sandboxId || 'sandbox' + const datasetLabel = datasetId || 'dataset' + const modelLabel = modelName || 'model' + + return `${sandboxLabel} · dataset ${datasetLabel} · ${modelLabel}` +} + +const createConfigurationSnapshot = async ({ sandboxId, systemPrompt, temperature, maxTokens }) => { + const snapshot = { + captured_at: new Date().toISOString(), + request: { + system_prompt: systemPrompt, + temperature, + max_tokens: maxTokens, + request_model: 'NA' + }, + workflow_nodes: [], + data_management: { + status: 'idle', + files: [], + error: null + } + } + + const chatflowId = String(sandboxId || '').replace(/^sandbox-/, '') + + if (!chatflowId) { + snapshot.data_management.status = 'not-applicable' + return snapshot + } + + try { + const cfRes = await chatflowsApi.getSpecificChatflow(chatflowId) + const flowData = cfRes.data?.flowData + const parsed = typeof flowData === 'string' ? JSON.parse(flowData) : flowData + const nodes = parsed?.nodes || [] + + snapshot.workflow_nodes = sanitizeWorkflowNodes(nodes) + + if (!hasDataprepNode(nodes)) { + snapshot.data_management.status = 'not-applicable' + return snapshot + } + + try { + const filesRes = await evaluationApi.getSandboxDataManagementFiles({ + sandbox_id: sandboxId + }) + snapshot.data_management.status = 'done' + snapshot.data_management.files = Array.isArray(filesRes.data?.files) ? filesRes.data.files : [] + } catch (filesErr) { + snapshot.data_management.status = 'error' + snapshot.data_management.error = + filesErr?.response?.data?.message || + filesErr?.response?.data?.detail || + filesErr?.message || + 'Unknown error' + } + } catch (chatflowErr) { + snapshot.data_management.status = 'error' + snapshot.data_management.error = + chatflowErr?.response?.data?.message || + chatflowErr?.response?.data?.detail || + chatflowErr?.message || + 'Failed to load workflow configuration' + } + + return snapshot +} +const CreateRunModal = ({ open, onClose, onRunCreated }) => { const [sandboxes, setSandboxes] = useState([]) const [datasets, setDatasets] = useState([]) const [models, setModels] = useState([]) @@ -53,6 +153,10 @@ const CreateRunModal = ({ open, onClose, onRunCreated }) => { const [selectedMetrics, setSelectedMetrics] = useState(['AnswerRelevancy', 'Faithfulness']) const [submitting, setSubmitting] = useState(false) const [error, setError] = useState('') + const [showAdvanced, setShowAdvanced] = useState(false) + const [systemPrompt, setSystemPrompt] = useState('You are a helpful assistant') + const [temperature, setTemperature] = useState(0.4) + const [maxTokens, setMaxTokens] = useState(100) useEffect(() => { if (!open) return @@ -64,7 +168,7 @@ const CreateRunModal = ({ open, onClose, onRunCreated }) => { evaluationApi.getModels() ]) .then(([sbRes, dsRes, mdRes]) => { - setSandboxes(sbRes.data || []) + setSandboxes(sbRes.data?.sandboxes || []) setDatasets(dsRes.data || []) setModels(mdRes.data || []) }) @@ -86,16 +190,40 @@ const CreateRunModal = ({ open, onClose, onRunCreated }) => { setSubmitting(true) setError('') try { + const parsedTemperature = parseFloat(temperature) + const parsedMaxTokens = parseInt(maxTokens, 10) + const configurationSnapshot = await createConfigurationSnapshot({ + sandboxId, + systemPrompt, + temperature: parsedTemperature, + maxTokens: parsedMaxTokens + }) + const res = await evaluationApi.createRun({ + name: buildRunName({ + sandboxId, + datasetId, + modelName: judgeModel + }), sandbox_id: sandboxId, dataset_id: datasetId, - judge_model: judgeModel, - metrics: selectedMetrics + model_name: judgeModel, + metrics: selectedMetrics, + system_prompt: systemPrompt, + temperature: parsedTemperature, + max_tokens: parsedMaxTokens, + configuration_snapshot: configurationSnapshot }) onRunCreated && onRunCreated(res.data) handleClose() } catch (err) { - setError(err?.response?.data?.message || 'Failed to create run.') + const detail = err?.response?.data?.detail + + if (Array.isArray(detail) && detail.length > 0) { + setError(detail.map((item) => item.msg).join(' ')) + } else { + setError(err?.response?.data?.message || err?.response?.data?.detail || 'Failed to create run.') + } } finally { setSubmitting(false) } @@ -106,6 +234,10 @@ const CreateRunModal = ({ open, onClose, onRunCreated }) => { setDatasetId('') setJudgeModel('') setSelectedMetrics(['AnswerRelevancy', 'Faithfulness']) + setShowAdvanced(false) + setSystemPrompt('You are a helpful assistant') + setTemperature(0.4) + setMaxTokens(100) setError('') onClose() } @@ -197,6 +329,57 @@ const CreateRunModal = ({ open, onClose, onRunCreated }) => { + {/* Advanced settings toggle */} + + + + + + setSystemPrompt(e.target.value)} + helperText='System message sent to the app-backend for every question.' + /> + + setTemperature(e.target.value)} + inputProps={{ min: 0, max: 2, step: 0.05 }} + sx={{ flex: 1 }} + /> + setMaxTokens(e.target.value)} + inputProps={{ min: 1, step: 1 }} + InputProps={{ + endAdornment: tok + }} + sx={{ flex: 1 }} + /> + + + + + {error && ( {error} diff --git a/studio-frontend/packages/ui/src/views/evaluation/DatasetTab.jsx b/studio-frontend/packages/ui/src/views/evaluation/DatasetTab.jsx index 812a408..7195c75 100644 --- a/studio-frontend/packages/ui/src/views/evaluation/DatasetTab.jsx +++ b/studio-frontend/packages/ui/src/views/evaluation/DatasetTab.jsx @@ -37,6 +37,8 @@ import { IconPlus, IconRefresh, IconWand, IconX } from '@tabler/icons-react' // API import evaluationApi from '@/api/evaluation' +import { StyledButton } from '@/ui-component/button/StyledButton' + const StyledTableCell = styled(TableCell)(({ theme }) => ({ borderColor: theme.palette.grey[900] + 25, [`&.${tableCellClasses.head}`]: { @@ -377,32 +379,35 @@ const DatasetTab = ({ isVisible }) => { return ( - + + } + onClick={() => setNewDatasetOpen(true)} + sx={{ borderRadius: 2, height: 40 }} + > + Create New Dataset + + } + onClick={() => setSynthesizeOpen(true)} + sx={{ borderRadius: 2, height: 40 }} + > + Synthesize Dataset + - + - - {error && ( diff --git a/studio-frontend/packages/ui/src/views/evaluation/ExecutionTab.jsx b/studio-frontend/packages/ui/src/views/evaluation/ExecutionTab.jsx index 1629d1a..e9289c0 100644 --- a/studio-frontend/packages/ui/src/views/evaluation/ExecutionTab.jsx +++ b/studio-frontend/packages/ui/src/views/evaluation/ExecutionTab.jsx @@ -7,6 +7,11 @@ import { Button, Chip, CircularProgress, + Dialog, + DialogActions, + DialogContent, + DialogTitle, + IconButton, Paper, Stack, Table, @@ -23,28 +28,39 @@ import { useTheme, styled } from '@mui/material/styles' import { tableCellClasses } from '@mui/material/TableCell' // icons -import { IconPlus, IconRefresh } from '@tabler/icons-react' +import { IconPlus, IconRefresh, IconEye, IconTrash } from '@tabler/icons-react' // API import evaluationApi from '@/api/evaluation' // components import CreateRunModal from './CreateRunModal' +import RunDetailsModal from './RunDetailsModal' + +import { StyledButton } from '@/ui-component/button/StyledButton' const POLL_INTERVAL_MS = 5000 const ACTIVE_STATUSES = ['pending', 'running'] const StyledTableCell = styled(TableCell)(({ theme }) => ({ borderColor: theme.palette.grey[900] + 25, + verticalAlign: 'middle', [`&.${tableCellClasses.head}`]: { color: theme.palette.grey[900] }, [`&.${tableCellClasses.body}`]: { - fontSize: 14, - height: 56 + fontSize: 14 } })) +const OverflowTypography = styled(Typography)(() => ({ + overflow: 'hidden', + textOverflow: 'ellipsis', + whiteSpace: 'nowrap', + display: 'block' +})) + + const StyledTableRow = styled(TableRow)(() => ({ '&:last-child td, &:last-child th': { border: 0 @@ -65,6 +81,13 @@ const statusColor = (status) => { } } +const effectiveRunStatus = (run) => { + if (run.status === 'completed' && run.results?.some((r) => r.passed === false)) { + return 'failed' + } + return run.status +} + const formatDate = (dateStr) => { if (!dateStr) return '—' try { @@ -76,22 +99,37 @@ const formatDate = (dateStr) => { } } -const formatScore = (scoreSummary) => { - if (!scoreSummary || typeof scoreSummary !== 'object') return '—' - const entries = Object.entries(scoreSummary) - if (entries.length === 0) return '—' - return entries.map(([k, v]) => `${k}: ${typeof v === 'number' ? v.toFixed(3) : v}`).join(', ') +const computeMetricAverages = (run) => { + const metrics = run.metrics || [] + if (metrics.length === 0) return [] + + const results = run.results || [] + return metrics.map((metricName) => { + const scores = results + .map((r) => r.metric_scores?.[metricName]?.score) + .filter((s) => s !== null && s !== undefined && typeof s === 'number') + const avg = scores.length > 0 ? scores.reduce((a, b) => a + b, 0) / scores.length : null + return { metric: metricName, avg } + }) } +const getRunId = (run) => run?.id ?? run?.run_id + const ExecutionTab = ({ isVisible }) => { const theme = useTheme() const [runs, setRuns] = useState([]) + const [datasetMap, setDatasetMap] = useState({}) const [isLoading, setIsLoading] = useState(true) const [error, setError] = useState(null) const [order, setOrder] = useState('desc') const [orderBy, setOrderBy] = useState('created_at') const [createModalOpen, setCreateModalOpen] = useState(false) + const [detailsModalOpen, setDetailsModalOpen] = useState(false) + const [selectedRunId, setSelectedRunId] = useState(null) + const [deleteConfirmOpen, setDeleteConfirmOpen] = useState(false) + const [runToDelete, setRunToDelete] = useState(null) + const [deleting, setDeleting] = useState(false) // Track interval ids per run_id const pollTimersRef = useRef({}) @@ -111,7 +149,7 @@ const ExecutionTab = ({ isVisible }) => { const res = await evaluationApi.getRun(runId) const updated = res.data setRuns((prev) => - prev.map((r) => (r.run_id === runId ? { ...r, ...updated } : r)) + prev.map((r) => (getRunId(r) === runId ? { ...r, ...updated } : r)) ) if (!ACTIVE_STATUSES.includes(updated?.status)) { stopPolling(runId) @@ -128,15 +166,40 @@ const ExecutionTab = ({ isVisible }) => { try { setIsLoading(true) setError(null) - const res = await evaluationApi.getRuns() - const data = res.data || [] - setRuns(data) + const [runsRes, datasetsRes] = await Promise.all([ + evaluationApi.getRuns(), + evaluationApi.getDatasets().catch(() => ({ data: [] })) + ]) + let data = runsRes.data || [] + const dsMap = {} + ;(datasetsRes.data || []).forEach((ds) => { dsMap[ds.id] = ds.name }) + setDatasetMap(dsMap) + // Start polling for any active runs data.forEach((run) => { if (ACTIVE_STATUSES.includes(run.status)) { - startPolling(run.run_id) + startPolling(getRunId(run)) } }) + + // Wait for full details of completed runs so we can detect per-entry failures + const completedRuns = data.filter((r) => r.status === 'completed') + if (completedRuns.length > 0) { + const responses = await Promise.all( + completedRuns.map((r) => + evaluationApi.getRun(getRunId(r)).catch(() => null) + ) + ) + const updates = {} + responses.forEach((resp, idx) => { + if (resp?.data) updates[getRunId(completedRuns[idx])] = resp.data + }) + if (Object.keys(updates).length > 0) { + data = data.map((r) => (updates[getRunId(r)] ? { ...r, ...updates[getRunId(r)] } : r)) + } + } + + setRuns(data) } catch (err) { setError(err?.response?.data?.message || 'Failed to load evaluation runs.') } finally { @@ -159,7 +222,39 @@ const ExecutionTab = ({ isVisible }) => { const handleRunCreated = (newRun) => { setRuns((prev) => [newRun, ...prev]) if (ACTIVE_STATUSES.includes(newRun.status)) { - startPolling(newRun.run_id) + startPolling(getRunId(newRun)) + } + } + + const handleOpenDetails = (runId) => { + setSelectedRunId(runId) + setDetailsModalOpen(true) + } + + const handleCloseDetails = () => { + setDetailsModalOpen(false) + setSelectedRunId(null) + } + + const handleOpenDeleteConfirm = (e, run) => { + e.stopPropagation() + setRunToDelete(run) + setDeleteConfirmOpen(true) + } + + const handleConfirmDelete = async () => { + if (!runToDelete) return + try { + setDeleting(true) + await evaluationApi.deleteRun(getRunId(runToDelete)) + setRuns((prev) => prev.filter((r) => getRunId(r) !== getRunId(runToDelete))) + stopPolling(getRunId(runToDelete)) + setDeleteConfirmOpen(false) + setRunToDelete(null) + } catch (err) { + setError(err?.response?.data?.detail || 'Failed to delete run.') + } finally { + setDeleting(false) } } @@ -174,7 +269,7 @@ const ExecutionTab = ({ isVisible }) => { if (orderBy === 'created_at') { cmp = new Date(a.created_at) - new Date(b.created_at) } else if (orderBy === 'run_id') { - cmp = String(a.run_id).localeCompare(String(b.run_id)) + cmp = String(getRunId(a)).localeCompare(String(getRunId(b))) } else if (orderBy === 'status') { cmp = String(a.status).localeCompare(String(b.status)) } @@ -184,32 +279,37 @@ const ExecutionTab = ({ isVisible }) => { const columns = [ { id: 'run_id', label: 'Run ID' }, { id: 'sandbox_id', label: 'Sandbox', sortable: false }, + { id: 'dataset_id', label: 'Dataset', sortable: false }, + { id: 'model_name', label: 'Judge Model', sortable: false }, { id: 'status', label: 'Status' }, { id: 'score_summary', label: 'Score Summary', sortable: false }, - { id: 'created_at', label: 'Created At' } + { id: 'created_at', label: 'Created At' }, + { id: 'actions', label: 'Actions', sortable: false } ] return ( - + + } + onClick={() => setCreateModalOpen(true)} + sx={{ borderRadius: 2, height: 40 }} + > + Create New Run + - + - {error && ( @@ -242,31 +342,50 @@ const ExecutionTab = ({ isVisible }) => { {isLoading ? ( - + ) : sortedRuns.length === 0 ? ( - + - No evaluation runs yet. Click "New Run" to get started. + No evaluation runs yet. Click "Create New Run" to get started. ) : ( sortedRuns.map((run) => ( - - - - {run.run_id} + handleOpenDetails(getRunId(run))} + sx={{ cursor: 'pointer' }} + > + + + {getRunId(run)} + + + + + {run.sandbox_id || '—'} + + + + + {datasetMap[run.dataset_id] || run.dataset_id || '—'} + + + + + {run.model_name || '—'} - {run.sandbox_id || '—'} { } /> + + {(() => { + const metricAvgs = computeMetricAverages(run) + if (metricAvgs.length === 0) return N/A + return ( + + {metricAvgs.map(({ metric, avg }) => ( + + + {metric}:{' '} + {avg !== null ? avg.toFixed(3) : 'N/A'} + + + ))} + + ) + })()} + - {formatScore(run.score_summary)} + + {formatDate(run.created_at)} + + + e.stopPropagation()}> + + + handleOpenDetails(getRunId(run))} + > + + + + + handleOpenDeleteConfirm(e, run)} + > + + + + - {formatDate(run.created_at)} )) )} @@ -291,6 +451,39 @@ const ExecutionTab = ({ isVisible }) => { onClose={() => setCreateModalOpen(false)} onRunCreated={handleRunCreated} /> + + + + {/* Delete Confirmation Dialog */} + setDeleteConfirmOpen(false)}> + Delete Run? + + + Are you sure you want to delete this evaluation run? This action cannot be undone. + + {runToDelete && ( + + Run ID: {getRunId(runToDelete).substring(0, 12)}... + + )} + + + + + + ) } diff --git a/studio-frontend/packages/ui/src/views/evaluation/RunDetailsModal.jsx b/studio-frontend/packages/ui/src/views/evaluation/RunDetailsModal.jsx new file mode 100644 index 0000000..cd2eb3b --- /dev/null +++ b/studio-frontend/packages/ui/src/views/evaluation/RunDetailsModal.jsx @@ -0,0 +1,777 @@ +import { useState, useEffect } from 'react' +import PropTypes from 'prop-types' + +// material-ui +import { + Box, + Button, + Chip, + CircularProgress, + Dialog, + DialogActions, + DialogContent, + DialogTitle, + Divider, + Drawer, + IconButton, + Paper, + Stack, + Tab, + Tabs, + Table, + TableBody, + TableCell, + TableContainer, + TableHead, + TableRow, + Tooltip, + Typography, + styled +} from '@mui/material' +import { useTheme } from '@mui/material/styles' +import { tableCellClasses } from '@mui/material/TableCell' + +// icons +import { IconX, IconAlertTriangle } from '@tabler/icons-react' + +// API +import evaluationApi from '@/api/evaluation' +import chatflowsApi from '@/api/chatflows' + +const StyledTableCell = styled(TableCell)(({ theme }) => ({ + borderColor: theme.palette.grey[900] + 25, + [`&.${tableCellClasses.head}`]: { + color: theme.palette.grey[900], + fontWeight: 600 + }, + [`&.${tableCellClasses.body}`]: { + fontSize: 13, + padding: '8px 12px' + } +})) + +const StyledTableRow = styled(TableRow)(() => ({ + '&:last-child td': { + border: 0 + } +})) + +const statusColor = (status) => { + switch (status) { + case true: + return 'success' + case false: + return 'error' + default: + return 'default' + } +} + +const formatDate = (dateStr) => { + if (!dateStr) return '—' + try { + const dt = new Date(dateStr) + if (isNaN(dt.getTime())) return dateStr + return dt.toLocaleString() + } catch { + return dateStr + } +} + +const truncateText = (text, maxLength = 100) => { + if (!text) return '—' + if (text.length <= maxLength) return text + return text.substring(0, maxLength) + '...' +} + +const formatMetricScore = (score) => { + if (score === null || score === undefined) return 'N/A' + if (typeof score === 'number') return score.toFixed(3) + return String(score) +} + +const applyConfigurationSnapshot = (snapshot, setters) => { + if (!snapshot || typeof snapshot !== 'object') { + return false + } + + const hasSnapshotData = Array.isArray(snapshot?.workflow_nodes) || snapshot?.data_management + if (!hasSnapshotData) { + return false + } + + const workflowNodes = Array.isArray(snapshot?.workflow_nodes) ? snapshot.workflow_nodes : [] + const dataManagement = snapshot?.data_management || {} + + setters.setWorkflowNodes(workflowNodes) + setters.setDataManagementFiles(Array.isArray(dataManagement.files) ? dataManagement.files : []) + setters.setDataFilesError(dataManagement.error || '') + + if (dataManagement.status) { + setters.setDataFilesStatus(dataManagement.status) + return true + } + + setters.setDataFilesStatus(workflowNodes.length > 0 ? 'done' : 'not-applicable') + return true +} + +function TabPanel(props) { + const { children, value, index, ...other } = props + + return ( + + ) +} +TabPanel.propTypes = { + children: PropTypes.node, + index: PropTypes.number.isRequired, + value: PropTypes.number.isRequired, +} + +const RunDetailsModal = ({ open, onClose, runId }) => { + const theme = useTheme() + + const [tabValue, setTabValue] = useState(0) + + const handleTabChange = (event, newValue) => { + setTabValue(newValue) + } + + const [run, setRun] = useState(null) + const [loading, setLoading] = useState(false) + const [error, setError] = useState(null) + const [datasetName, setDatasetName] = useState(null) + const [workflowNodes, setWorkflowNodes] = useState([]) + const [dataManagementFiles, setDataManagementFiles] = useState([]) + // 'idle' | 'not-applicable' | 'checking' | 'done' | 'error' + const [dataFilesStatus, setDataFilesStatus] = useState('idle') + const [dataFilesError, setDataFilesError] = useState('') + + const [selectedEntry, setSelectedEntry] = useState(null) + const [drawerOpen, setDrawerOpen] = useState(false) + + const handleOpenDrawer = (result) => { + setSelectedEntry(result) + setDrawerOpen(true) + } + + const handleCloseDrawer = () => { + setDrawerOpen(false) + setSelectedEntry(null) + } + + const getEffectiveStatus = (runData) => { + if (runData.status === 'completed' && runData.results?.some((r) => r.passed === false)) { + return 'failed' + } + return runData.status + } + + const getOrderedMetricEntries = (runData, result) => { + if (!result?.metric_scores) return [] + + const configuredMetrics = Array.isArray(runData?.metrics) ? runData.metrics : [] + const scoredMetrics = Object.keys(result.metric_scores) + const orderedMetricNames = [ + ...configuredMetrics, + ...scoredMetrics.filter((metricName) => !configuredMetrics.includes(metricName)) + ] + + return orderedMetricNames + .map((metricName) => [metricName, result.metric_scores[metricName]]) + .filter(([, scoreDetails]) => Boolean(scoreDetails)) + } + + const getResultReasons = (runData, result) => { + const metricReasons = getOrderedMetricEntries(runData, result) + .filter(([, scoreDetails]) => scoreDetails.reason) + .map(([metricName, scoreDetails]) => `${metricName}: ${scoreDetails.reason}`) + + if (metricReasons.length > 0) { + return metricReasons + } + + if (!result?.reason) return [] + + return String(result.reason) + .split(/;\s*/) + .filter(Boolean) + } + + useEffect(() => { + if (!open || !runId) return + + const fetchRun = async () => { + try { + setLoading(true) + setError(null) + setDatasetName(null) + setWorkflowNodes([]) + setDataManagementFiles([]) + setDataFilesStatus('idle') + setDataFilesError('') + const res = await evaluationApi.getRun(runId) + setRun(res.data) + try { + const dsRes = await evaluationApi.getDataset(res.data.dataset_id) + setDatasetName(dsRes.data.name) + } catch { + // non-critical + } + if ( + applyConfigurationSnapshot(res.data.configuration_snapshot, { + setWorkflowNodes, + setDataManagementFiles, + setDataFilesStatus, + setDataFilesError + }) + ) { + return + } + try { + const chatflowId = res.data.sandbox_id?.replace(/^sandbox-/, '') + if (chatflowId) { + const cfRes = await chatflowsApi.getSpecificChatflow(chatflowId).catch((e) => { + console.error('[RunDetailsModal] getSpecificChatflow failed:', e) + throw e + }) + const flowData = cfRes.data?.flowData + if (flowData) { + const parsed = typeof flowData === 'string' ? JSON.parse(flowData) : flowData + const nodes = parsed.nodes || [] + setWorkflowNodes(nodes) + + const hasDataprepNode = nodes.some((node) => + String(node?.data?.name || '').startsWith('opea_service@prepare_doc_redis_prep') + ) + + if (!hasDataprepNode) { + setDataFilesStatus('not-applicable') + } else if (res.data.sandbox_id) { + setDataFilesStatus('checking') + try { + const filesRes = await evaluationApi.getSandboxDataManagementFiles({ + sandbox_id: res.data.sandbox_id + }) + const files = Array.isArray(filesRes.data?.files) ? filesRes.data.files : [] + setDataManagementFiles(files) + setDataFilesStatus('done') + } catch (filesErr) { + console.error('[RunDetailsModal] getSandboxDataManagementFiles failed:', filesErr) + const errMsg = filesErr?.response?.data?.message || filesErr?.response?.data?.detail || filesErr?.message || 'Unknown error' + setDataManagementFiles([]) + setDataFilesError(errMsg) + setDataFilesStatus('error') + } + } + } + } + } catch { + // non-critical + } + } catch (err) { + setError(err?.response?.data?.detail || 'Failed to load run details.') + setLoading(false) + } finally { + setLoading(false) + } + } + + fetchRun() + }, [open, runId]) + + if (!open) return null + + return ( + + + Evaluation Run Details + + + + + + + {loading ? ( + + + + ) : error ? ( + {error} + ) : run ? ( + + + + + + + + + + {/* Run Metadata */} + + + Run Information + + + + + + + Run ID + + + {run.id} + + + + + Sandbox + + + {truncateText(run.sandbox_id, 50)} + + + + + Status + + + + + + + + Dataset + + {datasetName || run.dataset_id} + + + + Judge Model + + {run.model_name} + + + + Metrics + + + {run.metrics.map((m) => ( + + ))} + + + + + Created At + + + {formatDate(run.created_at)} + + + + + Completed At + + + {run.completed_at ? formatDate(run.completed_at) : '—'} + + + + + Average Scores + + + {run.results && run.results.length > 0 ? ( + + {run.metrics.map((metricName) => { + const scores = run.results + .map((r) => r.metric_scores?.[metricName]?.score) + .filter((s) => s !== null && s !== undefined && typeof s === 'number') + const avg = scores.length > 0 ? scores.reduce((a, b) => a + b, 0) / scores.length : null + return ( + + {metricName}: {avg !== null ? avg.toFixed(3) : 'N/A'} + + ) + })} + + ) : ( + N/A + )} + + + + {run.error && ( + <> + + + + + + Error + + + {run.error} + + + + + )} + + + + + {/* Results Table */} + {run.results && run.results.length > 0 && ( + + + Evaluation Results ({run.results.length} entries) + + {(() => { + const hasContext = run.results.some( + (r) => r.entry?.context && r.entry.context.length > 0 + ) + return ( + + + + + Entry + Input + {hasContext && Context} + Actual Output + Metrics + Status + Reason + + + + {run.results.map((result) => { + const orderedMetricEntries = getOrderedMetricEntries(run, result) + const failReasons = getResultReasons(run, result) + const failReasonText = failReasons.join('\n') + return ( + handleOpenDrawer(result)} + sx={{ cursor: 'pointer' }} + > + + + #{result.entry_id} + + + + + + {result.entry?.input || '—'} + + ({ position: 'absolute', bottom: 0, left: 0, right: 0, height: '36px', background: `linear-gradient(to bottom, transparent, ${t.palette.background.paper})`, pointerEvents: 'none' })} /> + + + {hasContext && ( + + + + {result.entry?.context?.length > 0 ? result.entry.context.join('\n\n') : '—'} + + ({ position: 'absolute', bottom: 0, left: 0, right: 0, height: '36px', background: `linear-gradient(to bottom, transparent, ${t.palette.background.paper})`, pointerEvents: 'none' })} /> + + + )} + + + + {result.actual_output || '—'} + + ({ position: 'absolute', bottom: 0, left: 0, right: 0, height: '36px', background: `linear-gradient(to bottom, transparent, ${t.palette.background.paper})`, pointerEvents: 'none' })} /> + + + + {orderedMetricEntries.length > 0 ? ( + + {orderedMetricEntries.map(([metricName, scores]) => ( + + + {metricName}: {formatMetricScore(scores.score)} + + + ))} + + ) : ( + '—' + )} + + + + + + {failReasons.length > 0 ? ( + + {failReasonText} + + ) : ( + '—' + )} + + + ) + })} + +
+
+ ) + })()} +
+ )} + + {(!run.results || run.results.length === 0) && ( + + No results available yet. + + )} +
+
+ + + + + Sandbox Workflow Configuration + + + + {workflowNodes.length === 0 ? ( + N/A + ) : ( + + {workflowNodes + .filter((node) => node.data?.inputs && Object.keys(node.data.inputs).length > 0) + .map((node) => { + const inputs = node.data.inputs + const displayInputs = Object.entries(inputs).filter( + ([, v]) => + v !== '' && + v !== null && + v !== undefined && + !String(v).startsWith('{{') + ) + if (displayInputs.length === 0) return null + const paramLabels = {} + ;(node.data.inputParams || []).forEach((p) => { + paramLabels[p.name] = p.label + }) + return ( + + + {node.data.label} + + + {displayInputs.map(([key, val]) => ( + + + {paramLabels[key] || key}:{' '} + {typeof val === 'boolean' + ? String(val) + : truncateText(String(val), 60)} + + + ))} + + + ) + })} + + )} + + + + + + Data Management Files + + + {dataFilesStatus === 'idle' || dataFilesStatus === 'not-applicable' ? ( + N/A + ) : dataFilesStatus === 'checking' ? ( + + + + Checking... + + + ) : dataFilesStatus === 'error' ? ( + + + Unable to check (sandbox may be stopped) + + + ) : dataManagementFiles.length > 0 ? ( + + {dataManagementFiles.slice(0, 5).map((file, idx) => { + const label = typeof file === 'string' ? file : file?.name || file?.id || file?.file_name || JSON.stringify(file) + return ( + + {truncateText(label, 70)} + + ) + })} + {dataManagementFiles.length > 5 && ( + + +{dataManagementFiles.length - 5} more + + )} + + ) : ( + + No files uploaded + + )} + + + + + + +
+ ) : null} +
+ + + + + + {/* Entry Details Drawer */} + theme.zIndex.modal + 10 }} + PaperProps={{ sx: { width: { xs: '100%', sm: 600, md: 800 }, p: 3, bgcolor: theme.palette.mode === 'dark' ? 'grey.900' : 'background.paper' } }} + > + {selectedEntry && ( + + + Entry #{selectedEntry.entry_id} Details + + + + + + + + + Input + + + {selectedEntry.entry?.input || '—'} + + + + + {selectedEntry.entry?.context && selectedEntry.entry.context.length > 0 && ( + + Context + + {selectedEntry.entry.context.map((ctx, idx) => ( + + {ctx} + + ))} + + + )} + + + Actual Output + + + {selectedEntry.actual_output || '—'} + + + + + {selectedEntry.expected_output && ( + + Expected Output + + + {selectedEntry.expected_output} + + + + )} + + + Execution Status + + + + {getResultReasons(run, selectedEntry).length > 0 && ( + + Reasons + + + {getResultReasons(run, selectedEntry).join('\n')} + + + + )} + + + + )} + +
+ ) +} + +RunDetailsModal.propTypes = { + open: PropTypes.bool.isRequired, + onClose: PropTypes.func.isRequired, + runId: PropTypes.string +} + +export default RunDetailsModal diff --git a/studio-frontend/packages/ui/src/views/tracer/index.jsx b/studio-frontend/packages/ui/src/views/tracer/index.jsx index d3fa4e9..971f03f 100644 --- a/studio-frontend/packages/ui/src/views/tracer/index.jsx +++ b/studio-frontend/packages/ui/src/views/tracer/index.jsx @@ -1,264 +1,755 @@ -import { useState, useEffect } from "react"; -import { useParams } from "react-router-dom"; -import { styled } from "@mui/material/styles"; +import { useEffect, useRef, useState } from 'react' +import { useParams } from 'react-router-dom' + import { + Box, + Chip, + CircularProgress, + Divider, + Drawer, + IconButton, + Paper, + Stack, Table, TableBody, TableCell, TableContainer, TableHead, - TableRow, - Paper, - Button, - Box, - Stack, - Typography, - Divider, TablePagination, - Fade -} from "@mui/material"; + TableRow, + Tooltip, + Typography +} from '@mui/material' +import { styled, useTheme } from '@mui/material/styles' import { tableCellClasses } from '@mui/material/TableCell' -import ViewHeader from '@/layout/MainLayout/ViewHeader' -import chatflowsApi from '@/api/chatflows'; -import useApi from '@/hooks/useApi'; -import config from '@/config' +import { IconEye, IconRefresh, IconX } from '@tabler/icons-react' -const TraceNode = ({ node, handleSelect }) => { - console.log('TraceNode', TraceNode) - return ( - - - {node.children && node.children.length > 0 && ( - - {node.children.map((child) => ( - - ))} - - )} - -)}; +import MainCard from '@/ui-component/cards/MainCard' +import ViewHeader from '@/layout/MainLayout/ViewHeader' +import { StyledButton } from '@/ui-component/button/StyledButton' +import chatflowsApi from '@/api/chatflows' +import useApi from '@/hooks/useApi' +import config from '@/config' const StyledTableCell = styled(TableCell)(({ theme }) => ({ borderColor: theme.palette.grey[900] + 25, - + verticalAlign: 'top', [`&.${tableCellClasses.head}`]: { - color: theme.palette.grey[900] + color: theme.palette.grey[900], + fontWeight: 600 }, [`&.${tableCellClasses.body}`]: { fontSize: 14, - height: 64 + padding: '10px 12px' } })) const StyledTableRow = styled(TableRow)(() => ({ - // hide last border '&:last-child td, &:last-child th': { border: 0 } })) +const OverflowTypography = styled(Typography)(() => ({ + overflow: 'hidden', + textOverflow: 'ellipsis', + whiteSpace: 'nowrap', + display: 'block' +})) + +const formatDate = (dateStr) => { + if (!dateStr) return '—' + + try { + const date = new Date(dateStr) + if (isNaN(date.getTime())) return dateStr + return date.toLocaleString() + } catch { + return dateStr + } +} + +const truncateText = (text, maxLength = 100) => { + if (!text) return '—' + if (text.length <= maxLength) return text + return `${text.substring(0, maxLength)}...` +} + +const formatDuration = (durationNs) => { + if (durationNs === null || durationNs === undefined || durationNs === '') return '—' + + const numericDuration = Number(durationNs) + if (Number.isNaN(numericDuration)) return String(durationNs) + + const durationMs = numericDuration / 1000000 + + if (durationMs < 1) return `${(numericDuration / 1000).toFixed(2)} μs` + if (durationMs < 1000) return `${durationMs.toFixed(durationMs < 10 ? 2 : 1)} ms` + return `${(durationMs / 1000).toFixed(2)} s` +} + +const formatPayload = (payload) => { + if (!payload) return '—' + + if (typeof payload === 'object') { + return JSON.stringify(payload, null, 2) + } + + if (typeof payload !== 'string') { + return String(payload) + } + + try { + return JSON.stringify(JSON.parse(payload), null, 2) + } catch { + return payload + } +} + +const getStatusPresentation = (statusCode) => { + const normalizedStatus = String(statusCode || '').toLowerCase() + + if (normalizedStatus.includes('error') || normalizedStatus.includes('fail')) { + return { label: statusCode || 'Error', color: 'error' } + } + + if (normalizedStatus === 'ok' || normalizedStatus === 'success') { + return { label: statusCode, color: 'success' } + } + + return { label: statusCode || 'Unset', color: 'default' } +} + +const flattenSpans = (spans, depth = 0) => { + if (!Array.isArray(spans)) return [] + + return spans.flatMap((span) => [{ ...span, depth }, ...flattenSpans(span.children, depth + 1)]) +} + +const buildTraceSummary = (traceTree) => { + const rootSpans = Array.isArray(traceTree?.spans) ? traceTree.spans : [] + const flatSpans = flattenSpans(rootSpans) + const services = [...new Set(flatSpans.map((span) => span.service_name).filter(Boolean))] + const llmCalls = flatSpans.filter((span) => span.llm_input || span.llm_output).length + const durationNs = flatSpans.reduce((maxDuration, span) => Math.max(maxDuration, Number(span.duration) || 0), 0) + + let statusLabel = 'Captured' + let statusColor = 'info' + if (flatSpans.some((span) => { + const normalizedStatus = String(span.status_code || '').toLowerCase() + return normalizedStatus.includes('error') || normalizedStatus.includes('fail') + })) { + statusLabel = 'Issues' + statusColor = 'error' + } else if (flatSpans.some((span) => { + const normalizedStatus = String(span.status_code || '').toLowerCase() + return normalizedStatus === 'ok' || normalizedStatus === 'success' + })) { + statusLabel = 'OK' + statusColor = 'success' + } + + return { + rootSpanCount: rootSpans.length, + rootSpanName: rootSpans[0]?.span_name || '—', + totalSpans: flatSpans.length, + llmCalls, + durationNs, + services, + statusLabel, + statusColor + } +} + +const MetadataField = ({ label, value, monospace = false }) => ( + + + {label} + + + {value || '—'} + + +) + +const DetailSection = ({ title, value, theme }) => ( + + + {title} + + + + {value || '—'} + + + +) + export default function LLMTraces() { - const [traceList, setTraceList] = useState([]); - const [traceData, setTraceData] = useState({}); - const [selectedTrace, setSelectedTrace] = useState(null); - const [selectedSpan, setSelectedSpan] = useState(null); - const [page, setPage] = useState(0); - const [rowsPerPage, setRowsPerPage] = useState(10); - const [workflowName, setWorkflowName] = useState(''); - - const { ns } = useParams(); - console.log("ns: ", ns); - - const studio_server_url = config.studio_server_url; - const sandbox_tracer_list_endpoint = config.sandbox_tracer_list_endpoint; - const sandbox_tracer_tree_endpoint = config.sandbox_tracer_tree_endpoint; - - const getAllOpeaflowsApi = useApi(chatflowsApi.getAllOpeaflows); - - // Fetch trace ids from the server - const fetchTraces = async (ns) => { - console.log("fetchTraces", ns); - const url = `${studio_server_url}/${sandbox_tracer_list_endpoint}/${ns}`; - try { - const response = await fetch(url, { headers: { "Content-Type": "application/json" } }); - if (response.ok) { - const response_data = await response.json(); - console.log("response_data", response_data); - if (response_data && response_data.trace_ids) { - setTraceList(response_data.trace_ids); - } else { - console.error("Invalid response data structure:", response_data); - setTraceList([]); - } - } else { - console.error("Error fetching traces:", response.statusText); - setTraceList([]); - } - } catch (error) { - console.error("Error fetching traces:", error); - setTraceList([]); + const theme = useTheme() + const { ns } = useParams() + + const [traceList, setTraceList] = useState([]) + const [traceSummaryMap, setTraceSummaryMap] = useState({}) + const [traceTreeCache, setTraceTreeCache] = useState({}) + const [traceData, setTraceData] = useState(null) + const [selectedTraceId, setSelectedTraceId] = useState(null) + const [selectedSpan, setSelectedSpan] = useState(null) + const [searchValue, setSearchValue] = useState('') + const [page, setPage] = useState(0) + const [rowsPerPage, setRowsPerPage] = useState(10) + const [workflowName, setWorkflowName] = useState('') + const [isLoadingTraces, setIsLoadingTraces] = useState(true) + const [isLoadingTraceDetails, setIsLoadingTraceDetails] = useState(false) + const [traceError, setTraceError] = useState(null) + const [traceDetailsError, setTraceDetailsError] = useState(null) + + const pendingSummaryIdsRef = useRef(new Set()) + + const studioServerUrl = config.studio_server_url + const sandboxTracerListEndpoint = config.sandbox_tracer_list_endpoint + const sandboxTracerTreeEndpoint = config.sandbox_tracer_tree_endpoint + + const getAllOpeaflowsApi = useApi(chatflowsApi.getAllOpeaflows) + + const filteredTraceList = traceList.filter((trace) => { + if (!searchValue.trim()) return true + return String(trace.trace_id || '').toLowerCase().includes(searchValue.trim().toLowerCase()) + }) + + const paginatedTraceList = filteredTraceList.slice(page * rowsPerPage, page * rowsPerPage + rowsPerPage) + const selectedTrace = traceList.find((trace) => trace.trace_id === selectedTraceId) || null + const selectedTraceSummary = selectedTraceId ? traceSummaryMap[selectedTraceId] || buildTraceSummary(traceData) : null + const selectedTraceSpans = flattenSpans(traceData?.spans || []) + + const fetchTraceTreeData = async (traceId) => { + const response = await fetch(`${studioServerUrl}/${sandboxTracerTreeEndpoint}/${traceId}`, { + headers: { 'Content-Type': 'application/json' } + }) + + if (!response.ok) { + const errorPayload = await response.json().catch(() => null) + throw new Error(errorPayload?.detail || 'Failed to load trace details.') } - }; - // Fetch trace tree from the server - const fetchTraceTree = async (trace_id) => { - console.log("fetchTraceTree", trace_id); - const url = `${studio_server_url}/${sandbox_tracer_tree_endpoint}/${trace_id}`; + return response.json() + } + + const loadTraces = async () => { try { - const response = await fetch(url, { headers: { "Content-Type": "application/json" } }); - const data = response.ok ? await response.json() : []; - setTraceData(data); - } catch (error) { - console.error("Error fetching trace tree:", error); - setTraceData([]); - } - }; + setIsLoadingTraces(true) + setTraceError(null) - const handleTraceSelect = (trace_id) => { - console.log("handleTraceSelect", trace_id); - setSelectedTrace(trace_id); - setSelectedSpan(null); - }; + const response = await fetch(`${studioServerUrl}/${sandboxTracerListEndpoint}/${ns}`, { + headers: { 'Content-Type': 'application/json' } + }) - const handleSpanSelect = (span) => { - setSelectedSpan(span); - }; + if (response.status === 404) { + setTraceList([]) + return + } - const handleChangePage = (event, newPage) => { - setPage(newPage); - }; + if (!response.ok) { + const errorPayload = await response.json().catch(() => null) + throw new Error(errorPayload?.detail || 'Failed to load traces.') + } - const handleChangeRowsPerPage = (event) => { - setRowsPerPage(parseInt(event.target.value, 10)); - setPage(0); - }; + const responseData = await response.json() + const nextTraceList = Array.isArray(responseData?.trace_ids) ? responseData.trace_ids : [] + setTraceList(nextTraceList) + setPage(0) + + if (selectedTraceId && !nextTraceList.some((trace) => trace.trace_id === selectedTraceId)) { + setSelectedTraceId(null) + } + } catch (error) { + setTraceError(error.message || 'Failed to load traces.') + setTraceList([]) + } finally { + setIsLoadingTraces(false) + } + } - // Use Effects useEffect(() => { - console.log("useEffect ns"); - fetchTraces(ns); - }, [studio_server_url, sandbox_tracer_list_endpoint, ns]); + setSelectedTraceId(null) + setTraceData(null) + setSelectedSpan(null) + loadTraces() + }, [ns, studioServerUrl, sandboxTracerListEndpoint]) useEffect(() => { - console.log("useEffect selectedTrace", selectedTrace); - if (selectedTrace) { - fetchTraceTree(selectedTrace); - } else { - setTraceData({}); - } - } - , [selectedTrace, studio_server_url, sandbox_tracer_tree_endpoint]); + getAllOpeaflowsApi.request() + }, []) + + useEffect(() => { + if (!getAllOpeaflowsApi.data || !ns) return + + const matchingFlow = getAllOpeaflowsApi.data.find((flow) => `sandbox-${flow.id}` === ns) + setWorkflowName(matchingFlow ? matchingFlow.name : '') + }, [getAllOpeaflowsApi.data, ns]) useEffect(() => { - getAllOpeaflowsApi.request(); - }, []); + const missingTraceIds = paginatedTraceList + .map((trace) => trace.trace_id) + .filter((traceId) => traceId && !traceSummaryMap[traceId] && !pendingSummaryIdsRef.current.has(traceId)) + + if (!missingTraceIds.length) return + + let isActive = true + missingTraceIds.forEach((traceId) => pendingSummaryIdsRef.current.add(traceId)) + + Promise.all( + missingTraceIds.map(async (traceId) => { + try { + const traceTree = await fetchTraceTreeData(traceId) + return { traceId, traceTree } + } catch { + return null + } + }) + ).then((results) => { + if (!isActive) return + + const nextSummaryMap = {} + const nextTraceTreeCache = {} + + results.forEach((result) => { + if (!result?.traceTree) return + + nextSummaryMap[result.traceId] = buildTraceSummary(result.traceTree) + nextTraceTreeCache[result.traceId] = result.traceTree + }) + + if (Object.keys(nextSummaryMap).length > 0) { + setTraceSummaryMap((prev) => ({ ...prev, ...nextSummaryMap })) + } + + if (Object.keys(nextTraceTreeCache).length > 0) { + setTraceTreeCache((prev) => ({ ...prev, ...nextTraceTreeCache })) + } + }).finally(() => { + missingTraceIds.forEach((traceId) => pendingSummaryIdsRef.current.delete(traceId)) + }) + + return () => { + isActive = false + } + }, [paginatedTraceList, traceSummaryMap]) useEffect(() => { - if (getAllOpeaflowsApi.data && ns) { - // Namespace is usually sandbox- - const flows = getAllOpeaflowsApi.data; - const found = flows.find(flow => `sandbox-${flow.id}` === ns); - setWorkflowName(found ? found.name : ''); + if (!selectedTraceId) { + setTraceData(null) + setSelectedSpan(null) + setTraceDetailsError(null) + return + } + + const cachedTrace = traceTreeCache[selectedTraceId] + if (cachedTrace) { + const flatSpans = flattenSpans(cachedTrace.spans || []) + setTraceData(cachedTrace) + setSelectedSpan((currentSelectedSpan) => { + if (currentSelectedSpan && flatSpans.some((span) => span.span_id === currentSelectedSpan.span_id)) { + return currentSelectedSpan + } + + return flatSpans[0] || null + }) + setTraceDetailsError(null) + setIsLoadingTraceDetails(false) + return } - }, [getAllOpeaflowsApi.data, ns]); + let isActive = true + + setIsLoadingTraceDetails(true) + setTraceDetailsError(null) + setTraceData(null) + setSelectedSpan(null) + + fetchTraceTreeData(selectedTraceId) + .then((traceTree) => { + if (!isActive) return + + const flatSpans = flattenSpans(traceTree.spans || []) + setTraceTreeCache((prev) => ({ ...prev, [selectedTraceId]: traceTree })) + setTraceSummaryMap((prev) => ({ + ...prev, + [selectedTraceId]: prev[selectedTraceId] || buildTraceSummary(traceTree) + })) + setTraceData(traceTree) + setSelectedSpan(flatSpans[0] || null) + }) + .catch((error) => { + if (!isActive) return + setTraceDetailsError(error.message || 'Failed to load trace details.') + }) + .finally(() => { + if (!isActive) return + setIsLoadingTraceDetails(false) + }) + + return () => { + isActive = false + } + }, [selectedTraceId, traceTreeCache, sandboxTracerTreeEndpoint, studioServerUrl]) + + const handleChangePage = (_event, newPage) => { + setPage(newPage) + } + + const handleChangeRowsPerPage = (event) => { + setRowsPerPage(parseInt(event.target.value, 10)) + setPage(0) + } + + const handleSearchChange = (event) => { + setSearchValue(event.target.value) + setPage(0) + } return ( - - - - + + + : } + sx={{ borderRadius: 2, height: 40 }} > - LLM Call Traces - - {workflowName && ( - - Workflow name: {workflowName} - - )} - - {traceList.length > 0 ? ( - <> - Traces: - - - - - Trace ID - Start - End - - - - {traceList.slice(page * rowsPerPage, page * rowsPerPage + rowsPerPage).map((trace) => ( - handleTraceSelect(trace.trace_id)}> - {trace.trace_id} - {trace.start} - {trace.end} + Refresh + + + + + + + + + + + + + {traceError && {traceError}} + + +
+ + + Trace ID + Root Span + Span Summary + Recorded At + Status + Actions + + + + {isLoadingTraces ? ( + + + + + + ) : paginatedTraceList.length === 0 ? ( + + + + {searchValue.trim() ? 'No traces match the current search.' : 'No traces found for this sandbox.'} + + + + ) : ( + paginatedTraceList.map((trace) => { + const traceSummary = traceSummaryMap[trace.trace_id] + + return ( + setSelectedTraceId(trace.trace_id)} + sx={{ + cursor: 'pointer', + bgcolor: + selectedTraceId === trace.trace_id + ? theme.palette.mode === 'dark' + ? 'grey.800' + : 'grey.100' + : 'inherit' + }} + > + + + {trace.trace_id} + + + + {traceSummary ? ( + + + {traceSummary.rootSpanCount > 1 + ? `${traceSummary.rootSpanName} +${traceSummary.rootSpanCount - 1} more` + : traceSummary.rootSpanName} + + + {traceSummary.services.length > 0 ? truncateText(traceSummary.services.join(', '), 50) : 'Service data unavailable'} + + + ) : ( + + Loading summary... + + )} + + + {traceSummary ? ( + + + {traceSummary.totalSpans} spans · {traceSummary.rootSpanCount} root spans + + + {traceSummary.llmCalls} LLM payloads · {formatDuration(traceSummary.durationNs)} + + + ) : ( + + )} + + + + + {formatDate(trace.start)} + + Ended: {formatDate(trace.end)} + + + + + + + + event.stopPropagation()}> + + setSelectedTraceId(trace.trace_id)}> + + + + - ))} - -
-
- - - ) : ( - No traces found + ) + }) + )} + + + + + {filteredTraceList.length > 0 && ( + )}
- {selectedTrace && ( - - - - Trace ID: {selectedTrace} - {traceData && traceData.spans ? ( -
- {traceData.spans.map((span) => ( - - ))} -
- ) : ( -

Loading trace data...

- )} + + setSelectedTraceId(null)} + sx={{ zIndex: (currentTheme) => currentTheme.zIndex.modal + 10 }} + PaperProps={{ + sx: { + width: { xs: '100%', sm: 680, md: 860 }, + p: 3, + bgcolor: theme.palette.mode === 'dark' ? 'grey.900' : 'background.paper' + } + }} + > + {selectedTraceId && ( + + + Trace Details + setSelectedTraceId(null)}> + + + + + + + + {isLoadingTraceDetails ? ( + + + + ) : traceDetailsError ? ( + {traceDetailsError} + ) : ( + + + + Trace Information + + + + + + + + + + + + + + + + + {selectedTraceSummary?.services?.length > 0 && ( + <> + + + + Services + + + {selectedTraceSummary.services.map((serviceName) => ( + + ))} + + + + )} + + + + + + + Span Execution Details ({selectedTraceSpans.length} spans) + + {selectedTraceSpans.length > 0 ? ( + + + + + Span + Service + Kind + Duration + Status + Payloads + + + + {selectedTraceSpans.map((span) => { + const spanStatus = getStatusPresentation(span.status_code) + const isSelected = selectedSpan?.span_id === span.span_id + + return ( + setSelectedSpan(span)} + sx={{ + cursor: 'pointer', + bgcolor: isSelected + ? theme.palette.mode === 'dark' + ? 'grey.800' + : 'grey.100' + : 'inherit' + }} + > + + + + #{span.span_id.slice(0, 8)} + + + {span.span_name} + + + + + + {span.service_name || '—'} + + + {span.span_kind || '—'} + {formatDuration(span.duration)} + + + + + {span.llm_input || span.llm_output ? : '—'} + + + ) + })} + +
+
+ ) : ( + No spans available for this trace. + )} +
+ + + + Span Details + + + {selectedSpan ? ( + + + + + + + + + + + + + + + + + + + ) : ( + Select a span to inspect its details. + )} + +
+ )} +
- {selectedSpan && ( - - - {selectedSpan.span_name} - Timestamp: {selectedSpan.timestmap}ms - Duration: {selectedSpan.duration}ms - Status: {selectedSpan.status_code} - Service: {selectedSpan.service_name} - LLM Input: {selectedSpan.llm_input} - LLM Output: {selectedSpan.llm_output} - - - )} -
- )} -
- ); + )} + + + ) } diff --git a/studio-frontend/packages/ui/vite.config.js b/studio-frontend/packages/ui/vite.config.js index a2591b2..921b6bd 100644 --- a/studio-frontend/packages/ui/vite.config.js +++ b/studio-frontend/packages/ui/vite.config.js @@ -9,12 +9,19 @@ export default defineConfig(async ({ mode }) => { const serverEnv = dotenv.config({ processEnv: {}, path: '../server/.env' }).parsed const serverHost = serverEnv?.['HOST'] ?? 'localhost' const serverPort = parseInt(serverEnv?.['PORT'] ?? '3000') + const studioBackendTarget = process.env.STUDIO_SERVER_URL ?? 'http://localhost:5001' proxy = { '/api': { target: `http://${serverHost}:${serverPort}`, changeOrigin: true, secure: false }, + '/studio-backend': { + target: studioBackendTarget, + changeOrigin: true, + ws: true, + secure: false + }, '/socket.io': { target: `http://${serverHost}:${serverPort}`, changeOrigin: true, @@ -39,7 +46,10 @@ export default defineConfig(async ({ mode }) => { open: true, proxy, port: process.env.VITE_PORT ?? 8088, - host: process.env.VITE_HOST ?? '0.0.0.0' + host: process.env.VITE_HOST ?? '0.0.0.0', + watch: { + usePolling: true + } } } }) diff --git a/studio-frontend/turbo.json b/studio-frontend/turbo.json index 010a193..a12dd0c 100644 --- a/studio-frontend/turbo.json +++ b/studio-frontend/turbo.json @@ -1,5 +1,21 @@ { "$schema": "https://turbo.build/schema.json", + "globalEnv": [ + "DATABASE_TYPE", + "DATABASE_HOST", + "DATABASE_PORT", + "DATABASE_NAME", + "DATABASE_USER", + "DATABASE_PASSWORD", + "DATABASE_SSL", + "DATABASE_SSL_KEY_BASE64", + "FINETUNING_SERVICE_DNS", + "FINETUNING_RAY_DNS", + "STUDIO_SERVER_URL", + "PREPARE_DOC_REDIS_PREP_DNS", + "LOG_LEVEL", + "LOG_PATH" + ], "tasks": { "build": { "dependsOn": ["^build"], From ecf21d6ea649107ba83c2965ca06f17e5cd7c2fd Mon Sep 17 00:00:00 2001 From: wwanarif Date: Wed, 22 Apr 2026 09:54:46 +0000 Subject: [PATCH 4/9] implement eval into k8 cluster Signed-off-by: wwanarif --- .../buildpush-genaistudio-images.yml | 4 + setup-scripts/setup-development/.env.example | 59 ++ .../docker-compose.local.yml | 10 +- .../setup-genai-studio/genai-studio.yml | 3 + .../helm-values/mysqldb.yaml | 1 + .../manifests/ollama-manifest.yaml | 95 +++ .../manifests/studio-manifest-aws-ecr.yaml | 2 +- .../manifests/studio-manifest.yaml | 71 +- .../playbooks/deploy-ollama.yml | 29 + .../setup-genai-studio/studio-config.yaml | 6 +- .../app/routers/evaluation_router.py | 9 +- studio-eval/Dockerfile | 2 + studio-eval/app/compat.py | 99 ++- studio-eval/app/db/schema.py | 21 +- studio-eval/app/main.py | 60 ++ studio-eval/app/models/eval_models.py | 202 ++++- studio-eval/app/routers/eval_router.py | 164 +++- studio-eval/app/services/dataset_service.py | 469 ++++++++++- studio-eval/app/services/eval_service.py | 113 ++- studio-eval/app/services/ollama_service.py | 94 ++- .../app/services/response_collector.py | 2 +- studio-eval/requirements.txt | 10 +- studio-eval/tests/test_eval_service.py | 196 ++++- studio-eval/tests/test_timeout_config.py | 14 +- studio-frontend/Dockerfile | 2 + studio-frontend/docker-compose.dev.yml | 47 -- studio-frontend/packages/server/package.json | 2 + .../server/src/routes/evaluation/index.ts | 80 +- .../server/src/services/chatflows/index.ts | 52 +- .../src/services/evaluationUploads/index.ts | 173 ++++ .../packages/ui/src/api/evaluation.js | 20 + .../src/ui-component/file/FileUploadArea.jsx | 325 ++++++++ .../src/views/evaluation/CreateRunModal.jsx | 2 +- .../views/evaluation/DatasetDetailDialog.jsx | 769 ++++++++++++++++++ .../ui/src/views/evaluation/DatasetTab.jsx | 745 ++++++++++++++--- .../ui/src/views/evaluation/ExecutionTab.jsx | 196 ++++- .../src/views/evaluation/RunDetailsModal.jsx | 553 +++++++++---- .../src/views/finetuning/FileUploadArea.jsx | 18 +- .../views/finetuning/FinetuningJobModal.jsx | 2 +- studio-frontend/pnpm-lock.yaml | 118 +++ 40 files changed, 4296 insertions(+), 543 deletions(-) create mode 100644 setup-scripts/setup-development/.env.example create mode 100644 setup-scripts/setup-genai-studio/manifests/ollama-manifest.yaml create mode 100644 setup-scripts/setup-genai-studio/playbooks/deploy-ollama.yml delete mode 100644 studio-frontend/docker-compose.dev.yml create mode 100644 studio-frontend/packages/server/src/services/evaluationUploads/index.ts create mode 100644 studio-frontend/packages/ui/src/ui-component/file/FileUploadArea.jsx create mode 100644 studio-frontend/packages/ui/src/views/evaluation/DatasetDetailDialog.jsx diff --git a/setup-scripts/build-image-to-registry/buildpush-genaistudio-images.yml b/setup-scripts/build-image-to-registry/buildpush-genaistudio-images.yml index 20839a2..c3ded6a 100755 --- a/setup-scripts/build-image-to-registry/buildpush-genaistudio-images.yml +++ b/setup-scripts/build-image-to-registry/buildpush-genaistudio-images.yml @@ -10,6 +10,7 @@ --build-arg http_proxy="{{ http_proxy }}" --build-arg https_proxy="{{ http_proxy }}" --build-arg no_proxy="{{ no_proxy }}" + --build-context sample_workflows="../sample-workflows" -t "{{ container_registry }}/{{ item.image_name }}:{{ container_tag }}" . args: chdir: "{{ item.directory }}" @@ -20,6 +21,7 @@ loop: - { directory: '../../studio-frontend/', image_name: 'studio-frontend' } - { directory: '../../studio-backend/', image_name: 'studio-backend' } + - { directory: '../../studio-eval/', image_name: 'studio-eval' } - { directory: '../../app-frontend/', image_name: 'app-frontend' } - { directory: '../../app-backend/', image_name: 'app-backend' } register: build_results @@ -33,6 +35,7 @@ loop: - { image_name: 'studio-frontend' } - { image_name: 'studio-backend' } + - { image_name: 'studio-eval' } - { image_name: 'app-frontend' } - { image_name: 'app-backend' } @@ -42,5 +45,6 @@ loop: - { image_name: 'studio-frontend' } - { image_name: 'studio-backend' } + - { image_name: 'studio-eval' } - { image_name: 'app-frontend' } - { image_name: 'app-backend' } \ No newline at end of file diff --git a/setup-scripts/setup-development/.env.example b/setup-scripts/setup-development/.env.example new file mode 100644 index 0000000..190e2e4 --- /dev/null +++ b/setup-scripts/setup-development/.env.example @@ -0,0 +1,59 @@ +# ============================================================================= +# GenAI Studio — Local Development Environment +# docker compose -f docker-compose.local.yml reads this file automatically +# Copy this file to .env and adjust values for your environment +# ============================================================================= + +# --- Proxy (leave blank if not behind a proxy) --- +http_proxy= +https_proxy= +no_proxy=localhost,127.0.0.1,mysql,studio-backend,studio-eval,ollama,finetuning-server + +# --- Container images for sandbox deployments --- +APP_FRONTEND_IMAGE=opea/app-frontend:latest +APP_BACKEND_IMAGE=opea/app-backend:latest + +# --- MySQL (shared by studio-frontend and studio-eval) --- +MYSQL_HOST=mysql +MYSQL_PORT=3306 +MYSQL_ROOT_PASSWORD=password +MYSQL_FLOWISE_DB=flowise +MYSQL_EVAL_DB=eval +MYSQL_USER=root +MYSQL_PASSWORD=password + +# --- Service endpoints --- +# studio-frontend server-side code runs inside Docker, so it should use Docker DNS. +# The browser-side UI should use the Vite /studio-backend proxy on port 8088 +STUDIO_SERVER_URL=http://studio-backend:5000 + +# studio-frontend UI dev flags +NODE_TLS_REJECT_UNAUTHORIZED=0 +NODE_ENV=development +VITE_DISABLE_KEYCLOAK=true +VITE_HOST=0.0.0.0 +VITE_PORT=8088 + +# studio-backend -> studio-eval (host:port, no scheme) +EVAL_SERVICE_DNS=studio-eval:8000 + +# Local compose: skip Grafana dashboard import/delete during sandbox lifecycle. +DISABLE_GRAFANA_DASHBOARD_SYNC=true + +# --- Sandbox routing from local-nginx to K8s namespaces --- +# CoreDNS ClusterIP of your cluster (kubeadm commonly 10.96.0.10, k3s 10.43.0.10) +K8S_DNS_RESOLVER=10.96.0.10 + +# studio-eval -> Ollama (host:port, no scheme) +OLLAMA_DNS=ollama:11434 + +# --- Finetuning service --- +# host:port, no scheme +FINETUNING_SERVICE_DNS=finetuning-server:8015 +FINETUNING_RAY_DNS=finetuning-server:8265 + +# studio-eval -> sandbox app-backend via kubectl port-forward on host. +# This mirrors app-backend behavior while bypassing local Docker->K8s DNS issues. +# Ensure you have `kubectl -n port-forward --address service/app-backend 18899:8899` running on your host. +# To get docker bridge IP: docker network inspect bridge --format '{{range .IPAM.Config}}{{.Gateway}}{{end}}' +APP_BACKEND_URL_TEMPLATE=http://host.docker.internal:18899/v1/app-backend diff --git a/setup-scripts/setup-development/docker-compose.local.yml b/setup-scripts/setup-development/docker-compose.local.yml index 9a00b4f..4647296 100644 --- a/setup-scripts/setup-development/docker-compose.local.yml +++ b/setup-scripts/setup-development/docker-compose.local.yml @@ -9,6 +9,8 @@ services: image: studio-frontend:local build: context: ../../studio-frontend + additional_contexts: + sample_workflows: ../../sample-workflows target: builder args: http_proxy: ${http_proxy} @@ -42,7 +44,7 @@ services: - DATABASE_PORT=${MYSQL_PORT:-3306} - DATABASE_NAME=${MYSQL_FLOWISE_DB:-flowise} - DATABASE_USER=${MYSQL_USER:-root} - - DATABASE_PASSWORD=${MYSQL_PASSWORD:-password} + - DATABASE_PASSWORD=${MYSQL_PASSWORD:-root} ulimits: nofile: soft: 65536 @@ -70,7 +72,7 @@ services: - ${HOME}/.kube:/root/.kube:ro command: ["uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "5000", "--reload"] environment: - - EVAL_SERVICE_DNS=${EVAL_SERVICE_DNS:-studio-eval:8000} + - STUDIO_EVAL_DNS=${STUDIO_EVAL_DNS:-studio-eval:8000} - GRAFANA_DNS=${GRAFANA_DNS:-grafana:3000} - DISABLE_GRAFANA_DASHBOARD_SYNC=${DISABLE_GRAFANA_DASHBOARD_SYNC:-true} - CLICKHOUSE_DNS=${CLICKHOUSE_DNS:-clickhouse:9000} @@ -141,7 +143,7 @@ services: - studio-local # Pull models after startup (OLLAMA_HOST must be overridden for docker exec to work): # docker exec -e OLLAMA_HOST=http://127.0.0.1:11434 ollama ollama pull nomic-embed-text - # docker exec -e OLLAMA_HOST=http://127.0.0.1:11434 ollama ollama pull llama3 + # docker exec -e OLLAMA_HOST=http://127.0.0.1:11434 ollama ollama pull qwen2.5:14b restart: unless-stopped mysql: @@ -170,7 +172,7 @@ services: # Local equivalent of studio-nginx in studio-manifest.yaml. # Routes /?ns= to app-frontend in the deployed K8s sandbox, # and everything else to studio-frontend. - # Access the studio via http://localhost:30010 (not localhost:3000) so that + # Access the studio via http://localhost:30010 (not localhost:8088) so that # the ?ns= routing works correctly when opening sandbox app-frontends. local-nginx: image: nginx:1.27.1 diff --git a/setup-scripts/setup-genai-studio/genai-studio.yml b/setup-scripts/setup-genai-studio/genai-studio.yml index 0c86d5b..ac9d484 100644 --- a/setup-scripts/setup-genai-studio/genai-studio.yml +++ b/setup-scripts/setup-genai-studio/genai-studio.yml @@ -16,5 +16,8 @@ - name: Deploy tracing import_playbook: playbooks/deploy-tracing.yml +- name: Deploy Ollama + import_playbook: playbooks/deploy-ollama.yml + - name: Deploy studio import_playbook: playbooks/deploy-studio.yml \ No newline at end of file diff --git a/setup-scripts/setup-genai-studio/helm-values/mysqldb.yaml b/setup-scripts/setup-genai-studio/helm-values/mysqldb.yaml index 9abe0c2..e3b1727 100644 --- a/setup-scripts/setup-genai-studio/helm-values/mysqldb.yaml +++ b/setup-scripts/setup-genai-studio/helm-values/mysqldb.yaml @@ -42,6 +42,7 @@ initdbScripts: -- Create databases for GenAIStudio CREATE DATABASE IF NOT EXISTS keycloak; CREATE DATABASE IF NOT EXISTS studio; + CREATE DATABASE IF NOT EXISTS eval; -- Create the Chinook database CREATE DATABASE IF NOT EXISTS Chinook; diff --git a/setup-scripts/setup-genai-studio/manifests/ollama-manifest.yaml b/setup-scripts/setup-genai-studio/manifests/ollama-manifest.yaml new file mode 100644 index 0000000..db0de87 --- /dev/null +++ b/setup-scripts/setup-genai-studio/manifests/ollama-manifest.yaml @@ -0,0 +1,95 @@ +--- +apiVersion: v1 +kind: Namespace +metadata: + name: ollama +--- +apiVersion: v1 +kind: PersistentVolumeClaim +metadata: + name: ollama-models + namespace: ollama +spec: + accessModes: + - ReadWriteOnce + storageClassName: local-path + resources: + requests: + storage: 100Gi +--- +apiVersion: v1 +kind: Service +metadata: + name: ollama + namespace: ollama +spec: + type: ClusterIP + ports: + - protocol: TCP + port: 11434 + targetPort: 11434 + selector: + app: ollama +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: ollama + namespace: ollama + labels: + app: ollama +spec: + replicas: 1 + selector: + matchLabels: + app: ollama + template: + metadata: + labels: + app: ollama + spec: + containers: + - name: ollama + image: ollama/ollama:latest + imagePullPolicy: IfNotPresent + ports: + - containerPort: 11434 + env: + - name: OLLAMA_HOST + value: "0.0.0.0" + - name: HTTP_PROXY + value: "${HTTP_PROXY}" + - name: HTTPS_PROXY + value: "${HTTP_PROXY}" + - name: NO_PROXY + value: "${NO_PROXY}" + resources: + requests: + memory: "12Gi" + cpu: "2" + limits: + memory: "32Gi" + cpu: "8" + volumeMounts: + - name: ollama-models + mountPath: /root/.ollama + livenessProbe: + httpGet: + path: / + port: 11434 + initialDelaySeconds: 30 + periodSeconds: 30 + timeoutSeconds: 10 + failureThreshold: 3 + readinessProbe: + httpGet: + path: / + port: 11434 + initialDelaySeconds: 15 + periodSeconds: 10 + timeoutSeconds: 5 + failureThreshold: 3 + volumes: + - name: ollama-models + persistentVolumeClaim: + claimName: ollama-models diff --git a/setup-scripts/setup-genai-studio/manifests/studio-manifest-aws-ecr.yaml b/setup-scripts/setup-genai-studio/manifests/studio-manifest-aws-ecr.yaml index 988d03b..1304f05 100644 --- a/setup-scripts/setup-genai-studio/manifests/studio-manifest-aws-ecr.yaml +++ b/setup-scripts/setup-genai-studio/manifests/studio-manifest-aws-ecr.yaml @@ -270,7 +270,7 @@ metadata: rules: - apiGroups: [""] resources: ["namespaces"] - verbs: ["get", "create", "delete"] + verbs: ["get", "list", "watch", "create", "delete"] - apiGroups: [""] resources: ["services"] verbs: ["get", "create", "list"] diff --git a/setup-scripts/setup-genai-studio/manifests/studio-manifest.yaml b/setup-scripts/setup-genai-studio/manifests/studio-manifest.yaml index d2e2f5b..b924952 100644 --- a/setup-scripts/setup-genai-studio/manifests/studio-manifest.yaml +++ b/setup-scripts/setup-genai-studio/manifests/studio-manifest.yaml @@ -305,7 +305,7 @@ metadata: rules: - apiGroups: [""] resources: ["namespaces"] - verbs: ["get", "create", "delete"] + verbs: ["get", "list", "watch", "create", "delete"] - apiGroups: [""] resources: ["services"] verbs: ["get", "create", "list"] @@ -712,4 +712,71 @@ spec: volumes: - name: shared-output-volume persistentVolumeClaim: - claimName: shared-finetuning-output \ No newline at end of file + claimName: shared-finetuning-output +--- +apiVersion: v1 +kind: Service +metadata: + name: studio-eval + namespace: studio +spec: + type: ClusterIP + ports: + - protocol: TCP + port: 8000 + targetPort: 8000 + selector: + app: studio-eval +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: studio-eval + namespace: studio + labels: + app: studio-eval +spec: + replicas: 1 + selector: + matchLabels: + app: studio-eval + template: + metadata: + labels: + app: studio-eval + spec: + containers: + - name: studio-eval + image: "${REGISTRY}/studio-eval:${TAG}" + imagePullPolicy: Always + ports: + - containerPort: 8000 + envFrom: + - configMapRef: + name: studio-config + env: + - name: MYSQL_DB + value: "eval" + - name: MYSQL_HOST + value: ${MYSQL_HOST} + - name: MYSQL_PORT + value: "3306" + - name: MYSQL_USER + value: "studio" + - name: MYSQL_PASSWORD + value: "studio" + resources: + requests: + memory: "512Mi" + cpu: "500m" + limits: + memory: "2Gi" + cpu: "2" + livenessProbe: + httpGet: + path: /health + port: 8000 + initialDelaySeconds: 60 + periodSeconds: 30 + timeoutSeconds: 10 + failureThreshold: 3 \ No newline at end of file diff --git a/setup-scripts/setup-genai-studio/playbooks/deploy-ollama.yml b/setup-scripts/setup-genai-studio/playbooks/deploy-ollama.yml new file mode 100644 index 0000000..d1617bd --- /dev/null +++ b/setup-scripts/setup-genai-studio/playbooks/deploy-ollama.yml @@ -0,0 +1,29 @@ +--- +- name: Deploy Ollama in ollama namespace + hosts: localhost + vars_files: + - ../vars.yml + tasks: + - name: Check if ollama namespace exists + shell: kubectl get namespace ollama --ignore-not-found + register: namespace_check + changed_when: false + + - name: End playbook if ollama namespace exists + meta: end_play + when: namespace_check.stdout != "" + + - name: Apply ollama manifest + shell: "envsubst '${HTTP_PROXY} ${NO_PROXY}' < ../manifests/ollama-manifest.yaml | kubectl apply -f -" + environment: + HTTP_PROXY: "{{ http_proxy }}" + NO_PROXY: "{{ no_proxy }}" + + - name: Wait for ollama pod to be ready + command: kubectl wait --namespace ollama --for=condition=ready pod -l app=ollama --timeout=300s + + - name: Pre-pull nomic-embed-text model + shell: kubectl exec -n ollama $(kubectl get pod -n ollama -l app=ollama -o jsonpath="{.items[0].metadata.name}") -- env OLLAMA_HOST=http://127.0.0.1:11434 HTTP_PROXY={{ http_proxy }} HTTPS_PROXY={{ http_proxy }} NO_PROXY={{ no_proxy }} ollama pull nomic-embed-text + + - name: Pre-pull qwen2.5:14b model + shell: kubectl exec -n ollama $(kubectl get pod -n ollama -l app=ollama -o jsonpath="{.items[0].metadata.name}") -- env OLLAMA_HOST=http://127.0.0.1:11434 HTTP_PROXY={{ http_proxy }} HTTPS_PROXY={{ http_proxy }} NO_PROXY={{ no_proxy }} ollama pull qwen2.5:14b \ No newline at end of file diff --git a/setup-scripts/setup-genai-studio/studio-config.yaml b/setup-scripts/setup-genai-studio/studio-config.yaml index e876692..b87974c 100644 --- a/setup-scripts/setup-genai-studio/studio-config.yaml +++ b/setup-scripts/setup-genai-studio/studio-config.yaml @@ -7,6 +7,8 @@ data: KC_ASSETS_GIT_URL: "https://github.com/opea-project/GenAIStudio/tree/main/assets/keycloak" TELEMETRY_ENDPOINT: "http://tracing-opentelemetry-collector.tracing.svc.cluster.local:4318/v1/traces" CLICKHOUSE_DNS: "clickhouse.tracing.svc.cluster.local:9000" + OLLAMA_DNS: "ollama.ollama.svc.cluster.local:11434" + MYSQL_HOST: "mysql.mysql.svc.cluster.local" KEYCLOAK_DNS: "keycloak.studio.svc.cluster.local:8443" GRAFANA_DNS: "kube-prometheus-stack-grafana.monitoring.svc.cluster.local" STUDIO_FRONTEND_DNS: "studio-frontend.studio.svc.cluster.local:3000" @@ -15,4 +17,6 @@ data: APP_CHATHISTORY_DNS: "chathistory-mongo.$namespace.svc.cluster.local:6012" PREPARE_DOC_REDIS_PREP_DNS: "opea-prepare-doc-redis-prep-0.$namespace.svc.cluster.local:6007" STUDIO_BACKEND_DNS: "studio-backend.studio.svc.cluster.local:5000" - FINETUNING_HOST: "finetuning-server.studio.svc.cluster.local" \ No newline at end of file + FINETUNING_SERVICE_DNS: "finetuning-server.studio.svc.cluster.local:8015" + FINETUNING_RAY_DNS: "finetuning-server.studio.svc.cluster.local:8265" + STUDIO_EVAL_DNS: "studio-eval.studio.svc.cluster.local:8000" \ No newline at end of file diff --git a/studio-backend/app/routers/evaluation_router.py b/studio-backend/app/routers/evaluation_router.py index 0ea782d..b549f1b 100644 --- a/studio-backend/app/routers/evaluation_router.py +++ b/studio-backend/app/routers/evaluation_router.py @@ -4,8 +4,8 @@ from fastapi import APIRouter, HTTPException, Request from fastapi.responses import Response -EVAL_SERVICE_DNS = os.getenv( - "EVAL_SERVICE_DNS", +STUDIO_EVAL_DNS = os.getenv( + "STUDIO_EVAL_DNS", "studio-eval-service.studio.svc.cluster.local:8000", ) @@ -14,7 +14,8 @@ @router.api_route("/evaluation/{path:path}", methods=["GET", "POST", "PUT", "PATCH", "DELETE", "OPTIONS", "HEAD"]) async def proxy_evaluation(path: str, request: Request): - target_url = f"http://{EVAL_SERVICE_DNS}/eval/{path}" + target_url = f"http://{STUDIO_EVAL_DNS}/eval/{path}" + upstream_timeout = 60.0 query = request.url.query if query: target_url = f"{target_url}?{query}" @@ -27,7 +28,7 @@ async def proxy_evaluation(path: str, request: Request): body = await request.body() try: - async with httpx.AsyncClient(timeout=60.0) as client: + async with httpx.AsyncClient(timeout=upstream_timeout) as client: upstream = await client.request( method=request.method, url=target_url, diff --git a/studio-eval/Dockerfile b/studio-eval/Dockerfile index 2a41de8..76fad51 100644 --- a/studio-eval/Dockerfile +++ b/studio-eval/Dockerfile @@ -7,6 +7,8 @@ ARG no_proxy ENV http_proxy=${http_proxy} ENV https_proxy=${https_proxy} ENV no_proxy=${no_proxy} +ENV DEEPEVAL_TELEMETRY_OPT_OUT=YES +ENV DEEPEVAL_UPDATE_WARNING_OPT_OUT=YES WORKDIR /app diff --git a/studio-eval/app/compat.py b/studio-eval/app/compat.py index eae1b90..0803fa6 100644 --- a/studio-eval/app/compat.py +++ b/studio-eval/app/compat.py @@ -1,46 +1,85 @@ -"""Compatibility shims for third-party libraries. +"""Compatibility shims for third-party libraries.""" -This service uses Pydantic v1, while some third-party packages imported at -runtime expect small parts of the Pydantic v2 API surface. Keep the shim local -and minimal so the rest of the app can continue using explicit Pydantic v1 -syntax. -""" +import os -import pydantic -from pydantic import root_validator, validator +import pydantic.config def ensure_pydantic_v2_apis() -> None: - """Provide a small subset of Pydantic v2 APIs on top of Pydantic v1. + """Set DeepEval opt-out env vars and apply third-party compatibility patches. - DeepEval currently imports ``field_validator`` and ``model_validator`` from - ``pydantic``. Those names do not exist in Pydantic v1, so add compatible - wrappers before importing DeepEval. + DeepEval performs a PyPI version check and telemetry IP lookup at import + time. Default those opt-outs to YES so evaluation and synthesis do not + wait on external DeepEval calls unless a deployment explicitly opts back in. """ + os.environ.setdefault("DEEPEVAL_TELEMETRY_OPT_OUT", "YES") + os.environ.setdefault("DEEPEVAL_UPDATE_WARNING_OPT_OUT", "YES") - if not hasattr(pydantic, "field_validator"): - def field_validator(*fields, mode="after", **kwargs): - pre = mode == "before" - return validator(*fields, pre=pre, allow_reuse=True, **kwargs) + # DeepEval Synthesizer imports ExtraValues from pydantic.config. This + # exists in Pydantic v2 but guard defensively in case of version skew. + if not hasattr(pydantic.config, "ExtraValues"): + from typing import Literal + pydantic.config.ExtraValues = Literal["allow", "ignore", "forbid"] # type: ignore[attr-defined] - pydantic.field_validator = field_validator # type: ignore[attr-defined] + # DeepEval imports InvalidCollectionException from chromadb.errors, but + # newer chromadb versions expose NotFoundError instead when a collection + # is missing. Alias the exact runtime exception so DeepEval's except block + # still catches a missing collection and creates it. + try: + import chromadb.errors as _ce + if not hasattr(_ce, "InvalidCollectionException"): + if hasattr(_ce, "NotFoundError"): + _ce.InvalidCollectionException = _ce.NotFoundError # type: ignore[attr-defined] + else: + class InvalidCollectionException(_ce.ChromaError): # type: ignore[misc] + pass + _ce.InvalidCollectionException = InvalidCollectionException # type: ignore[attr-defined] + except ImportError: + pass - if not hasattr(pydantic, "model_validator"): - def model_validator(*, mode="after", **kwargs): - pre = mode == "before" - def decorator(func): - return root_validator(pre=pre, allow_reuse=True, **kwargs)(func) +def patch_deepeval_document_chunker() -> None: + """Patch DeepEval's DocumentChunker to collapse dot-leaders before chunking. - return decorator + PDF table-of-contents lines like "Chapter 1 ............... 5" produce + hundreds of single-dot tokens that overflow the embedding context window. + Collapsing runs of 4+ dots down to "..." prevents the overflow and avoids + the downstream ChromaDB "Collection does not exist" error that results from + a failed chunking call. + """ + import re + + try: + from deepeval.synthesizer.chunking import doc_chunker + except ImportError: + return + + Chunker = doc_chunker.DocumentChunker + if getattr(Chunker, "_dot_leader_patch_applied", False): + return + + dot_pattern = re.compile(r"\.{4,}") + + original_load = Chunker.load_doc + original_aload = Chunker.a_load_doc - pydantic.model_validator = model_validator # type: ignore[attr-defined] + def _process(chunker_instance) -> None: + if not chunker_instance.sections: + return + for section in chunker_instance.sections: + section.page_content = dot_pattern.sub("...", section.page_content) + chunker_instance.text_token_count = chunker_instance.count_tokens( + chunker_instance.sections + ) - if not hasattr(pydantic, "ConfigDict"): - pydantic.ConfigDict = dict # type: ignore[attr-defined] + def patched_load_doc(self, path: str): # type: ignore[override] + original_load(self, path) + _process(self) - if not hasattr(pydantic.BaseModel, "model_dump"): - pydantic.BaseModel.model_dump = pydantic.BaseModel.dict # type: ignore[attr-defined] + async def patched_a_load_doc(self, path: str): # type: ignore[override] + await original_aload(self, path) + _process(self) - if not hasattr(pydantic.BaseModel, "model_copy"): - pydantic.BaseModel.model_copy = pydantic.BaseModel.copy # type: ignore[attr-defined] + Chunker.load_doc = patched_load_doc # type: ignore[method-assign] + Chunker.a_load_doc = patched_a_load_doc # type: ignore[method-assign] + Chunker._dot_leader_patch_applied = True # type: ignore[attr-defined] diff --git a/studio-eval/app/db/schema.py b/studio-eval/app/db/schema.py index a2461a6..deb9146 100644 --- a/studio-eval/app/db/schema.py +++ b/studio-eval/app/db/schema.py @@ -20,9 +20,15 @@ class EvalDataset(Base): __tablename__ = "eval_datasets" - id = Column(Integer, primary_key=True, index=True) + id = Column(String(36), primary_key=True, index=True, default=lambda: str(uuid.uuid4())) name = Column(String(255), nullable=False) description = Column(Text, nullable=True) + status = Column(String(50), nullable=False, default="completed") # pending/synthesizing/completed/failed/stopped + error = Column(Text, nullable=True) + completed_contexts = Column(Integer, nullable=True) + total_contexts = Column(Integer, nullable=True) + completed_goldens = Column(Integer, nullable=True) + target_goldens = Column(Integer, nullable=True) created_at = Column(DateTime, default=datetime.utcnow) updated_at = Column(DateTime, default=datetime.utcnow, onupdate=datetime.utcnow) @@ -31,14 +37,14 @@ class EvalDataset(Base): back_populates="dataset", cascade="all, delete-orphan", ) - runs = relationship("EvalRun", back_populates="dataset") + runs = relationship("EvalRun", back_populates="dataset", passive_deletes=True) class EvalDatasetEntry(Base): __tablename__ = "eval_dataset_entries" id = Column(Integer, primary_key=True, index=True) - dataset_id = Column(Integer, ForeignKey("eval_datasets.id"), nullable=False) + dataset_id = Column(String(36), ForeignKey("eval_datasets.id"), nullable=False) input = Column(Text, nullable=False) expected_output = Column(Text, nullable=True) context = Column(JSON, nullable=True) # list[str] @@ -53,11 +59,13 @@ class EvalRun(Base): id = Column(String(36), primary_key=True, index=True, default=lambda: str(uuid.uuid4())) name = Column(String(255), nullable=False) - dataset_id = Column(Integer, ForeignKey("eval_datasets.id"), nullable=False) + dataset_id = Column(String(36), ForeignKey("eval_datasets.id", ondelete="SET NULL"), nullable=True) sandbox_id = Column(String(255), nullable=False) model_name = Column(String(255), nullable=False) metrics = Column(JSON, nullable=False) # list[str] - status = Column(String(50), default="pending") # pending/running/completed/failed + status = Column(String(50), default="pending") # pending/running/completed/failed/stopped + completed_count = Column(Integer, nullable=True) + total_count = Column(Integer, nullable=True) created_at = Column(DateTime, default=datetime.utcnow) completed_at = Column(DateTime, nullable=True) error = Column(Text, nullable=True) @@ -67,6 +75,9 @@ class EvalRun(Base): max_tokens = Column(Integer, nullable=True) request_model = Column(String(255), nullable=True) configuration_snapshot = Column(JSON, nullable=True) + dataset_name_snapshot = Column(String(255), nullable=True) + dataset_entries_snapshot = Column(JSON, nullable=True) + # shape: [{"id": int, "input": str, "expected_output": str|null, "context": list[str]|null}, ...] dataset = relationship("EvalDataset", back_populates="runs") results = relationship( diff --git a/studio-eval/app/main.py b/studio-eval/app/main.py index 4803cc4..9d8dff6 100644 --- a/studio-eval/app/main.py +++ b/studio-eval/app/main.py @@ -15,6 +15,53 @@ logger = logging.getLogger(__name__) +def _migrate_eval_datasets_table() -> None: + inspector = inspect(engine) + if "eval_datasets" not in inspector.get_table_names(): + return + + columns = inspector.get_columns("eval_datasets") + id_col = next((c for c in columns if c["name"] == "id"), None) + if id_col and "int" in str(id_col["type"]).lower(): + logger.info("Detected schema change: migrating eval_datasets table from Integer to UUID id — dropping all tables…") + Base.metadata.drop_all(bind=engine) + logger.info("Dropped old tables") + return + + existing = {c["name"] for c in columns} + with engine.begin() as connection: + if "status" not in existing: + logger.info("Adding missing status column to eval_datasets…") + connection.execute( + text("ALTER TABLE eval_datasets ADD COLUMN status VARCHAR(50) NOT NULL DEFAULT 'completed'") + ) + if "error" not in existing: + logger.info("Adding missing error column to eval_datasets…") + connection.execute( + text("ALTER TABLE eval_datasets ADD COLUMN error TEXT NULL") + ) + if "completed_contexts" not in existing: + logger.info("Adding missing completed_contexts column to eval_datasets…") + connection.execute( + text("ALTER TABLE eval_datasets ADD COLUMN completed_contexts INT NULL") + ) + if "total_contexts" not in existing: + logger.info("Adding missing total_contexts column to eval_datasets…") + connection.execute( + text("ALTER TABLE eval_datasets ADD COLUMN total_contexts INT NULL") + ) + if "completed_goldens" not in existing: + logger.info("Adding missing completed_goldens column to eval_datasets…") + connection.execute( + text("ALTER TABLE eval_datasets ADD COLUMN completed_goldens INT NULL") + ) + if "target_goldens" not in existing: + logger.info("Adding missing target_goldens column to eval_datasets…") + connection.execute( + text("ALTER TABLE eval_datasets ADD COLUMN target_goldens INT NULL") + ) + + def _migrate_eval_runs_table() -> None: inspector = inspect(engine) if "eval_runs" not in inspector.get_table_names(): @@ -36,6 +83,18 @@ def _migrate_eval_runs_table() -> None: text("ALTER TABLE eval_runs ADD COLUMN configuration_snapshot JSON NULL") ) + with engine.begin() as connection: + if "completed_count" not in column_names: + logger.info("Adding missing completed_count column to eval_runs…") + connection.execute( + text("ALTER TABLE eval_runs ADD COLUMN completed_count INT NULL") + ) + if "total_count" not in column_names: + logger.info("Adding missing total_count column to eval_runs…") + connection.execute( + text("ALTER TABLE eval_runs ADD COLUMN total_count INT NULL") + ) + app = FastAPI( title="Studio Eval Service", description=( @@ -56,6 +115,7 @@ async def startup() -> None: delay = 3 for attempt in range(1, retries + 1): try: + _migrate_eval_datasets_table() _migrate_eval_runs_table() Base.metadata.create_all(bind=engine) logger.info("Database tables ready") diff --git a/studio-eval/app/models/eval_models.py b/studio-eval/app/models/eval_models.py index 71d32e0..6216196 100644 --- a/studio-eval/app/models/eval_models.py +++ b/studio-eval/app/models/eval_models.py @@ -1,7 +1,7 @@ from datetime import datetime from typing import Any, Dict, List, Optional, Set -from pydantic import BaseModel, validator +from pydantic import BaseModel, ConfigDict, field_validator # --------------------------------------------------------------------------- # Supported DeepEval metrics @@ -25,16 +25,15 @@ class EvalDatasetEntryCreate(BaseModel): class EvalDatasetEntryResponse(BaseModel): + model_config = ConfigDict(from_attributes=True) + id: int - dataset_id: int + dataset_id: str input: str expected_output: Optional[str] = None context: Optional[List[str]] = None created_at: datetime - class Config: - orm_mode = True - # --------------------------------------------------------------------------- # Dataset @@ -47,29 +46,66 @@ class EvalDatasetCreate(BaseModel): entries: Optional[List[EvalDatasetEntryCreate]] = [] +class EvalDatasetUpdate(BaseModel): + name: Optional[str] = None + description: Optional[str] = None + + +class EvalDatasetEntryUpdate(BaseModel): + input: Optional[str] = None + expected_output: Optional[str] = None + context: Optional[List[str]] = None + + +class AddEntriesRequest(BaseModel): + entries: List[EvalDatasetEntryCreate] + + @field_validator("entries") + @classmethod + def entries_not_empty(cls, v): + if not v: + raise ValueError("entries must not be empty") + return v + + +class AddEntriesResponse(BaseModel): + added: int + entries: List[EvalDatasetEntryResponse] + + class EvalDatasetResponse(BaseModel): - id: int + model_config = ConfigDict(from_attributes=True) + + id: str name: str description: Optional[str] = None + status: str = "completed" + error: Optional[str] = None + completed_contexts: Optional[int] = None + total_contexts: Optional[int] = None + completed_goldens: Optional[int] = None + target_goldens: Optional[int] = None created_at: datetime updated_at: datetime entries: List[EvalDatasetEntryResponse] = [] - class Config: - orm_mode = True - class EvalDatasetSummaryResponse(BaseModel): - id: int + model_config = ConfigDict(from_attributes=True) + + id: str name: str description: Optional[str] = None + status: str = "completed" + error: Optional[str] = None + completed_contexts: Optional[int] = None + total_contexts: Optional[int] = None + completed_goldens: Optional[int] = None + target_goldens: Optional[int] = None created_at: datetime updated_at: datetime entry_count: int = 0 - class Config: - orm_mode = True - # --------------------------------------------------------------------------- # Synthesis request @@ -81,20 +117,120 @@ class SynthesizeRequest(BaseModel): description: Optional[str] = None contexts: List[List[str]] model_name: str + target_goldens: int = 10 num_goldens_per_context: int = 2 - - @validator("contexts") + num_evolutions: int = 1 + input_quality_threshold: float = 0.4 + llm_timeout: float = 0 + async_mode: bool = False + max_concurrent: int = 1 + + @field_validator("contexts") + @classmethod def contexts_not_empty(cls, v): if not v: raise ValueError("contexts must not be empty") return v - @validator("num_goldens_per_context") + @field_validator("target_goldens") + @classmethod + def target_goldens_positive(cls, v): + if v < 1: + raise ValueError("target_goldens must be >= 1") + return v + + @field_validator("num_goldens_per_context") + @classmethod def goldens_positive(cls, v): if v < 1: raise ValueError("num_goldens_per_context must be >= 1") return v + @field_validator("num_evolutions") + @classmethod + def evolutions_positive(cls, v): + if v < 1: + raise ValueError("num_evolutions must be >= 1") + return v + + @field_validator("input_quality_threshold") + @classmethod + def input_quality_range(cls, v): + if v < 0 or v > 1: + raise ValueError("input_quality_threshold must be between 0 and 1") + return v + + @field_validator("llm_timeout") + @classmethod + def llm_timeout_non_negative(cls, v): + if v < 0: + raise ValueError("llm_timeout must be >= 0") + return v + + @field_validator("max_concurrent") + @classmethod + def max_concurrent_positive(cls, v): + if v < 1: + raise ValueError("max_concurrent must be >= 1") + return v + + +# --------------------------------------------------------------------------- +# Synthesis request (document-based — file upload path) +# --------------------------------------------------------------------------- + + +class SynthesizeFromDocRequest(BaseModel): + """Synthesis request that accepts a raw document file. + + The file bytes + extension are passed separately to the background task; + this model carries only the scalar configuration fields. + """ + + name: str + description: Optional[str] = None + model_name: str + embed_model_name: str = "nomic-embed-text" + target_goldens: int = 10 + max_goldens_per_context: int = 2 + max_contexts: int = 5 + max_context_length: int = 3 + chunk_size: int = 1024 + chunk_overlap: int = 0 + num_evolutions: int = 1 + input_quality_threshold: float = 0.4 + llm_timeout: float = 0 + async_mode: bool = False + max_concurrent: int = 1 + + @field_validator("target_goldens") + @classmethod + def target_goldens_positive(cls, v): + if v < 1: + raise ValueError("target_goldens must be >= 1") + return v + + @field_validator("max_goldens_per_context") + @classmethod + def goldens_positive(cls, v): + if v < 1: + raise ValueError("max_goldens_per_context must be >= 1") + return v + + @field_validator("input_quality_threshold") + @classmethod + def input_quality_range(cls, v): + if v < 0 or v > 1: + raise ValueError("input_quality_threshold must be between 0 and 1") + return v + + @field_validator("max_concurrent") + @classmethod + def max_concurrent_positive(cls, v): + if v < 1: + raise ValueError("max_concurrent must be >= 1") + return v + # --------------------------------------------------------------------------- # Eval Run @@ -103,7 +239,7 @@ def goldens_positive(cls, v): class EvalRunCreate(BaseModel): name: str - dataset_id: int + dataset_id: str sandbox_id: str model_name: str metrics: List[str] @@ -117,7 +253,8 @@ class EvalRunCreate(BaseModel): max_tokens: Optional[int] = 100 request_model: Optional[str] = "NA" - @validator("metrics") + @field_validator("metrics") + @classmethod def validate_metrics(cls, v): if not v: raise ValueError("At least one metric must be specified") @@ -137,6 +274,8 @@ class MetricScoreDetail(BaseModel): class EvalRunResultResponse(BaseModel): + model_config = ConfigDict(from_attributes=True) + id: int run_id: str entry_id: int @@ -147,18 +286,19 @@ class EvalRunResultResponse(BaseModel): created_at: datetime entry: Optional[EvalDatasetEntryResponse] = None - class Config: - orm_mode = True - class EvalRunSummaryResponse(BaseModel): + model_config = ConfigDict(from_attributes=True) + id: str name: str - dataset_id: int + dataset_id: Optional[str] = None sandbox_id: str model_name: str metrics: List[str] status: str + completed_count: Optional[int] = None + total_count: Optional[int] = None created_at: datetime completed_at: Optional[datetime] = None error: Optional[str] = None @@ -167,19 +307,21 @@ class EvalRunSummaryResponse(BaseModel): max_tokens: Optional[int] = None request_model: Optional[str] = None configuration_snapshot: Optional[Dict[str, Any]] = None - - class Config: - orm_mode = True + dataset_name_snapshot: Optional[str] = None class EvalRunResponse(BaseModel): + model_config = ConfigDict(from_attributes=True) + id: str name: str - dataset_id: int + dataset_id: Optional[str] = None sandbox_id: str model_name: str metrics: List[str] status: str + completed_count: Optional[int] = None + total_count: Optional[int] = None created_at: datetime completed_at: Optional[datetime] = None error: Optional[str] = None @@ -188,11 +330,10 @@ class EvalRunResponse(BaseModel): max_tokens: Optional[int] = None request_model: Optional[str] = None configuration_snapshot: Optional[Dict[str, Any]] = None + dataset_name_snapshot: Optional[str] = None + dataset_entries_snapshot: Optional[List[Dict[str, Any]]] = None results: List[EvalRunResultResponse] = [] - class Config: - orm_mode = True - # --------------------------------------------------------------------------- # Ollama model management @@ -208,7 +349,8 @@ class OllamaModelInfo(BaseModel): class ModelPullRequest(BaseModel): model_name: str - @validator("model_name") + @field_validator("model_name") + @classmethod def model_name_not_empty(cls, v): if not v.strip(): raise ValueError("model_name must not be empty") diff --git a/studio-eval/app/routers/eval_router.py b/studio-eval/app/routers/eval_router.py index e248994..bfa049f 100644 --- a/studio-eval/app/routers/eval_router.py +++ b/studio-eval/app/routers/eval_router.py @@ -1,20 +1,27 @@ +import asyncio import logging -from typing import List +from pathlib import Path +from typing import List, Optional -from fastapi import APIRouter, BackgroundTasks, Depends, HTTPException +from fastapi import APIRouter, BackgroundTasks, Depends, File, Form, HTTPException, UploadFile from sqlalchemy.orm import Session from app.db.database import get_db from app.models.eval_models import ( + AddEntriesRequest, + AddEntriesResponse, EvalDatasetCreate, + EvalDatasetEntryResponse, + EvalDatasetEntryUpdate, EvalDatasetResponse, EvalDatasetSummaryResponse, + EvalDatasetUpdate, EvalRunCreate, EvalRunResponse, EvalRunSummaryResponse, ModelPullRequest, OllamaModelInfo, - SynthesizeRequest, + SynthesizeFromDocRequest, ) from app.services import dataset_service, eval_service from app.services.ollama_service import ensure_model, list_models @@ -60,10 +67,19 @@ def delete_eval_run(run_id: str, db: Session = Depends(get_db)): raise HTTPException(status_code=404, detail=f"Eval run {run_id} not found") +@router.post("/runs/{run_id}/stop", status_code=200) +def stop_eval_run(run_id: str, db: Session = Depends(get_db)): + """Request cancellation of a pending or running eval job.""" + result = eval_service.stop_run(db, run_id) + if result is None: + raise HTTPException(status_code=404, detail=f"Eval run {run_id} not found") + return {"status": result} + + # --------------------------------------------------------------------------- # Datasets # Note: /datasets/synthesize MUST be registered before /datasets/{dataset_id} -# so FastAPI does not treat "synthesize" as an integer dataset_id. +# so FastAPI does not treat "synthesize" as a dataset_id string that happens to match. # --------------------------------------------------------------------------- @@ -72,16 +88,73 @@ def create_dataset(payload: EvalDatasetCreate, db: Session = Depends(get_db)): return dataset_service.create_dataset(db, payload) -@router.post("/datasets/synthesize", response_model=EvalDatasetResponse, status_code=201) +@router.post("/datasets/synthesize", response_model=EvalDatasetSummaryResponse, status_code=202) async def synthesize_dataset( - payload: SynthesizeRequest, + background_tasks: BackgroundTasks, db: Session = Depends(get_db), + file: UploadFile = File(...), + name: str = Form(...), + model_name: str = Form(...), + embed_model_name: str = Form("nomic-embed-text"), + description: Optional[str] = Form(None), + num_goldens: int = Form(10), + max_goldens_per_document: int = Form(2), + max_contexts: int = Form(5), + min_contexts: int = Form(1), + average_chunks_per_context: int = Form(3), + chunk_size: int = Form(1024), + chunk_overlap: int = Form(0), + num_evolutions: int = Form(1), + input_quality: float = Form(0.4), + llm_timeout: float = Form(0), + async_mode: str = Form("false"), + max_concurrent: int = Form(1), ): - """Generate a golden dataset with the DeepEval Synthesizer (blocking).""" + """Receive an uploaded document, build contexts via OllamaEmbeddingModel, + and synthesize golden Q/A pairs with DeepEval in the background.""" try: - return await dataset_service.synthesize_dataset(db, payload) + file_bytes = await file.read() + file_ext = Path(file.filename).suffix.lower() if file.filename else ".pdf" + + payload = SynthesizeFromDocRequest( + name=name, + description=description or None, + model_name=model_name, + embed_model_name=embed_model_name, + target_goldens=num_goldens, + max_goldens_per_context=max_goldens_per_document, + max_contexts=max_contexts, + max_context_length=average_chunks_per_context, + chunk_size=chunk_size, + chunk_overlap=chunk_overlap, + num_evolutions=num_evolutions, + input_quality_threshold=input_quality, + llm_timeout=llm_timeout, + async_mode=str(async_mode).lower() in ("true", "1", "yes"), + max_concurrent=max_concurrent, + ) + + dataset = dataset_service.create_pending_dataset(db, payload) + task = asyncio.create_task( + dataset_service.run_synthesis_background(dataset.id, payload, file_bytes, file_ext) + ) + dataset_service.register_synthesis_task(dataset.id, task) + return EvalDatasetSummaryResponse( + id=dataset.id, + name=dataset.name, + description=dataset.description, + status="pending", + error=None, + completed_contexts=0, + total_contexts=0, + completed_goldens=0, + target_goldens=payload.target_goldens, + created_at=dataset.created_at, + updated_at=dataset.updated_at, + entry_count=0, + ) except Exception as exc: - logger.error("Dataset synthesis failed: %s", exc) + logger.error("Failed to create synthesis job: %s", exc) raise HTTPException(status_code=500, detail=str(exc)) @@ -93,6 +166,12 @@ def list_datasets(db: Session = Depends(get_db)): id=ds.id, name=ds.name, description=ds.description, + status=ds.status, + error=ds.error, + completed_contexts=ds.completed_contexts, + total_contexts=ds.total_contexts, + completed_goldens=ds.completed_goldens, + target_goldens=ds.target_goldens, created_at=ds.created_at, updated_at=ds.updated_at, entry_count=len(ds.entries), @@ -102,7 +181,7 @@ def list_datasets(db: Session = Depends(get_db)): @router.get("/datasets/{dataset_id}", response_model=EvalDatasetResponse) -def get_dataset(dataset_id: int, db: Session = Depends(get_db)): +def get_dataset(dataset_id: str, db: Session = Depends(get_db)): dataset = dataset_service.get_dataset(db, dataset_id) if not dataset: raise HTTPException(status_code=404, detail=f"Dataset {dataset_id} not found") @@ -110,11 +189,74 @@ def get_dataset(dataset_id: int, db: Session = Depends(get_db)): @router.delete("/datasets/{dataset_id}", status_code=204) -def delete_dataset(dataset_id: int, db: Session = Depends(get_db)): +def delete_dataset(dataset_id: str, db: Session = Depends(get_db)): if not dataset_service.delete_dataset(db, dataset_id): raise HTTPException(status_code=404, detail=f"Dataset {dataset_id} not found") +@router.post("/datasets/{dataset_id}/stop", status_code=200) +def stop_dataset_synthesis(dataset_id: str, db: Session = Depends(get_db)): + """Request cancellation of a pending or synthesizing dataset job.""" + result = dataset_service.stop_dataset(db, dataset_id) + if result is None: + raise HTTPException(status_code=404, detail=f"Dataset {dataset_id} not found") + return {"status": result} + + +@router.put("/datasets/{dataset_id}", response_model=EvalDatasetResponse) +def update_dataset(dataset_id: str, payload: EvalDatasetUpdate, db: Session = Depends(get_db)): + dataset = dataset_service.update_dataset(db, dataset_id, payload) + if not dataset: + raise HTTPException(status_code=404, detail=f"Dataset {dataset_id} not found") + return dataset + + +@router.post("/datasets/{dataset_id}/entries", response_model=AddEntriesResponse, status_code=201) +def add_entries( + dataset_id: str, + payload: AddEntriesRequest, + db: Session = Depends(get_db), +): + dataset = dataset_service.get_dataset(db, dataset_id) + if not dataset: + raise HTTPException(status_code=404, detail=f"Dataset {dataset_id} not found") + if dataset.status == "synthesizing": + raise HTTPException(status_code=409, detail="Cannot add entries while dataset is synthesizing") + new_entries = dataset_service.add_entries(db, dataset_id, payload) + return AddEntriesResponse(added=len(new_entries), entries=new_entries) + + +@router.put("/datasets/{dataset_id}/entries/{entry_id}", response_model=EvalDatasetEntryResponse) +def update_entry( + dataset_id: str, + entry_id: int, + payload: EvalDatasetEntryUpdate, + db: Session = Depends(get_db), +): + dataset = dataset_service.get_dataset(db, dataset_id) + if not dataset: + raise HTTPException(status_code=404, detail=f"Dataset {dataset_id} not found") + entry = dataset_service.update_entry(db, dataset_id, entry_id, payload) + if not entry: + raise HTTPException(status_code=404, detail=f"Entry {entry_id} not found in dataset {dataset_id}") + return entry + + +@router.delete("/datasets/{dataset_id}/entries/{entry_id}", status_code=204) +def delete_entry( + dataset_id: str, + entry_id: int, + db: Session = Depends(get_db), +): + dataset = dataset_service.get_dataset(db, dataset_id) + if not dataset: + raise HTTPException(status_code=404, detail=f"Dataset {dataset_id} not found") + if dataset.status == "synthesizing": + raise HTTPException(status_code=409, detail="Cannot delete entries while dataset is synthesizing") + if not dataset_service.delete_entry(db, dataset_id, entry_id): + raise HTTPException(status_code=404, detail=f"Entry {entry_id} not found in dataset {dataset_id}") + + # --------------------------------------------------------------------------- # Ollama model management # --------------------------------------------------------------------------- diff --git a/studio-eval/app/services/dataset_service.py b/studio-eval/app/services/dataset_service.py index 61345f5..5c50538 100644 --- a/studio-eval/app/services/dataset_service.py +++ b/studio-eval/app/services/dataset_service.py @@ -1,17 +1,70 @@ import asyncio import json import logging -from typing import List, Optional +import math +import multiprocessing +import os +import tempfile +from queue import Empty +from typing import Any, Dict, List, Optional import pandas as pd from sqlalchemy.orm import Session +from app.compat import ensure_pydantic_v2_apis +from app.db.database import SessionLocal from app.db.schema import EvalDataset, EvalDatasetEntry -from app.models.eval_models import EvalDatasetCreate, SynthesizeRequest -from app.services.ollama_service import OllamaJudge, ensure_model +from app.models.eval_models import ( + AddEntriesRequest, + EvalDatasetCreate, + EvalDatasetEntryUpdate, + EvalDatasetUpdate, + SynthesizeFromDocRequest, + SynthesizeRequest, +) +from app.services.ollama_service import OllamaEmbeddingModel, OllamaJudge, ensure_model logger = logging.getLogger(__name__) +# --------------------------------------------------------------------------- +# In-process cancellation registry +# --------------------------------------------------------------------------- +_cancelled_datasets: set = set() +_synthesis_tasks: Dict[str, asyncio.Task] = {} +_synthesis_processes: Dict[str, multiprocessing.Process] = {} + + +def request_dataset_cancellation(dataset_id: str) -> None: + """Signal the background synthesis job for *dataset_id* to exit at its next checkpoint.""" + _cancelled_datasets.add(dataset_id) + process = _synthesis_processes.get(dataset_id) + if process and process.is_alive(): + process.terminate() + task = _synthesis_tasks.get(dataset_id) + if task and not task.done(): + task.cancel() + + +def register_synthesis_task(dataset_id: str, task: asyncio.Task) -> None: + """Register the asyncio Task running synthesis so it can be cancelled.""" + _synthesis_tasks[dataset_id] = task + + +def stop_dataset(db: Session, dataset_id: str) -> Optional[str]: + """Mark a dataset synthesis as stopped and register its cancellation. + + Returns the resulting status string, or None if the dataset was not found. + """ + dataset = db.query(EvalDataset).filter(EvalDataset.id == dataset_id).first() + if not dataset: + return None + if dataset.status not in ("pending", "synthesizing"): + return dataset.status # already finished — nothing to do + request_dataset_cancellation(dataset_id) + dataset.status = "stopped" + db.commit() + return "stopped" + def _normalize_context(context) -> Optional[List[str]]: if context is None: @@ -43,6 +96,72 @@ def _normalize_context(context) -> Optional[List[str]]: return [str(context)] +def _goldens_to_records(goldens: list) -> List[Dict[str, Any]]: + return [ + { + "input": getattr(golden, "input", None), + "expected_output": getattr(golden, "expected_output", None), + "context": getattr(golden, "context", None), + } + for golden in goldens + ] + + +def _start_synthesis_process( + dataset_id: str, + tmp_path: str, + payload: SynthesizeFromDocRequest, +): + ctx = multiprocessing.get_context("spawn") + result_queue = ctx.Queue() + process = ctx.Process( + target=_run_synthesizer_from_docs_worker, + args=(tmp_path, payload.dict(), result_queue), + daemon=True, + ) + process.start() + _synthesis_processes[dataset_id] = process + return process, result_queue + + +def _stop_synthesis_process(dataset_id: str, process: Optional[multiprocessing.Process]) -> None: + if process is None: + return + try: + if process.is_alive(): + process.terminate() + process.join(timeout=1) + elif process.exitcode is None: + process.join(timeout=1) + if process.is_alive(): + process.kill() + process.join(timeout=1) + except Exception: + logger.warning("Failed to stop synthesis process for dataset %s", dataset_id, exc_info=True) + finally: + _synthesis_processes.pop(dataset_id, None) + + +async def _wait_for_synthesis_process_result( + dataset_id: str, + process: multiprocessing.Process, + result_queue, +) -> Dict[str, Any]: + while process.is_alive(): + if dataset_id in _cancelled_datasets: + raise asyncio.CancelledError() + await asyncio.sleep(0.25) + + await asyncio.to_thread(process.join, 1) + + try: + return await asyncio.to_thread(result_queue.get, True, 1) + except Empty as exc: + if process.exitcode in (0, None): + raise RuntimeError("Synthesis process exited without returning results") from exc + raise RuntimeError(f"Synthesis process exited with code {process.exitcode}") from exc + + # --------------------------------------------------------------------------- # CRUD helpers # --------------------------------------------------------------------------- @@ -68,7 +187,7 @@ def create_dataset(db: Session, payload: EvalDatasetCreate) -> EvalDataset: return dataset -def get_dataset(db: Session, dataset_id: int) -> Optional[EvalDataset]: +def get_dataset(db: Session, dataset_id: str) -> Optional[EvalDataset]: return db.query(EvalDataset).filter(EvalDataset.id == dataset_id).first() @@ -76,31 +195,198 @@ def list_datasets(db: Session) -> List[EvalDataset]: return db.query(EvalDataset).all() -def delete_dataset(db: Session, dataset_id: int) -> bool: +def delete_dataset(db: Session, dataset_id: str) -> bool: dataset = get_dataset(db, dataset_id) if not dataset: return False + # Signal background job to stop if active so it exits before the FK is gone. + if dataset.status in ("pending", "synthesizing"): + request_dataset_cancellation(dataset_id) db.delete(dataset) db.commit() return True +def update_dataset(db: Session, dataset_id: str, payload: EvalDatasetUpdate) -> Optional[EvalDataset]: + dataset = get_dataset(db, dataset_id) + if not dataset: + return None + updates = payload.dict(exclude_none=True) + for field, value in updates.items(): + setattr(dataset, field, value) + db.commit() + db.refresh(dataset) + return dataset + + +def get_entry(db: Session, dataset_id: str, entry_id: int) -> Optional[EvalDatasetEntry]: + return ( + db.query(EvalDatasetEntry) + .filter(EvalDatasetEntry.id == entry_id, EvalDatasetEntry.dataset_id == dataset_id) + .first() + ) + + +def update_entry( + db: Session, dataset_id: str, entry_id: int, payload: EvalDatasetEntryUpdate +) -> Optional[EvalDatasetEntry]: + entry = get_entry(db, dataset_id, entry_id) + if not entry: + return None + if payload.input is not None: + entry.input = payload.input + if payload.expected_output is not None: + entry.expected_output = payload.expected_output + if payload.context is not None: + entry.context = _normalize_context(payload.context) + db.commit() + db.refresh(entry) + return entry + + +def add_entries(db: Session, dataset_id: str, payload: AddEntriesRequest) -> List[EvalDatasetEntry]: + dataset = get_dataset(db, dataset_id) + if not dataset: + return [] + new_entries = [] + for entry_data in payload.entries: + entry = EvalDatasetEntry( + dataset_id=dataset_id, + input=entry_data.input, + expected_output=entry_data.expected_output, + context=_normalize_context(entry_data.context), + ) + db.add(entry) + new_entries.append(entry) + db.commit() + for entry in new_entries: + db.refresh(entry) + return new_entries + + +def delete_entry(db: Session, dataset_id: str, entry_id: int) -> bool: + entry = get_entry(db, dataset_id, entry_id) + if not entry: + return False + db.delete(entry) + db.commit() + return True + + # --------------------------------------------------------------------------- # DeepEval Synthesizer → pandas → MySQL # --------------------------------------------------------------------------- -def _run_synthesizer(judge: OllamaJudge, contexts: List[List[str]], max_per_context: int): +def _run_synthesizer(payload: SynthesizeRequest, judge: OllamaJudge, contexts: List[List[str]], max_per_context: int): """Blocking call to DeepEval Synthesizer — run via asyncio.to_thread.""" + ensure_pydantic_v2_apis() from deepeval.synthesizer import Synthesizer # noqa: PLC0415 - - synthesizer = Synthesizer(model=judge) + from deepeval.synthesizer.config import EvolutionConfig, FiltrationConfig # noqa: PLC0415 + + synthesizer = Synthesizer( + model=judge, + async_mode=payload.async_mode, + max_concurrent=payload.max_concurrent, + filtration_config=FiltrationConfig( + synthetic_input_quality_threshold=payload.input_quality_threshold, + critic_model=judge, + ), + evolution_config=EvolutionConfig(num_evolutions=payload.num_evolutions), + ) return synthesizer.generate_goldens_from_contexts( contexts=contexts, max_goldens_per_context=max_per_context, ) +def _resolve_doc_generation_limits(payload: SynthesizeFromDocRequest) -> tuple[int, int]: + target_goldens = max(payload.target_goldens, 1) + max_contexts = max(payload.max_contexts, 1) + max_goldens_per_context = max(payload.max_goldens_per_context, 1) + best_plan: Optional[tuple[int, int]] = None + best_total: Optional[int] = None + + for contexts in range(1, max_contexts + 1): + goldens_per_context = math.ceil(target_goldens / contexts) + if goldens_per_context > max_goldens_per_context: + continue + total_goldens = contexts * goldens_per_context + if ( + best_plan is None + or total_goldens < best_total + or (total_goldens == best_total and contexts > best_plan[0]) + ): + best_plan = (contexts, goldens_per_context) + best_total = total_goldens + + if best_plan is not None: + return best_plan + + return max_contexts, math.ceil(target_goldens / max_contexts) + + +def _run_synthesizer_from_docs( + tmp_path: str, + payload: SynthesizeFromDocRequest, + judge: OllamaJudge, + embedder: OllamaEmbeddingModel, +) -> list: + """Blocking call to DeepEval Synthesizer.generate_goldens_from_docs — run via asyncio.to_thread.""" + ensure_pydantic_v2_apis() + from app.compat import patch_deepeval_document_chunker # noqa: PLC0415 + patch_deepeval_document_chunker() + from deepeval.synthesizer import Synthesizer # noqa: PLC0415 + from deepeval.synthesizer.config import ( # noqa: PLC0415 + ContextConstructionConfig, + EvolutionConfig, + FiltrationConfig, + ) + + synthesizer = Synthesizer( + model=judge, + async_mode=payload.async_mode, + max_concurrent=payload.max_concurrent, + filtration_config=FiltrationConfig( + synthetic_input_quality_threshold=payload.input_quality_threshold, + critic_model=judge, + ), + evolution_config=EvolutionConfig(num_evolutions=payload.num_evolutions), + ) + max_contexts_per_document, max_goldens_per_context = _resolve_doc_generation_limits(payload) + return synthesizer.generate_goldens_from_docs( + document_paths=[tmp_path], + include_expected_output=True, + max_goldens_per_context=max_goldens_per_context, + context_construction_config=ContextConstructionConfig( + embedder=embedder, + critic_model=judge, + chunk_size=payload.chunk_size, + chunk_overlap=payload.chunk_overlap, + max_context_length=payload.max_context_length, + max_contexts_per_document=max_contexts_per_document, + ), + ) + + +def _run_synthesizer_from_docs_worker( + tmp_path: str, + payload_data: Dict[str, Any], + result_queue, +) -> None: + payload = SynthesizeFromDocRequest(**payload_data) + judge = OllamaJudge(model_name=payload.model_name, timeout=payload.llm_timeout) + embedder = OllamaEmbeddingModel(model_name=payload.embed_model_name) + + try: + goldens = _run_synthesizer_from_docs(tmp_path, payload, judge, embedder) + result_queue.put({"records": _goldens_to_records(goldens)}) + except Exception as exc: + message = str(exc).strip() or exc.__class__.__name__ + logger.exception("Document synthesis worker failed for %s", tmp_path) + result_queue.put({"error": message}) + + async def synthesize_dataset(db: Session, payload: SynthesizeRequest) -> EvalDataset: """Generate a golden dataset with DeepEval Synthesizer and persist it. @@ -111,35 +397,35 @@ async def synthesize_dataset(db: Session, payload: SynthesizeRequest) -> EvalDat 4. Persist EvalDataset + EvalDatasetEntry rows. """ await ensure_model(payload.model_name) - judge = OllamaJudge(model_name=payload.model_name) + judge = OllamaJudge(model_name=payload.model_name, timeout=payload.llm_timeout) logger.info( - "Synthesizing dataset '%s': %d contexts, %d goldens each", + "Synthesizing dataset '%s': %d contexts, target=%d, %d goldens each, async=%s, max_concurrent=%d", payload.name, len(payload.contexts), + payload.target_goldens, payload.num_goldens_per_context, + payload.async_mode, + payload.max_concurrent, ) goldens = await asyncio.to_thread( _run_synthesizer, + payload, judge, payload.contexts, payload.num_goldens_per_context, ) # Normalise with pandas - records = [ - { - "input": getattr(g, "input", None), - "expected_output": getattr(g, "expected_output", None), - "context": getattr(g, "context", None), - } - for g in goldens - ] + records = _goldens_to_records(goldens) df = pd.DataFrame(records) df = df.dropna(subset=["input"]).reset_index(drop=True) + if payload.target_goldens and len(df) > payload.target_goldens: + df = df.head(payload.target_goldens).reset_index(drop=True) + if df.empty: raise ValueError("Synthesizer returned no valid goldens (all inputs were null)") @@ -161,3 +447,150 @@ async def synthesize_dataset(db: Session, payload: SynthesizeRequest) -> EvalDat db.refresh(dataset) logger.info("Synthesized dataset '%s' with %d entries", dataset.name, len(df)) return dataset + + +# --------------------------------------------------------------------------- +# Background synthesis helpers (non-blocking endpoint pattern) +# --------------------------------------------------------------------------- + + +def create_pending_dataset(db: Session, payload: SynthesizeFromDocRequest) -> EvalDataset: + """Create a dataset record with status='pending' and return immediately.""" + dataset = EvalDataset( + name=payload.name, + description=payload.description, + status="pending", + ) + db.add(dataset) + db.commit() + db.refresh(dataset) + return dataset + + +async def run_synthesis_background( + dataset_id: str, + payload: SynthesizeFromDocRequest, + file_bytes: bytes, + file_ext: str, +) -> None: + """Background task: synthesize a dataset from an uploaded document file. + + Writes the file to a temp path, calls DeepEval's generate_goldens_from_docs + with OllamaEmbeddingModel for chunking/context construction, then persists + the resulting goldens. Cleans up the temp file on exit. + """ + tmp_path: Optional[str] = None + synthesis_process: Optional[multiprocessing.Process] = None + result_queue = None + db: Session = SessionLocal() + try: + dataset = db.query(EvalDataset).filter(EvalDataset.id == dataset_id).first() + if not dataset: + logger.error("Background synthesis: dataset %s not found", dataset_id) + return + dataset.status = "synthesizing" + dataset.total_contexts = 0 + dataset.completed_contexts = 0 + dataset.target_goldens = payload.target_goldens + dataset.completed_goldens = 0 + db.commit() + + # Cancellation check before pulling models / writing file. + if dataset_id in _cancelled_datasets: + _cancelled_datasets.discard(dataset_id) + logger.info("Synthesis for dataset %s cancelled before start", dataset_id) + return + + # Ensure both models are available (pull if absent). + await ensure_model(payload.model_name) + await ensure_model(payload.embed_model_name) + + judge = OllamaJudge(model_name=payload.model_name, timeout=payload.llm_timeout) + embedder = OllamaEmbeddingModel(model_name=payload.embed_model_name) + + # Write document bytes to a temporary file that DeepEval can read. + suffix = file_ext if file_ext.startswith(".") else f".{file_ext}" + with tempfile.NamedTemporaryFile(delete=False, suffix=suffix) as tmp: + tmp.write(file_bytes) + tmp_path = tmp.name + + logger.info( + "Background synthesis for dataset %s '%s': embed=%s, file=%s, target=%d", + dataset_id, payload.name, payload.embed_model_name, tmp_path, payload.target_goldens, + ) + + # Final cancellation check before the expensive synthesizer call. + if dataset_id in _cancelled_datasets: + _cancelled_datasets.discard(dataset_id) + logger.info("Synthesis for dataset %s cancelled before synthesizer", dataset_id) + return + + synthesis_process, result_queue = _start_synthesis_process(dataset_id, tmp_path, payload) + worker_result = await _wait_for_synthesis_process_result(dataset_id, synthesis_process, result_queue) + + if worker_result.get("error"): + raise RuntimeError(worker_result["error"]) + + records = worker_result.get("records") or [] + + # Cancellation check after synthesis, before DB write. + if dataset_id in _cancelled_datasets: + _cancelled_datasets.discard(dataset_id) + logger.info("Synthesis for dataset %s cancelled after synthesizer", dataset_id) + return + + df = pd.DataFrame(records) + df = df.dropna(subset=["input"]).reset_index(drop=True) + + if payload.target_goldens and len(df) > payload.target_goldens: + df = df.head(payload.target_goldens).reset_index(drop=True) + + if df.empty: + raise ValueError("Synthesizer returned no valid goldens (all inputs were null)") + + dataset = db.query(EvalDataset).filter(EvalDataset.id == dataset_id).first() + for _, row in df.iterrows(): + db.add( + EvalDatasetEntry( + dataset_id=dataset.id, + input=row["input"], + expected_output=row.get("expected_output"), + context=_normalize_context(row.get("context")), + ) + ) + dataset.status = "completed" + dataset.completed_goldens = len(df) + dataset.error = None + db.commit() + logger.info("Background synthesis completed for dataset %s with %d entries", dataset_id, len(df)) + + except asyncio.CancelledError: + _stop_synthesis_process(dataset_id, synthesis_process) + logger.info("Synthesis task for dataset %s was cancelled", dataset_id) + # Status was already set to 'stopped' by stop_dataset / request_dataset_cancellation. + except Exception as exc: + logger.error("Background synthesis failed for dataset %s: %s", dataset_id, exc) + try: + dataset = db.query(EvalDataset).filter(EvalDataset.id == dataset_id).first() + if dataset: + dataset.status = "failed" + dataset.error = str(exc) + db.commit() + except Exception: + pass + finally: + _stop_synthesis_process(dataset_id, synthesis_process) + if result_queue is not None: + try: + result_queue.close() + result_queue.join_thread() + except Exception: + logger.debug("Failed to close synthesis result queue for dataset %s", dataset_id, exc_info=True) + if tmp_path and os.path.exists(tmp_path): + try: + os.unlink(tmp_path) + except OSError: + pass + _synthesis_tasks.pop(dataset_id, None) + _cancelled_datasets.discard(dataset_id) + db.close() diff --git a/studio-eval/app/services/eval_service.py b/studio-eval/app/services/eval_service.py index 9f69aaa..e9f9370 100644 --- a/studio-eval/app/services/eval_service.py +++ b/studio-eval/app/services/eval_service.py @@ -8,7 +8,7 @@ from app.compat import ensure_pydantic_v2_apis from app.db.database import SessionLocal -from app.db.schema import EvalDatasetEntry, EvalRun, EvalRunResult +from app.db.schema import EvalDataset, EvalDatasetEntry, EvalRun, EvalRunResult from app.models.eval_models import EvalRunCreate from app.services.ollama_service import OllamaJudge, ensure_model from app.services.response_collector import ( # noqa: PLC0415 @@ -21,6 +21,42 @@ # Lazy import cache — avoids importing deepeval at module load time. _METRIC_CLASSES: Optional[dict] = None +# --------------------------------------------------------------------------- +# In-process cancellation registry +# --------------------------------------------------------------------------- +_cancelled_runs: set = set() +_run_tasks: Dict[str, asyncio.Task] = {} + + +def request_run_cancellation(run_id: str) -> None: + """Signal the background job for *run_id* to exit at its next checkpoint.""" + _cancelled_runs.add(run_id) + task = _run_tasks.get(run_id) + if task and not task.done(): + task.cancel() + + +def register_run_task(run_id: str, task: asyncio.Task) -> None: + """Register the asyncio Task running an eval so it can be cancelled.""" + _run_tasks[run_id] = task + + +def stop_run(db: Session, run_id: str) -> Optional[str]: + """Mark a run as stopped and register its cancellation. + + Returns the resulting status string, or None if the run was not found. + """ + run = db.query(EvalRun).filter(EvalRun.id == run_id).first() + if not run: + return None + if run.status not in ("pending", "running"): + return run.status # already finished — nothing to do + request_run_cancellation(run_id) + run.status = "stopped" + run.completed_at = datetime.utcnow() + db.commit() + return "stopped" + def _normalize_context(context) -> List[str]: if context is None: @@ -81,6 +117,21 @@ def _get_metric_classes() -> dict: def create_eval_run(db: Session, payload: EvalRunCreate) -> EvalRun: + dataset = db.query(EvalDataset).filter_by(id=payload.dataset_id).first() + dataset_name_snap = dataset.name if dataset else None + entries_snap = None + if dataset: + entries = db.query(EvalDatasetEntry).filter_by(dataset_id=dataset.id).all() + entries_snap = [ + { + "id": e.id, + "input": e.input, + "expected_output": e.expected_output, + "context": e.context, + } + for e in entries + ] + run = EvalRun( name=payload.name, dataset_id=payload.dataset_id, @@ -93,6 +144,8 @@ def create_eval_run(db: Session, payload: EvalRunCreate) -> EvalRun: max_tokens=payload.max_tokens, request_model=payload.request_model, configuration_snapshot=payload.configuration_snapshot, + dataset_name_snapshot=dataset_name_snap, + dataset_entries_snapshot=entries_snap, ) db.add(run) db.commit() @@ -115,9 +168,12 @@ def list_runs(db: Session) -> List[EvalRun]: def delete_run(db: Session, run_id: str) -> bool: - run = get_run(db, run_id) + run = db.query(EvalRun).filter(EvalRun.id == run_id).first() if not run: return False + # Signal background job to stop if active so it exits before the FK is gone. + if run.status in ("pending", "running"): + request_run_cancellation(run_id) db.delete(run) db.commit() return True @@ -128,9 +184,20 @@ def delete_run(db: Session, run_id: str) -> bool: # --------------------------------------------------------------------------- -def _measure_metric(metric, test_case) -> None: - """Thin wrapper so asyncio.to_thread can call metric.measure.""" - metric.measure(test_case) +def _raise_if_cancelled(run_id: str, checkpoint: str) -> None: + if run_id not in _cancelled_runs: + return + logger.info("Eval run %s stopped at %s", run_id, checkpoint) + raise asyncio.CancelledError() + + +async def _measure_metric(metric, test_case) -> None: + """Use async DeepEval execution when available so task cancellation propagates.""" + async_measure = getattr(metric, "a_measure", None) + if callable(async_measure): + await async_measure(test_case) + return + await asyncio.to_thread(metric.measure, test_case) async def _run_eval_job(run_id: str) -> None: # noqa: C901 @@ -140,6 +207,10 @@ async def _run_eval_job(run_id: str) -> None: # noqa: C901 pending → running → completed | failed """ db: Session = SessionLocal() + task = asyncio.current_task() + if task is not None: + register_run_task(run_id, task) + try: run: Optional[EvalRun] = ( db.query(EvalRun).filter(EvalRun.id == run_id).first() @@ -148,10 +219,13 @@ async def _run_eval_job(run_id: str) -> None: # noqa: C901 logger.error("Eval run %s not found in DB", run_id) return + _raise_if_cancelled(run_id, "startup") + run.status = "running" db.commit() # --- ensure judge model ----------------------------------------------- + _raise_if_cancelled(run_id, "model preparation") await ensure_model(run.model_name) judge = OllamaJudge(model_name=run.model_name) @@ -171,8 +245,14 @@ async def _run_eval_job(run_id: str) -> None: # noqa: C901 db.commit() return + run.total_count = len(entries) + run.completed_count = 0 + db.commit() + # --- process each entry ------------------------------------------------ for entry in entries: + _raise_if_cancelled(run_id, f"entry {entry.id} start") + actual_output: Optional[str] = None metric_scores: Dict[str, dict] = {} all_passed = True @@ -189,6 +269,7 @@ async def _run_eval_job(run_id: str) -> None: # noqa: C901 request_model=run.request_model or "NA", ) actual_output = actual_output or "" + _raise_if_cancelled(run_id, f"entry {entry.id} response collection") from deepeval.test_case import LLMTestCase # noqa: PLC0415 @@ -201,15 +282,15 @@ async def _run_eval_job(run_id: str) -> None: # noqa: C901 ) for metric_name in run.metrics: + _raise_if_cancelled(run_id, f"entry {entry.id} metric {metric_name} start") cls = metric_classes.get(metric_name) if cls is None: logger.warning("Unknown metric '%s' — skipping", metric_name) continue try: metric = cls(model=judge, threshold=0.5) - # Run blocking measure() in a thread pool to avoid - # blocking the event loop during LLM generation. - await asyncio.to_thread(_measure_metric, metric, test_case) + await _measure_metric(metric, test_case) + _raise_if_cancelled(run_id, f"entry {entry.id} metric {metric_name} completion") score = getattr(metric, "score", None) passed = getattr(metric, "success", None) @@ -252,6 +333,8 @@ async def _run_eval_job(run_id: str) -> None: # noqa: C901 all_passed = False reasons.append(_format_exception(exc)) + _raise_if_cancelled(run_id, f"entry {entry.id} result persistence") + db.add( EvalRunResult( run_id=run.id, @@ -262,6 +345,7 @@ async def _run_eval_job(run_id: str) -> None: # noqa: C901 reason="; ".join(reasons) if reasons else None, ) ) + run.completed_count = (run.completed_count or 0) + 1 db.commit() run.status = "completed" @@ -269,6 +353,17 @@ async def _run_eval_job(run_id: str) -> None: # noqa: C901 db.commit() logger.info("Eval run %s completed successfully", run_id) + except asyncio.CancelledError: + logger.info("Eval run %s cancelled", run_id) + try: + db.rollback() + run = db.query(EvalRun).filter(EvalRun.id == run_id).first() + if run and run.status in ("pending", "running"): + run.status = "stopped" + run.completed_at = datetime.utcnow() + db.commit() + except Exception as inner: + logger.error("Could not persist stopped status for run %s: %s", run_id, inner) except Exception as exc: logger.error("Eval run %s failed with unhandled error: %s", run_id, exc) try: @@ -281,4 +376,6 @@ async def _run_eval_job(run_id: str) -> None: # noqa: C901 except Exception as inner: logger.error("Could not persist failed status for run %s: %s", run_id, inner) finally: + _run_tasks.pop(run_id, None) + _cancelled_runs.discard(run_id) db.close() diff --git a/studio-eval/app/services/ollama_service.py b/studio-eval/app/services/ollama_service.py index a1fdc77..bf76f0b 100644 --- a/studio-eval/app/services/ollama_service.py +++ b/studio-eval/app/services/ollama_service.py @@ -1,10 +1,12 @@ import json import logging import os -from typing import Any +from typing import Any, List import httpx +from app.compat import ensure_pydantic_v2_apis + logger = logging.getLogger(__name__) @@ -26,6 +28,18 @@ def _parse_timeout(env_name: str, default: str): OLLAMA_GENERATE_TIMEOUT = _parse_timeout("OLLAMA_GENERATE_TIMEOUT", "0") +def _normalize_generate_timeout(value): + if value is None: + return OLLAMA_GENERATE_TIMEOUT + + try: + parsed = float(value) + except (TypeError, ValueError): + return OLLAMA_GENERATE_TIMEOUT + + return None if parsed <= 0 else parsed + + def _schema_to_ollama_format(schema) -> Any: return "json" @@ -124,11 +138,18 @@ async def ensure_model(model_name: str) -> None: # OllamaJudge — DeepEvalBaseLLM wrapper # --------------------------------------------------------------------------- +ensure_pydantic_v2_apis() + try: from deepeval.models import DeepEvalBaseLLM # deepeval >= 0.21 except ImportError: from deepeval.models.base_model import DeepEvalBaseLLM # older deepeval +try: + from deepeval.models.base_model import DeepEvalBaseEmbeddingModel +except ImportError: + from deepeval.models import DeepEvalBaseEmbeddingModel + class OllamaJudge(DeepEvalBaseLLM): """Custom DeepEval LLM that routes all generation requests to Ollama. @@ -141,9 +162,10 @@ class OllamaJudge(DeepEvalBaseLLM): asked to return JSON via ``"format": "json"``. """ - def __init__(self, model_name: str) -> None: + def __init__(self, model_name: str, timeout=None) -> None: self._model_name = model_name self._base_url = OLLAMA_BASE_URL + self._timeout = _normalize_generate_timeout(timeout) # -- DeepEvalBaseLLM contract ------------------------------------------- @@ -164,7 +186,7 @@ def generate(self, prompt: str, schema=None) -> Any: if schema is not None: payload["format"] = _schema_to_ollama_format(schema) - with httpx.Client(timeout=OLLAMA_GENERATE_TIMEOUT) as client: + with httpx.Client(timeout=self._timeout) as client: resp = client.post(f"{self._base_url}/api/generate", json=payload) resp.raise_for_status() response_text = resp.json()["response"] @@ -185,7 +207,7 @@ async def a_generate(self, prompt: str, schema=None) -> Any: if schema is not None: payload["format"] = _schema_to_ollama_format(schema) - async with httpx.AsyncClient(timeout=OLLAMA_GENERATE_TIMEOUT) as client: + async with httpx.AsyncClient(timeout=self._timeout) as client: resp = await client.post( f"{self._base_url}/api/generate", json=payload ) @@ -197,3 +219,67 @@ async def a_generate(self, prompt: str, schema=None) -> Any: return _parse_structured_response(schema, response_text) except Exception as exc: raise TypeError("Structured response parsing failed") from exc + + +# --------------------------------------------------------------------------- +# OllamaEmbeddingModel — DeepEvalBaseEmbeddingModel wrapper +# --------------------------------------------------------------------------- + + +class OllamaEmbeddingModel(DeepEvalBaseEmbeddingModel): + """DeepEval embedding model backed by Ollama's /api/embed endpoint. + + Uses the same OLLAMA_BASE_URL as OllamaJudge so no additional config + is required. Default model is ``nomic-embed-text``. + """ + + _EMBED_TIMEOUT = 120.0 + + def __init__(self, model_name: str = "nomic-embed-text") -> None: + self._base_url = OLLAMA_BASE_URL + # super().__init__ sets self.model_name and calls self.load_model() + super().__init__(model_name) + + # -- DeepEvalBaseEmbeddingModel contract -------------------------------- + + def load_model(self): + # Ollama is a remote service; nothing to load locally. + return self + + def get_model_name(self) -> str: + return self.model_name + + # -- Internal HTTP helpers ---------------------------------------------- + + def _call_embed(self, texts: List[str]) -> List[List[float]]: + with httpx.Client(timeout=self._EMBED_TIMEOUT) as client: + resp = client.post( + f"{self._base_url}/api/embed", + json={"model": self.model_name, "input": texts}, + ) + resp.raise_for_status() + return resp.json()["embeddings"] + + async def _a_call_embed(self, texts: List[str]) -> List[List[float]]: + async with httpx.AsyncClient(timeout=self._EMBED_TIMEOUT) as client: + resp = await client.post( + f"{self._base_url}/api/embed", + json={"model": self.model_name, "input": texts}, + ) + resp.raise_for_status() + return resp.json()["embeddings"] + + # -- Public embedding interface ----------------------------------------- + + def embed_text(self, text: str) -> List[float]: + return self._call_embed([text])[0] + + def embed_texts(self, texts: List[str]) -> List[List[float]]: + return self._call_embed(texts) + + async def a_embed_text(self, text: str) -> List[float]: + result = await self._a_call_embed([text]) + return result[0] + + async def a_embed_texts(self, texts: List[str]) -> List[List[float]]: + return await self._a_call_embed(texts) diff --git a/studio-eval/app/services/response_collector.py b/studio-eval/app/services/response_collector.py index 3fd5d4c..eb58b23 100644 --- a/studio-eval/app/services/response_collector.py +++ b/studio-eval/app/services/response_collector.py @@ -21,7 +21,7 @@ def _parse_timeout(env_name: str, default: str): return None if parsed <= 0 else parsed _APP_BACKEND_PORT = 8899 -_CHAT_PATH = "/v1/chat/completions" +_CHAT_PATH = "/v1/app-backend" _TIMEOUT = _parse_timeout("APP_BACKEND_TIMEOUT", "0") _APP_BACKEND_URL_TEMPLATE = os.getenv( "APP_BACKEND_URL_TEMPLATE", diff --git a/studio-eval/requirements.txt b/studio-eval/requirements.txt index 74006c1..876d97d 100644 --- a/studio-eval/requirements.txt +++ b/studio-eval/requirements.txt @@ -1,9 +1,15 @@ fastapi>=0.100.0,<0.200.0 uvicorn[standard]>=0.23.0 -deepeval +deepeval==2.5.5 pandas>=2.0.0 sqlalchemy>=2.0.0 pymysql>=1.1.0 httpx>=0.24.0 -pydantic==1.10.13 +pydantic>=2.0,<3.0 cryptography>=41.0.0 +python-multipart>=0.0.7 +chromadb==1.5.8 +langchain>=0.2.0,<1.0.0 +langchain-core>=0.2.0,<1.0.0 +langchain-community>=0.2.0,<1.0.0 +pypdf>=3.0.0 diff --git a/studio-eval/tests/test_eval_service.py b/studio-eval/tests/test_eval_service.py index 5e26c8f..5fdecbe 100644 --- a/studio-eval/tests/test_eval_service.py +++ b/studio-eval/tests/test_eval_service.py @@ -1,4 +1,7 @@ from app.services import eval_service +from app.services import dataset_service +from app.compat import ensure_pydantic_v2_apis +from app.models.eval_models import SynthesizeFromDocRequest def test_normalize_context_handles_json_string_list(): @@ -12,4 +15,195 @@ def test_normalize_context_handles_plain_string(): def test_format_exception_uses_class_name_when_message_empty(): - assert eval_service._format_exception(AssertionError()) == "AssertionError" \ No newline at end of file + assert eval_service._format_exception(AssertionError()) == "AssertionError" + + +def test_run_synthesizer_applies_pydantic_compat(monkeypatch): + compat_calls = [] + + def fake_ensure_pydantic_v2_apis(): + compat_calls.append(True) + + class FakeSynthesizer: + def __init__(self, model): + self.model = model + + def generate_goldens_from_contexts(self, contexts, max_goldens_per_context): + return [ + { + "contexts": contexts, + "max_goldens_per_context": max_goldens_per_context, + "model": self.model, + } + ] + + import sys + from types import ModuleType + + deepeval_module = ModuleType("deepeval") + synthesizer_module = ModuleType("deepeval.synthesizer") + synthesizer_module.Synthesizer = FakeSynthesizer + + monkeypatch.setattr(dataset_service, "ensure_pydantic_v2_apis", fake_ensure_pydantic_v2_apis) + monkeypatch.setitem(sys.modules, "deepeval", deepeval_module) + monkeypatch.setitem(sys.modules, "deepeval.synthesizer", synthesizer_module) + monkeypatch.setitem(sys.modules, "deepeval.synthesizer.config", ModuleType("deepeval.synthesizer.config")) + + class Payload: + async_mode = False + max_concurrent = 1 + input_quality_threshold = 0.4 + num_evolutions = 1 + + result = dataset_service._run_synthesizer(Payload(), "judge", [["context"]], 2) + + assert compat_calls == [True] + assert result[0]["max_goldens_per_context"] == 2 + + +def test_request_dataset_cancellation_stops_registered_work(): + class FakeTask: + def __init__(self): + self.cancelled = False + + def done(self): + return False + + def cancel(self): + self.cancelled = True + + class FakeProcess: + def __init__(self): + self.terminated = False + + def is_alive(self): + return True + + def terminate(self): + self.terminated = True + + dataset_id = "dataset-123" + task = FakeTask() + process = FakeProcess() + + dataset_service._cancelled_datasets.discard(dataset_id) + dataset_service._synthesis_tasks[dataset_id] = task + dataset_service._synthesis_processes[dataset_id] = process + + try: + dataset_service.request_dataset_cancellation(dataset_id) + + assert dataset_id in dataset_service._cancelled_datasets + assert task.cancelled is True + assert process.terminated is True + finally: + dataset_service._cancelled_datasets.discard(dataset_id) + dataset_service._synthesis_tasks.pop(dataset_id, None) + dataset_service._synthesis_processes.pop(dataset_id, None) + + +def test_ensure_pydantic_v2_apis_aliases_invalid_collection_exception(monkeypatch): + import chromadb.errors as chroma_errors + + monkeypatch.delattr(chroma_errors, "InvalidCollectionException", raising=False) + + ensure_pydantic_v2_apis() + + assert chroma_errors.InvalidCollectionException is chroma_errors.NotFoundError + + +def test_resolve_doc_generation_limits_matches_target_without_overshoot(): + payload = SynthesizeFromDocRequest( + name="dataset", + model_name="qwen2.5:14b", + target_goldens=10, + max_goldens_per_context=5, + max_contexts=5, + ) + + assert dataset_service._resolve_doc_generation_limits(payload) == (5, 2) + + +def test_resolve_doc_generation_limits_falls_back_when_target_exceeds_user_cap(): + payload = SynthesizeFromDocRequest( + name="dataset", + model_name="qwen2.5:14b", + target_goldens=17, + max_goldens_per_context=2, + max_contexts=5, + ) + + assert dataset_service._resolve_doc_generation_limits(payload) == (5, 4) + + +def test_run_synthesizer_from_docs_uses_resolved_limits(monkeypatch): + compat_calls = [] + chunker_patch_calls = [] + + def fake_ensure_pydantic_v2_apis(): + compat_calls.append(True) + + def fake_patch_deepeval_document_chunker(): + chunker_patch_calls.append(True) + + class FakeSynthesizer: + def __init__(self, **kwargs): + self.kwargs = kwargs + + def generate_goldens_from_docs(self, **kwargs): + return [kwargs] + + class FakeContextConstructionConfig: + def __init__(self, **kwargs): + self.kwargs = kwargs + + class FakeEvolutionConfig: + def __init__(self, **kwargs): + self.kwargs = kwargs + + class FakeFiltrationConfig: + def __init__(self, **kwargs): + self.kwargs = kwargs + + import sys + from types import ModuleType + + deepeval_module = ModuleType("deepeval") + synthesizer_module = ModuleType("deepeval.synthesizer") + config_module = ModuleType("deepeval.synthesizer.config") + compat_module = ModuleType("app.compat") + + synthesizer_module.Synthesizer = FakeSynthesizer + config_module.ContextConstructionConfig = FakeContextConstructionConfig + config_module.EvolutionConfig = FakeEvolutionConfig + config_module.FiltrationConfig = FakeFiltrationConfig + compat_module.patch_deepeval_document_chunker = fake_patch_deepeval_document_chunker + + monkeypatch.setattr(dataset_service, "ensure_pydantic_v2_apis", fake_ensure_pydantic_v2_apis) + monkeypatch.setitem(sys.modules, "deepeval", deepeval_module) + monkeypatch.setitem(sys.modules, "deepeval.synthesizer", synthesizer_module) + monkeypatch.setitem(sys.modules, "deepeval.synthesizer.config", config_module) + monkeypatch.setitem(sys.modules, "app.compat", compat_module) + + payload = SynthesizeFromDocRequest( + name="dataset", + model_name="qwen2.5:14b", + target_goldens=10, + max_goldens_per_context=5, + max_contexts=5, + chunk_size=1024, + chunk_overlap=64, + max_context_length=3, + ) + + result = dataset_service._run_synthesizer_from_docs( + "/tmp/source.pdf", + payload, + "judge", + "embedder", + ) + + assert compat_calls == [True] + assert chunker_patch_calls == [True] + assert result[0]["max_goldens_per_context"] == 2 + assert result[0]["context_construction_config"].kwargs["max_contexts_per_document"] == 5 \ No newline at end of file diff --git a/studio-eval/tests/test_timeout_config.py b/studio-eval/tests/test_timeout_config.py index 9466403..1eed5bb 100644 --- a/studio-eval/tests/test_timeout_config.py +++ b/studio-eval/tests/test_timeout_config.py @@ -2,6 +2,8 @@ import os import sys +from app import compat + def _reload_module(module_name: str): sys.modules.pop(module_name, None) @@ -39,4 +41,14 @@ def test_ollama_generate_timeout_can_be_enabled(monkeypatch): ollama_service = _reload_module("app.services.ollama_service") - assert ollama_service.OLLAMA_GENERATE_TIMEOUT == 240.0 \ No newline at end of file + assert ollama_service.OLLAMA_GENERATE_TIMEOUT == 240.0 + + +def test_deepeval_network_timeouts_disabled_by_default(monkeypatch): + monkeypatch.delenv("DEEPEVAL_TELEMETRY_OPT_OUT", raising=False) + monkeypatch.delenv("DEEPEVAL_UPDATE_WARNING_OPT_OUT", raising=False) + + compat.ensure_pydantic_v2_apis() + + assert os.getenv("DEEPEVAL_TELEMETRY_OPT_OUT") == "YES" + assert os.getenv("DEEPEVAL_UPDATE_WARNING_OPT_OUT") == "YES" \ No newline at end of file diff --git a/studio-frontend/Dockerfile b/studio-frontend/Dockerfile index 2d2331c..49c6b14 100644 --- a/studio-frontend/Dockerfile +++ b/studio-frontend/Dockerfile @@ -31,6 +31,7 @@ RUN pnpm install # Copy source code COPY . . +COPY --from=sample_workflows . ./sample-workflows # Build the app (separate stage target so dev compose stops here) RUN pnpm build @@ -70,6 +71,7 @@ WORKDIR /usr/src COPY --from=pruned /usr/src/package.json /usr/src/pnpm-workspace.yaml ./ COPY --from=pruned /usr/src/packages ./packages COPY --from=pruned /usr/src/node_modules ./node_modules +COPY --from=pruned /usr/src/sample-workflows ./sample-workflows EXPOSE 3000 diff --git a/studio-frontend/docker-compose.dev.yml b/studio-frontend/docker-compose.dev.yml deleted file mode 100644 index 3624dcb..0000000 --- a/studio-frontend/docker-compose.dev.yml +++ /dev/null @@ -1,47 +0,0 @@ -version: '3.8' - -services: - finetuning-server: - image: opea/finetuning:latest - container_name: finetuning-server - user: "0:0" - ipc: host - ports: - - "8015:8015" - - "8265:8265" - environment: - - http_proxy=${http_proxy} - - https_proxy=${https_proxy} - - no_proxy=${no_proxy} - volumes: - - finetune-output:/home/user/comps/finetuning/src/output - restart: unless-stopped - - studio-frontend: - image: studio-frontend:latest - build: - context: . - dockerfile: Dockerfile - container_name: studio-frontend-dev - ports: - - "3000:3000" - - "8088:8088" - volumes: - - .:/usr/src - - node_modules:/usr/src/node_modules - - pnpm_store:/usr/src/.pnpm-store - - /usr/src/packages/ui/build - - finetune-output:/tmp/finetuning/output - command: ["sh", "-c", "pnpm install && pnpm dev"] - environment: - - http_proxy=${http_proxy} - - https_proxy=${https_proxy} - - no_proxy=${no_proxy} - stdin_open: true - tty: true - restart: unless-stopped - -volumes: - node_modules: - pnpm_store: - finetune-output: \ No newline at end of file diff --git a/studio-frontend/packages/server/package.json b/studio-frontend/packages/server/package.json index 1cb36b1..d504d98 100644 --- a/studio-frontend/packages/server/package.json +++ b/studio-frontend/packages/server/package.json @@ -52,11 +52,13 @@ "http-status-codes": "^2.3.0", "https-proxy-agent": "^7.0.4", "lodash": "^4.17.21", + "mammoth": "^1.11.0", "moment": "^2.29.3", "moment-timezone": "^0.5.34", "multer": "^1.4.5-lts.1", "mysql2": "^3.11.3", "pg": "^8.11.1", + "pdf-parse": "^1.1.1", "reflect-metadata": "^0.1.13", "sanitize-html": "^2.11.0", "socket.io": "^4.6.1", diff --git a/studio-frontend/packages/server/src/routes/evaluation/index.ts b/studio-frontend/packages/server/src/routes/evaluation/index.ts index e819626..fd8086f 100644 --- a/studio-frontend/packages/server/src/routes/evaluation/index.ts +++ b/studio-frontend/packages/server/src/routes/evaluation/index.ts @@ -1,8 +1,11 @@ import express from 'express' import axios from 'axios' +import multer from 'multer' import { Request, Response, NextFunction } from 'express' +import { parseDatasetUpload } from '../../services/evaluationUploads' const router: express.Router = express.Router() +const upload = multer({ storage: multer.memoryStorage() }) const getStudioServerUrl = () => process.env.STUDIO_SERVER_URL || 'http://studio-backend.studio.svc.cluster.local:5000' @@ -29,17 +32,23 @@ const proxy = async ( targetPath: string, req: Request, res: Response, - next: NextFunction + next: NextFunction, + options?: { + data?: unknown + headers?: Record + timeout?: number + } ) => { try { const url = `${getStudioServerUrl()}/${targetPath}` + const hasOverrideData = Boolean(options && Object.prototype.hasOwnProperty.call(options, 'data')) const response = await axios({ method, url, - data: ['post', 'put', 'patch'].includes(method) ? req.body : undefined, + data: hasOverrideData ? options?.data : ['post', 'put', 'patch'].includes(method) ? req.body : undefined, params: req.query, - headers: { 'Content-Type': 'application/json' }, - timeout: 60_000 + headers: options?.headers || { 'Content-Type': 'application/json' }, + timeout: options?.timeout ?? 60_000 }) return res.status(response.status).json(response.data) } catch (error: unknown) { @@ -127,18 +136,67 @@ router.get('/datasets/:id', (req: Request, res: Response, next: NextFunction) => proxy('get', `studio-backend/evaluation/datasets/${req.params.id}`, req, res, next) ) -router.post('/datasets', (req: Request, res: Response, next: NextFunction) => - proxy('post', 'studio-backend/evaluation/datasets', req, res, next) -) +router.post('/datasets', upload.single('file'), async (req: Request, res: Response, next: NextFunction) => { + try { + const payload = req.file ? parseDatasetUpload(req.file, req.body as Record) : req.body + return proxy('post', 'studio-backend/evaluation/datasets', req, res, next, { data: payload }) + } catch (error) { + const message = error instanceof Error ? error.message : 'Failed to process dataset upload.' + return res.status(400).json({ message }) + } +}) + +router.post('/datasets/synthesize', upload.single('file'), async (req: Request, res: Response, next: NextFunction) => { + try { + if (!req.file) { + return res.status(400).json({ message: 'A source file is required for synthesis.' }) + } + const FormDataLib = require('form-data') + const fd = new FormDataLib() + fd.append('file', req.file.buffer, { + filename: req.file.originalname, + contentType: req.file.mimetype || 'application/octet-stream', + }) + // Forward all text fields unchanged + for (const [key, value] of Object.entries(req.body as Record)) { + if (value !== undefined && value !== null) { + fd.append(key, String(value)) + } + } + return proxy('post', 'studio-backend/evaluation/datasets/synthesize', req, res, next, { + data: fd, + headers: fd.getHeaders(), + }) + } catch (error) { + const message = error instanceof Error ? error.message : 'Failed to process synthesis upload.' + return res.status(400).json({ message }) + } +}) -router.post('/datasets/synthesize', (req: Request, res: Response, next: NextFunction) => - proxy('post', 'studio-backend/evaluation/datasets/synthesize', req, res, next) +router.put('/datasets/:id', (req: Request, res: Response, next: NextFunction) => + proxy('put', `studio-backend/evaluation/datasets/${req.params.id}`, req, res, next) ) router.delete('/datasets/:id', (req: Request, res: Response, next: NextFunction) => proxy('delete', `studio-backend/evaluation/datasets/${req.params.id}`, req, res, next) ) +router.post('/datasets/:id/stop', (req: Request, res: Response, next: NextFunction) => + proxy('post', `studio-backend/evaluation/datasets/${req.params.id}/stop`, req, res, next) +) + +router.post('/datasets/:id/entries', (req: Request, res: Response, next: NextFunction) => + proxy('post', `studio-backend/evaluation/datasets/${req.params.id}/entries`, req, res, next) +) + +router.put('/datasets/:id/entries/:entryId', (req: Request, res: Response, next: NextFunction) => + proxy('put', `studio-backend/evaluation/datasets/${req.params.id}/entries/${req.params.entryId}`, req, res, next) +) + +router.delete('/datasets/:id/entries/:entryId', (req: Request, res: Response, next: NextFunction) => + proxy('delete', `studio-backend/evaluation/datasets/${req.params.id}/entries/${req.params.entryId}`, req, res, next) +) + // ── Runs ────────────────────────────────────────────────────────────────────── router.get('/runs', (req: Request, res: Response, next: NextFunction) => proxy('get', 'studio-backend/evaluation/runs', req, res, next) @@ -152,6 +210,10 @@ router.post('/runs', (req: Request, res: Response, next: NextFunction) => proxy('post', 'studio-backend/evaluation/runs', req, res, next) ) +router.post('/runs/:id/stop', (req: Request, res: Response, next: NextFunction) => + proxy('post', `studio-backend/evaluation/runs/${req.params.id}/stop`, req, res, next) +) + router.delete('/runs/:id', (req: Request, res: Response, next: NextFunction) => proxy('delete', `studio-backend/evaluation/runs/${req.params.id}`, req, res, next) ) diff --git a/studio-frontend/packages/server/src/services/chatflows/index.ts b/studio-frontend/packages/server/src/services/chatflows/index.ts index e230089..e67134f 100644 --- a/studio-frontend/packages/server/src/services/chatflows/index.ts +++ b/studio-frontend/packages/server/src/services/chatflows/index.ts @@ -11,25 +11,7 @@ import { getErrorMessage } from '../../errors/utils' import { getAppVersion } from '../../utils' import { getRunningExpressApp } from '../../utils/getRunningExpressApp' import logger from '../../utils/logger' -import axios, { AxiosRequestConfig } from 'axios' -import { HttpsProxyAgent } from 'https-proxy-agent' - -// Configure github axios to support HTTP_PROXY/HTTPS_PROXY environment variables -const getGithubAxiosConfig = (): AxiosRequestConfig => { - const http_proxy = process.env.http_proxy || process.env.HTTP_PROXY - const agent = (http_proxy && http_proxy.trim() !== "") ? new HttpsProxyAgent(http_proxy) : undefined - - return { - headers: { - Accept: 'application/vnd.github.v3+json', - }, - proxy: false, - ...(agent && { - httpAgent: agent, - httpsAgent: agent, - }), - } -} +import axios from 'axios' const SAMPLE_WORKFLOWS_DIR = process.env.SAMPLE_WORKFLOWS_DIR || path.resolve(process.cwd(), '..', '..', 'sample-workflows') @@ -137,37 +119,9 @@ const getAllChatflowsbyUserId = async (userid: string, type?: ChatflowType): Pro const importSampleChatflowsbyUserId = async (userid: string, type?: ChatflowType): Promise => { try { - const axiosConfig = getGithubAxiosConfig() - console.log('Importing sample chatflows for user:', userid) - - let chatflows: Partial[] = [] - - try { - chatflows = await loadLocalSampleChatflows(userid, type) - logger.info(`[server]: Loaded ${chatflows.length} sample chatflows from local directory ${SAMPLE_WORKFLOWS_DIR}`) - } catch (localError) { - logger.warn(`[server]: Falling back to GitHub sample workflows: ${getErrorMessage(localError)}`) - - const response = await axios.get( - 'https://api.github.com/repos/opea-project/GenAIStudio/contents/sample-workflows', - axiosConfig - ) - - const files = response.data.filter((item: any) => item.type === 'file') - for (const file of files) { - const fileResponse = await axios.get(file.download_url, axiosConfig) - const parsedFlowData = fileResponse.data - chatflows.push({ - userid, - name: file.name.replace('.json', ''), - flowData: JSON.stringify(parsedFlowData), - type: type || 'OPEA', - deployed: false, - isPublic: false - }) - } - } + const chatflows = await loadLocalSampleChatflows(userid, type) + logger.info(`[server]: Loaded ${chatflows.length} sample chatflows from local directory ${SAMPLE_WORKFLOWS_DIR}`) const insertResponse = await importChatflows(chatflows) return insertResponse diff --git a/studio-frontend/packages/server/src/services/evaluationUploads/index.ts b/studio-frontend/packages/server/src/services/evaluationUploads/index.ts new file mode 100644 index 0000000..a06f8f0 --- /dev/null +++ b/studio-frontend/packages/server/src/services/evaluationUploads/index.ts @@ -0,0 +1,173 @@ +import path from 'path' + +type DatasetEntryPayload = { + input: string + expected_output?: string + context?: string[] +} + +type DatasetCreatePayload = { + name: string + description?: string + entries: DatasetEntryPayload[] +} + +const DATASET_FILE_TYPES = new Set(['.json', '.jsonl']) +const DATASET_INPUT_KEYS = ['input', 'question', 'prompt', 'query'] +const DATASET_EXPECTED_KEYS = ['expected_output', 'expectedOutput', 'expected', 'answer', 'ground_truth', 'groundTruth'] +const DATASET_CONTEXT_KEYS = ['context', 'contexts', 'retrieval_context', 'retrievalContexts', 'reference_context'] + +const getStringField = (value: unknown): string => { + if (typeof value !== 'string') return '' + return value.trim() +} + +const deriveNameFromFilename = (filename: string): string => path.basename(filename, path.extname(filename)).trim() + +const getExtension = (filename: string): string => path.extname(filename || '').toLowerCase() + +const normalizeWhitespace = (value: string): string => value.replace(/\r\n/g, '\n').replace(/\uFEFF/g, '').trim() + +const getFirstNonEmptyValue = (record: Record, keys: string[]): string => { + for (const key of keys) { + const value = record[key] + if (typeof value === 'string' && value.trim()) return value.trim() + } + return '' +} + +const normalizeContext = (value: unknown): string[] | undefined => { + if (value == null) return undefined + + if (Array.isArray(value)) { + const normalized = value + .map((item) => (typeof item === 'string' ? item.trim() : String(item || '').trim())) + .filter(Boolean) + return normalized.length ? normalized : undefined + } + + if (typeof value === 'string') { + const trimmed = value.trim() + if (!trimmed) return undefined + + try { + const parsed = JSON.parse(trimmed) + if (Array.isArray(parsed)) return normalizeContext(parsed) + } catch { + return [trimmed] + } + + return [trimmed] + } + + return [String(value).trim()].filter(Boolean) +} + +const ensureDatasetExtension = (filename: string) => { + const extension = getExtension(filename) + if (!DATASET_FILE_TYPES.has(extension)) { + throw new Error('Dataset upload must be a .json or .jsonl file.') + } + return extension +} + +const normalizeDatasetEntry = (item: unknown, index: number): DatasetEntryPayload => { + if (typeof item === 'string') { + const input = item.trim() + if (!input) throw new Error(`Dataset entry ${index + 1} is empty.`) + return { input } + } + + if (!item || typeof item !== 'object' || Array.isArray(item)) { + throw new Error(`Dataset entry ${index + 1} must be a JSON object.`) + } + + const record = item as Record + const input = getFirstNonEmptyValue(record, DATASET_INPUT_KEYS) + if (!input) { + throw new Error(`Dataset entry ${index + 1} is missing an input field.`) + } + + const expectedOutput = getFirstNonEmptyValue(record, DATASET_EXPECTED_KEYS) + let context: string[] | undefined + for (const key of DATASET_CONTEXT_KEYS) { + context = normalizeContext(record[key]) + if (context?.length) break + } + + return { + input, + ...(expectedOutput ? { expected_output: expectedOutput } : {}), + ...(context?.length ? { context } : {}) + } +} + +const parseJsonDatasetEntries = (rawText: string): { name?: string; description?: string; entries: DatasetEntryPayload[] } => { + const parsed = JSON.parse(rawText) + const topLevel = parsed && typeof parsed === 'object' && !Array.isArray(parsed) ? (parsed as Record) : null + + let rawEntries: unknown[] = [] + if (Array.isArray(parsed)) { + rawEntries = parsed + } else if (topLevel && Array.isArray(topLevel.entries)) { + rawEntries = topLevel.entries + } else if (topLevel && Array.isArray(topLevel.items)) { + rawEntries = topLevel.items + } else if (topLevel && Array.isArray(topLevel.data)) { + rawEntries = topLevel.data + } else if (topLevel) { + rawEntries = [topLevel] + } + + if (!rawEntries.length) { + throw new Error('Dataset file does not contain any entries.') + } + + return { + name: topLevel ? getStringField(topLevel.name) || undefined : undefined, + description: topLevel ? getStringField(topLevel.description) || undefined : undefined, + entries: rawEntries.map((entry, index) => normalizeDatasetEntry(entry, index)) + } +} + +const parseJsonlDatasetEntries = (rawText: string): DatasetEntryPayload[] => { + const lines = rawText + .split(/\r?\n/) + .map((line) => line.trim()) + .filter(Boolean) + + if (!lines.length) { + throw new Error('Dataset JSONL file is empty.') + } + + return lines.map((line, index) => { + try { + return normalizeDatasetEntry(JSON.parse(line), index) + } catch (error) { + const message = error instanceof Error ? error.message : 'Invalid JSONL entry.' + throw new Error(`Invalid JSONL on line ${index + 1}: ${message}`) + } + }) +} + +export const parseDatasetUpload = (file: Express.Multer.File, fields: Record): DatasetCreatePayload => { + const extension = ensureDatasetExtension(file.originalname) + const rawText = normalizeWhitespace(file.buffer.toString('utf8')) + if (!rawText) { + throw new Error('Uploaded dataset file is empty.') + } + + const parsed = extension === '.jsonl' ? { entries: parseJsonlDatasetEntries(rawText) } : parseJsonDatasetEntries(rawText) + const name = getStringField(fields.name) || parsed.name || deriveNameFromFilename(file.originalname) + if (!name) { + throw new Error('Dataset name is required.') + } + + const description = getStringField(fields.description) || parsed.description + + return { + name, + ...(description ? { description } : {}), + entries: parsed.entries + } +} \ No newline at end of file diff --git a/studio-frontend/packages/ui/src/api/evaluation.js b/studio-frontend/packages/ui/src/api/evaluation.js index 38e13fc..4d33913 100644 --- a/studio-frontend/packages/ui/src/api/evaluation.js +++ b/studio-frontend/packages/ui/src/api/evaluation.js @@ -43,8 +43,28 @@ const evaluationApi = { // Delete an evaluation run deleteRun: (runId) => client.delete(`/evaluation/runs/${runId}`), + // Stop a running/pending evaluation run + stopRun: (runId) => client.post(`/evaluation/runs/${runId}/stop`), + // Delete a dataset deleteDataset: (datasetId) => client.delete(`/evaluation/datasets/${datasetId}`), + + // Stop a pending/synthesizing dataset job + stopDataset: (datasetId) => client.post(`/evaluation/datasets/${datasetId}/stop`), + + // Update dataset metadata (name / description) + updateDataset: (datasetId, payload) => client.put(`/evaluation/datasets/${datasetId}`, payload), + + // Update a single entry inside a dataset + updateEntry: (datasetId, entryId, payload) => + client.put(`/evaluation/datasets/${datasetId}/entries/${entryId}`, payload), + + // Add one or more entries to an existing dataset + addEntries: (datasetId, payload) => client.post(`/evaluation/datasets/${datasetId}/entries`, payload), + + // Delete a single entry from a dataset + deleteEntry: (datasetId, entryId) => + client.delete(`/evaluation/datasets/${datasetId}/entries/${entryId}`), } export default evaluationApi diff --git a/studio-frontend/packages/ui/src/ui-component/file/FileUploadArea.jsx b/studio-frontend/packages/ui/src/ui-component/file/FileUploadArea.jsx new file mode 100644 index 0000000..665d594 --- /dev/null +++ b/studio-frontend/packages/ui/src/ui-component/file/FileUploadArea.jsx @@ -0,0 +1,325 @@ +import { useState, useRef } from 'react' +import PropTypes from 'prop-types' + +// material-ui +import { + Box, + Button, + LinearProgress, + Paper, + Stack, + Typography, + IconButton, + Chip, + Alert +} from '@mui/material' +import { useTheme, alpha } from '@mui/material/styles' + +// icons +import { + IconUpload, + IconFile, + IconX, + IconCheck, + IconAlertTriangle +} from '@tabler/icons-react' + +const FileUploadArea = ({ + onFileUpload, + acceptedTypes = ['.json', '.jsonl', '.csv'], + maxSizeMB = 10, + error = null, + title = 'Drop your training dataset file here or click to browse', + subtitle, + buttonLabel = 'Choose File' +}) => { + const theme = useTheme() + const fileInputRef = useRef(null) + + const [dragActive, setDragActive] = useState(false) + const [uploadedFile, setUploadedFile] = useState(null) + const [uploadProgress, setUploadProgress] = useState(0) + const [uploadError, setUploadError] = useState(null) + const [preview, setPreview] = useState(null) + + const maxSizeBytes = maxSizeMB * 1024 * 1024 + + const validateFile = (file) => { + const errors = [] + + // Check file type + const fileExtension = '.' + file.name.split('.').pop().toLowerCase() + if (!acceptedTypes.includes(fileExtension)) { + errors.push(`File type ${fileExtension} not supported. Accepted types: ${acceptedTypes.join(', ')}`) + } + + // Check file size + if (file.size > maxSizeBytes) { + errors.push(`File size (${(file.size / 1024 / 1024).toFixed(2)}MB) exceeds limit of ${maxSizeMB}MB`) + } + + return errors + } + + const previewFile = async (file) => { + try { + const text = await file.text() + const lines = text.split('\n').slice(0, 5) // First 5 lines + setPreview({ + lines, + totalSize: file.size, + totalLines: text.split('\n').length + }) + } catch (error) { + console.error('Error previewing file:', error) + setPreview(null) + } + } + + const handleFileUpload = async (file) => { + const validationErrors = validateFile(file) + if (validationErrors.length > 0) { + setUploadError(validationErrors[0]) + return + } + + setUploadError(null) + setUploadProgress(0) + + // Simulate upload progress + const uploadInterval = setInterval(() => { + setUploadProgress(prev => { + if (prev >= 90) { + clearInterval(uploadInterval) + return prev + } + return prev + 10 + }) + }, 100) + + try { + // Simulate upload delay + await new Promise(resolve => setTimeout(resolve, 1000)) + + setUploadProgress(100) + + // Store the actual File object so parent can upload the real Blob/File + setUploadedFile(file) + + // Generate preview + await previewFile(file) + + // Notify parent with the real File object (not a plain JS object) + // Parent will perform the FormData upload and receive server response + onFileUpload(file) + + setTimeout(() => setUploadProgress(0), 500) + } catch (error) { + console.error('Upload error:', error) + setUploadError('Failed to upload file. Please try again.') + setUploadProgress(0) + } + } + + const handleDrag = (e) => { + e.preventDefault() + e.stopPropagation() + if (e.type === 'dragenter' || e.type === 'dragover') { + setDragActive(true) + } else if (e.type === 'dragleave') { + setDragActive(false) + } + } + + const handleDrop = (e) => { + e.preventDefault() + e.stopPropagation() + setDragActive(false) + + if (e.dataTransfer.files && e.dataTransfer.files[0]) { + handleFileUpload(e.dataTransfer.files[0]) + } + } + + const handleFileSelect = (e) => { + if (e.target.files && e.target.files[0]) { + handleFileUpload(e.target.files[0]) + } + } + + const removeFile = () => { + setUploadedFile(null) + setPreview(null) + setUploadError(null) + setUploadProgress(0) + onFileUpload(null) + + if (fileInputRef.current) { + fileInputRef.current.value = '' + } + } + + const openFileDialog = () => { + fileInputRef.current?.click() + } + + const formatFileSize = (bytes) => { + if (bytes === 0) return '0 Bytes' + const k = 1024 + const sizes = ['Bytes', 'KB', 'MB', 'GB'] + const i = Math.floor(Math.log(bytes) / Math.log(k)) + return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i] + } + + const helperText = subtitle || `Supported formats: ${acceptedTypes.join(', ')} (Max filesize: ${maxSizeMB}MB)` + + return ( + + + + {!uploadedFile ? ( + + + + + + + {title} + + + {helperText} + + + + + + + ) : ( + + + {/* File Info */} + + + + + {uploadedFile.name} + + } + label="Selected" + color="primary" + size="small" + variant="outlined" + /> + + + + + + + + {formatFileSize(uploadedFile.size)} + + + {/* Upload Progress */} + {uploadProgress > 0 && uploadProgress < 100 && ( + + )} + + {/* File Preview */} + {preview && ( + + + Preview ({preview.totalLines} total lines): + + + + {preview.lines.join('\n')} + {preview.lines.length < preview.totalLines && '\n...'} + + + + )} + + + )} + + {/* Error Display */} + {(uploadError || error) && ( + } + > + {uploadError || error} + + )} + + ) +} + +FileUploadArea.propTypes = { + onFileUpload: PropTypes.func.isRequired, + acceptedTypes: PropTypes.arrayOf(PropTypes.string), + maxSizeMB: PropTypes.number, + error: PropTypes.string, + title: PropTypes.string, + subtitle: PropTypes.string, + buttonLabel: PropTypes.string +} + +export default FileUploadArea \ No newline at end of file diff --git a/studio-frontend/packages/ui/src/views/evaluation/CreateRunModal.jsx b/studio-frontend/packages/ui/src/views/evaluation/CreateRunModal.jsx index eb20ce2..6fea855 100644 --- a/studio-frontend/packages/ui/src/views/evaluation/CreateRunModal.jsx +++ b/studio-frontend/packages/ui/src/views/evaluation/CreateRunModal.jsx @@ -169,7 +169,7 @@ const CreateRunModal = ({ open, onClose, onRunCreated }) => { ]) .then(([sbRes, dsRes, mdRes]) => { setSandboxes(sbRes.data?.sandboxes || []) - setDatasets(dsRes.data || []) + setDatasets((dsRes.data || []).filter((ds) => ds.status === 'completed')) setModels(mdRes.data || []) }) .catch(() => setError('Failed to load options.')) diff --git a/studio-frontend/packages/ui/src/views/evaluation/DatasetDetailDialog.jsx b/studio-frontend/packages/ui/src/views/evaluation/DatasetDetailDialog.jsx new file mode 100644 index 0000000..aaec8ef --- /dev/null +++ b/studio-frontend/packages/ui/src/views/evaluation/DatasetDetailDialog.jsx @@ -0,0 +1,769 @@ +import { useState, useEffect, useCallback } from 'react' +import PropTypes from 'prop-types' + +// material-ui +import { + Alert, + Box, + Button, + Chip, + CircularProgress, + Dialog, + DialogActions, + DialogContent, + DialogTitle, + IconButton, + Paper, + Stack, + Table, + TableBody, + TableCell, + TableContainer, + TableHead, + TablePagination, + TableRow, + TextField, + Tooltip, + Typography +} from '@mui/material' +import { styled } from '@mui/material/styles' +import { tableCellClasses } from '@mui/material/TableCell' + +// icons +import { IconCheck, IconPencil, IconPlus, IconTrash, IconX } from '@tabler/icons-react' + +// API +import evaluationApi from '@/api/evaluation' + +// ── Constants ───────────────────────────────────────────────────────────────── + +const ROWS_PER_PAGE = 10 + +const STATUS_COLOR = { + pending: 'default', + synthesizing: 'warning', + completed: 'success', + failed: 'error' +} + +// ── Styled components (same pattern as DatasetTab) ──────────────────────────── + +const StyledTableCell = styled(TableCell)(({ theme }) => ({ + borderColor: theme.palette.grey[900] + 25, + [`&.${tableCellClasses.head}`]: { + color: theme.palette.grey[900] + }, + [`&.${tableCellClasses.body}`]: { + fontSize: 14 + } +})) + +const StyledTableRow = styled(TableRow)(() => ({ + '&:last-child td, &:last-child th': { + border: 0 + } +})) + +// ── Helpers ─────────────────────────────────────────────────────────────────── + +const contextToText = (context) => { + if (!context || context.length === 0) return '' + return context.join('\n') +} + +const textToContext = (text) => { + if (!text.trim()) return [] + return text + .split('\n') + .map((s) => s.trim()) + .filter(Boolean) +} + +// ── DatasetDetailDialog ─────────────────────────────────────────────────────── + +const DatasetDetailDialog = ({ open, datasetId, onClose, onDatasetChanged }) => { + const [loading, setLoading] = useState(false) + const [dataset, setDataset] = useState(null) + + // Editable metadata + const [name, setName] = useState('') + const [description, setDescription] = useState('') + const [savingMeta, setSavingMeta] = useState(false) + const [metaError, setMetaError] = useState('') + + // Per-row edit state + // editingRow: null | { id: number|null, input, expected_output, contextText } + // id === null means new entry + const [editingRow, setEditingRow] = useState(null) + const [savingRow, setSavingRow] = useState(false) + const [rowError, setRowError] = useState('') + + // Entry deletion + const [confirmDeleteId, setConfirmDeleteId] = useState(null) + const [deletingEntry, setDeletingEntry] = useState(false) + + // Pagination + const [page, setPage] = useState(0) + + // ── Data loading ────────────────────────────────────────────────────────── + + const loadDataset = useCallback(async (id) => { + setLoading(true) + try { + const res = await evaluationApi.getDataset(id) + const ds = res.data + setDataset(ds) + setName(ds.name || '') + setDescription(ds.description || '') + } catch { + setDataset(null) + } finally { + setLoading(false) + } + }, []) + + useEffect(() => { + if (open && datasetId) { + setPage(0) + setEditingRow(null) + setConfirmDeleteId(null) + setMetaError('') + setRowError('') + setDataset(null) + loadDataset(datasetId) + } + }, [open, datasetId, loadDataset]) + + // ── Metadata ────────────────────────────────────────────────────────────── + + const metaDirty = + dataset !== null && + (name !== (dataset.name || '') || description !== (dataset.description || '')) + + const handleSaveMeta = async () => { + if (!name.trim()) { + setMetaError('Name is required.') + return + } + setSavingMeta(true) + setMetaError('') + try { + const res = await evaluationApi.updateDataset(datasetId, { + name: name.trim(), + description: description.trim() + }) + const updated = res.data + setDataset((prev) => ({ ...prev, name: updated.name, description: updated.description })) + onDatasetChanged && onDatasetChanged({ id: datasetId, name: updated.name, description: updated.description }) + } catch (err) { + setMetaError(err?.response?.data?.message || 'Failed to save metadata.') + } finally { + setSavingMeta(false) + } + } + + // ── Entry editing ───────────────────────────────────────────────────────── + + const handleEditRow = (entry) => { + setRowError('') + setEditingRow({ + id: entry.id, + input: entry.input || '', + expected_output: entry.expected_output || '', + contextText: contextToText(entry.context) + }) + } + + const handleAddRow = () => { + setRowError('') + setEditingRow({ id: null, input: '', expected_output: '', contextText: '' }) + // Navigate to the page where the new row will appear + if (dataset?.entries) { + const newPage = Math.max(0, Math.ceil((dataset.entries.length + 1) / ROWS_PER_PAGE) - 1) + setPage(newPage) + } + } + + const handleCancelEdit = () => { + setEditingRow(null) + setRowError('') + } + + const handleSaveRow = async () => { + if (!editingRow.input.trim()) { + setRowError('Input is required.') + return + } + setSavingRow(true) + setRowError('') + try { + const payload = { + input: editingRow.input.trim(), + expected_output: editingRow.expected_output.trim() || null, + context: textToContext(editingRow.contextText) + } + + if (editingRow.id !== null) { + // Update existing entry + const res = await evaluationApi.updateEntry(datasetId, editingRow.id, payload) + const updated = res.data + setDataset((prev) => ({ + ...prev, + entries: prev.entries.map((e) => (e.id === updated.id ? updated : e)) + })) + } else { + // Add new entry + const res = await evaluationApi.addEntries(datasetId, { entries: [payload] }) + const newEntries = res.data.entries || [] + const updatedEntries = [...(dataset?.entries || []), ...newEntries] + setDataset((prev) => ({ ...prev, entries: updatedEntries })) + onDatasetChanged && onDatasetChanged({ id: datasetId, entry_count: updatedEntries.length }) + } + setEditingRow(null) + } catch (err) { + setRowError(err?.response?.data?.message || 'Failed to save entry.') + } finally { + setSavingRow(false) + } + } + + // ── Entry deletion ──────────────────────────────────────────────────────── + + const handleDeleteEntry = async (entryId) => { + setDeletingEntry(true) + try { + await evaluationApi.deleteEntry(datasetId, entryId) + const updatedEntries = (dataset?.entries || []).filter((e) => e.id !== entryId) + setDataset((prev) => ({ ...prev, entries: updatedEntries })) + onDatasetChanged && onDatasetChanged({ id: datasetId, entry_count: updatedEntries.length }) + setConfirmDeleteId(null) + // Adjust page down if we removed the last entry on the current page + const maxPage = Math.max(0, Math.ceil(updatedEntries.length / ROWS_PER_PAGE) - 1) + setPage((prev) => Math.min(prev, maxPage)) + } catch { + // silently fail — user can retry + } finally { + setDeletingEntry(false) + } + } + + // ── Derived values ──────────────────────────────────────────────────────── + + const entries = dataset?.entries || [] + const pageEntries = entries.slice(page * ROWS_PER_PAGE, page * ROWS_PER_PAGE + ROWS_PER_PAGE) + const showNewRow = editingRow !== null && editingRow.id === null + const isSynthesizing = dataset?.status === 'synthesizing' + + // ── Render ──────────────────────────────────────────────────────────────── + + return ( + + + Dataset Detail + + + + + + + {loading ? ( + + + + ) : !dataset ? ( + Failed to load dataset. + ) : ( + + {/* ── Metadata section ── */} + + + + + Metadata + + + + {entries.length} {entries.length === 1 ? 'entry' : 'entries'} + + + + + setName(e.target.value)} + sx={{ flex: 1 }} + required + /> + setDescription(e.target.value)} + sx={{ flex: 2 }} + /> + + + + {metaError && ( + + {metaError} + + )} + + + + {/* ── Entries table ── */} + + + + Entries + + + + + + + + + {rowError && ( + + {rowError} + + )} + + + + + + # + Input + + Expected Output + + Context + + Actions + + + + + {pageEntries.length === 0 && !showNewRow ? ( + + + + No entries yet. Click "Add Entry" to create one. + + + + ) : ( + pageEntries.map((entry, idx) => { + const globalIdx = page * ROWS_PER_PAGE + idx + 1 + const isEditing = editingRow?.id === entry.id + const isConfirmingDelete = confirmDeleteId === entry.id + + return ( + + {globalIdx} + + {isEditing ? ( + <> + + + setEditingRow((r) => ({ + ...r, + input: e.target.value + })) + } + multiline + minRows={2} + maxRows={6} + size='small' + fullWidth + placeholder='Input (required)' + /> + + + + setEditingRow((r) => ({ + ...r, + expected_output: e.target.value + })) + } + multiline + minRows={2} + maxRows={6} + size='small' + fullWidth + placeholder='Expected output' + /> + + + + setEditingRow((r) => ({ + ...r, + contextText: e.target.value + })) + } + multiline + minRows={2} + maxRows={6} + size='small' + fullWidth + placeholder='One context item per line' + helperText='Each line → one context string' + /> + + + + + + + {savingRow ? ( + + ) : ( + + )} + + + + + + + + + + + + ) : isConfirmingDelete ? ( + <> + + + Delete this entry? This cannot be undone. + + + + + + + + + + ) : ( + <> + + + {entry.input || '—'} + + + + + {entry.expected_output || '—'} + + + + + {entry.context && entry.context.length > 0 + ? entry.context.join('\n') + : '—'} + + + + + + + handleEditRow(entry)} + disabled={ + !!editingRow || isSynthesizing + } + > + + + + + + + + setConfirmDeleteId(entry.id) + } + disabled={ + !!editingRow || isSynthesizing + } + > + + + + + + + + )} + + ) + }) + )} + + {/* New entry row — always shown at bottom when adding */} + {showNewRow && ( + + {entries.length + 1} + + + setEditingRow((r) => ({ ...r, input: e.target.value })) + } + multiline + minRows={2} + maxRows={6} + size='small' + fullWidth + placeholder='Input (required)' + autoFocus + /> + + + + setEditingRow((r) => ({ + ...r, + expected_output: e.target.value + })) + } + multiline + minRows={2} + maxRows={6} + size='small' + fullWidth + placeholder='Expected output' + /> + + + + setEditingRow((r) => ({ + ...r, + contextText: e.target.value + })) + } + multiline + minRows={2} + maxRows={6} + size='small' + fullWidth + placeholder='One context item per line' + helperText='Each line → one context string' + /> + + + + + + + {savingRow ? ( + + ) : ( + + )} + + + + + + + + + + + + )} + +
+
+ + {entries.length > ROWS_PER_PAGE && ( + { + setEditingRow(null) + setConfirmDeleteId(null) + setPage(newPage) + }} + /> + )} +
+
+ )} +
+ + + + +
+ ) +} + +DatasetDetailDialog.propTypes = { + open: PropTypes.bool.isRequired, + datasetId: PropTypes.oneOfType([PropTypes.number, PropTypes.string]), + onClose: PropTypes.func.isRequired, + onDatasetChanged: PropTypes.func +} + +export default DatasetDetailDialog diff --git a/studio-frontend/packages/ui/src/views/evaluation/DatasetTab.jsx b/studio-frontend/packages/ui/src/views/evaluation/DatasetTab.jsx index 7195c75..0d1bfd6 100644 --- a/studio-frontend/packages/ui/src/views/evaluation/DatasetTab.jsx +++ b/studio-frontend/packages/ui/src/views/evaluation/DatasetTab.jsx @@ -1,22 +1,27 @@ -import { useState, useEffect, useCallback } from 'react' +import { useState, useEffect, useCallback, useRef } from 'react' import PropTypes from 'prop-types' // material-ui import { Box, Button, + Chip, CircularProgress, Dialog, DialogActions, DialogContent, DialogTitle, + FormControlLabel, FormControl, IconButton, InputLabel, + LinearProgress, + Menu, MenuItem, Paper, Select, Stack, + Switch, Table, TableBody, TableCell, @@ -28,17 +33,60 @@ import { Tooltip, Typography } from '@mui/material' -import { useTheme, styled } from '@mui/material/styles' +import { styled } from '@mui/material/styles' import { tableCellClasses } from '@mui/material/TableCell' // icons -import { IconPlus, IconRefresh, IconWand, IconX } from '@tabler/icons-react' +import { IconChevronDown, IconEye, IconPlus, IconRefresh, IconTrash, IconWand, IconX, IconPlayerStop } from '@tabler/icons-react' // API import evaluationApi from '@/api/evaluation' +import DatasetDetailDialog from './DatasetDetailDialog' +import FileUploadArea from '@/ui-component/file/FileUploadArea' import { StyledButton } from '@/ui-component/button/StyledButton' +const DATASET_UPLOAD_TYPES = ['.json', '.jsonl'] +const SYNTHESIZE_UPLOAD_TYPES = ['.pdf', '.txt', '.docx'] + +const DEFAULT_SYNTHESIS_OPTIONS = { + targetGoldens: 10, + maxGoldensPerDocument: 5, + maxContexts: 5, + minContexts: 1, + averageChunksPerContext: 3, + chunkSize: 1024, + chunkOverlap: 64, + numEvolutions: 1, + inputQuality: 0.4, + llmTimeout: 0, + asyncMode: false, + maxConcurrent: 1 +} + +const getAcceptedFileLabel = (types) => types.join(', ') + +const deriveNameFromFile = (filename) => { + const lastDot = filename.lastIndexOf('.') + return lastDot > 0 ? filename.slice(0, lastDot) : filename +} + +const parseManualEntries = (entriesText) => { + const trimmed = entriesText.trim() + if (!trimmed) return [] + + try { + const parsed = JSON.parse(trimmed) + return Array.isArray(parsed) ? parsed : [parsed] + } catch { + return trimmed + .split('\n') + .map((line) => line.trim()) + .filter(Boolean) + .map((line) => JSON.parse(line)) + } +} + const StyledTableCell = styled(TableCell)(({ theme }) => ({ borderColor: theme.palette.grey[900] + 25, [`&.${tableCellClasses.head}`]: { @@ -67,12 +115,64 @@ const formatDate = (dateStr) => { } } +const normalizeDatasetName = (value) => (value || '').trim().toLowerCase() + +const statusColor = (status) => { + switch (status) { + case 'completed': + return 'success' + case 'failed': + return 'error' + case 'synthesizing': + return 'primary' + case 'pending': + default: + return 'default' + } +} + +const STATUS_CONFIG = { + pending: { label: 'pending' }, + synthesizing: { label: 'synthesizing' }, + completed: { label: 'completed' }, + failed: { label: 'failed' }, + stopped: { label: 'stopped' }, +} + +const StatusChip = ({ status, error }) => { + const cfg = STATUS_CONFIG[status] || { label: status } + const isInProgress = status === 'synthesizing' || status === 'pending' + const chip = ( + : undefined} + /> + ) + if (status === 'failed' && error) { + return ( + + {chip} + + ) + } + return chip +} + +StatusChip.propTypes = { + status: PropTypes.string, + error: PropTypes.string +} + // ── New Dataset Modal ───────────────────────────────────────────────────────── -const NewDatasetModal = ({ open, onClose, onCreated }) => { +const NewDatasetModal = ({ open, onClose, onCreated, existingDatasetNames }) => { + const missingUploadError = 'Upload a .json or .jsonl file, or paste dataset entries manually.' const [name, setName] = useState('') const [description, setDescription] = useState('') const [entries, setEntries] = useState('') + const [file, setFile] = useState(null) const [submitting, setSubmitting] = useState(false) const [error, setError] = useState('') @@ -80,39 +180,58 @@ const NewDatasetModal = ({ open, onClose, onCreated }) => { setName('') setDescription('') setEntries('') + setFile(null) setError('') onClose() } + const handleFileChange = (nextFile) => { + setFile(nextFile) + setError('') + if (nextFile && !name.trim()) { + setName(deriveNameFromFile(nextFile.name)) + } + } + const handleSubmit = async () => { if (!name.trim()) { setError('Dataset name is required.') return } + + const normalizedName = normalizeDatasetName(name) + const hasDuplicateName = existingDatasetNames.some( + (existingName) => normalizeDatasetName(existingName) === normalizedName + ) + if (hasDuplicateName) { + setError('A dataset with this name already exists. Please choose a different name.') + return + } + + if (!file && !entries.trim()) { + setError(missingUploadError) + return + } + setSubmitting(true) setError('') try { - // Parse entries: newline-separated JSON objects or a JSON array - let parsedEntries = [] - const trimmed = entries.trim() - if (trimmed) { - try { - parsedEntries = JSON.parse(trimmed) - if (!Array.isArray(parsedEntries)) parsedEntries = [parsedEntries] - } catch { - // Try newline-separated JSON objects - parsedEntries = trimmed - .split('\n') - .map((l) => l.trim()) - .filter(Boolean) - .map((l) => JSON.parse(l)) - } + let res + + if (file) { + const formData = new FormData() + formData.append('file', file) + formData.append('name', name.trim()) + if (description.trim()) formData.append('description', description.trim()) + res = await evaluationApi.createDataset(formData) + } else { + res = await evaluationApi.createDataset({ + name: name.trim(), + description: description.trim(), + entries: parseManualEntries(entries) + }) } - const res = await evaluationApi.createDataset({ - name: name.trim(), - description: description.trim(), - entries: parsedEntries - }) + onCreated && onCreated(res.data) handleClose() } catch (err) { @@ -125,13 +244,22 @@ const NewDatasetModal = ({ open, onClose, onCreated }) => { return ( - New Dataset + Import Dataset + { value={description} onChange={(e) => setDescription(e.target.value)} /> - setEntries(e.target.value)} - placeholder={'[{"question": "...", "expected": "..."}]'} - inputProps={{ style: { fontFamily: 'monospace', fontSize: 13 } }} - /> {error && ( {error} @@ -187,16 +304,20 @@ const NewDatasetModal = ({ open, onClose, onCreated }) => { NewDatasetModal.propTypes = { open: PropTypes.bool.isRequired, onClose: PropTypes.func.isRequired, - onCreated: PropTypes.func + onCreated: PropTypes.func, + existingDatasetNames: PropTypes.arrayOf(PropTypes.string) } // ── Synthesize Modal ────────────────────────────────────────────────────────── -const SynthesizeModal = ({ open, onClose, onCreated }) => { - const [sandboxes, setSandboxes] = useState([]) - const [sandboxId, setSandboxId] = useState('') +const SynthesizeModal = ({ open, onClose, onCreated, existingDatasetNames }) => { + const [models, setModels] = useState([]) const [datasetName, setDatasetName] = useState('') - const [numSamples, setNumSamples] = useState(20) + const [description, setDescription] = useState('') + const [modelName, setModelName] = useState('') + const [sourceFile, setSourceFile] = useState(null) + const [options, setOptions] = useState(DEFAULT_SYNTHESIS_OPTIONS) + const [showAdvanced, setShowAdvanced] = useState(false) const [loadingOptions, setLoadingOptions] = useState(false) const [submitting, setSubmitting] = useState(false) const [error, setError] = useState('') @@ -204,45 +325,96 @@ const SynthesizeModal = ({ open, onClose, onCreated }) => { useEffect(() => { if (!open) return setLoadingOptions(true) + setError('') evaluationApi - .getSandboxes() - .then((res) => setSandboxes(res.data || [])) - .catch(() => setError('Failed to load sandboxes.')) + .getModels() + .then((res) => setModels(res.data || [])) + .catch(() => setError('Failed to load judge models.')) .finally(() => setLoadingOptions(false)) }, [open]) const handleClose = () => { - setSandboxId('') setDatasetName('') - setNumSamples(20) + setDescription('') + setModelName('') + setSourceFile(null) + setOptions(DEFAULT_SYNTHESIS_OPTIONS) + setShowAdvanced(false) setError('') onClose() } + const updateOption = (key, value) => { + setOptions((previous) => ({ + ...previous, + [key]: value + })) + } + + const missingSrcError = 'A source file is required to synthesize a dataset.' + + const handleFileChange = (nextFile) => { + setSourceFile(nextFile) + setError('') + if (nextFile && !datasetName.trim()) { + setDatasetName(deriveNameFromFile(nextFile.name)) + } + } + const handleSubmit = async () => { - if (!sandboxId || !datasetName.trim()) { - setError('Sandbox and dataset name are required.') + if (!sourceFile) { + setError(missingSrcError) + return + } + if (!datasetName.trim() || !modelName) { + setError('Dataset name and judge model are required.') + return + } + + const normalizedName = normalizeDatasetName(datasetName) + const hasDuplicateName = existingDatasetNames.some( + (existingName) => normalizeDatasetName(existingName) === normalizedName + ) + if (hasDuplicateName) { + setError('A dataset with this name already exists. Please choose a different name.') return } + setSubmitting(true) setError('') try { - const res = await evaluationApi.synthesizeDataset({ - sandbox_id: sandboxId, - name: datasetName.trim(), - num_samples: numSamples - }) + const formData = new FormData() + formData.append('file', sourceFile) + formData.append('name', datasetName.trim()) + formData.append('model_name', modelName) + formData.append('embed_model_name', 'nomic-embed-text') + formData.append('num_goldens', String(options.targetGoldens)) + formData.append('max_goldens_per_document', String(options.maxGoldensPerDocument)) + formData.append('max_contexts', String(options.maxContexts)) + formData.append('min_contexts', String(options.minContexts)) + formData.append('average_chunks_per_context', String(options.averageChunksPerContext)) + formData.append('chunk_size', String(options.chunkSize)) + formData.append('chunk_overlap', String(options.chunkOverlap)) + formData.append('num_evolutions', String(options.numEvolutions)) + formData.append('input_quality', String(options.inputQuality)) + formData.append('llm_timeout', String(options.llmTimeout)) + formData.append('async_mode', String(options.asyncMode)) + formData.append('max_concurrent', String(options.maxConcurrent)) + if (description.trim()) formData.append('description', description.trim()) + + const res = await evaluationApi.synthesizeDataset(formData) + // Server returns 202 immediately — close the modal and let polling update status onCreated && onCreated(res.data) handleClose() } catch (err) { - setError(err?.response?.data?.message || 'Failed to synthesize dataset.') + setError(err?.response?.data?.message || 'Failed to start synthesis.') } finally { setSubmitting(false) } } return ( - + Synthesize Dataset @@ -256,20 +428,15 @@ const SynthesizeModal = ({ open, onClose, onCreated }) => {
) : ( - - Sandbox - - + { required /> setDescription(e.target.value)} + /> + + Judge Model + + + setNumSamples(Number(e.target.value))} + inputProps={{ min: 1, max: 200 }} + value={options.targetGoldens} + onChange={(e) => updateOption('targetGoldens', Number(e.target.value))} + helperText='The synthesized dataset will contain exactly this many goldens.' /> + + + {showAdvanced && ( + + updateOption('maxGoldensPerDocument', Number(e.target.value))} + /> + updateOption('maxContexts', Number(e.target.value))} + /> + updateOption('minContexts', Number(e.target.value))} + /> + updateOption('averageChunksPerContext', Number(e.target.value))} + /> + updateOption('chunkSize', Number(e.target.value))} + helperText='Applied during uploaded document splitting before contexts are sent to studio-eval.' + /> + updateOption('chunkOverlap', Number(e.target.value))} + /> + updateOption('numEvolutions', Number(e.target.value))} + /> + updateOption('inputQuality', Number(e.target.value))} + /> + updateOption('llmTimeout', Number(e.target.value))} + helperText='0 disables the Ollama request timeout for this synthesis request.' + /> + updateOption('asyncMode', e.target.checked)} />} + label='Enable Async Generation' + /> + updateOption('maxConcurrent', Number(e.target.value))} + disabled={!options.asyncMode} + /> + + Omitted from the UI: output path and Ollama base URL are deployment-managed; context quality scoring is not available in this flow. + + + )} + {error && ( {error} @@ -305,7 +601,7 @@ const SynthesizeModal = ({ open, onClose, onCreated }) => { disabled={submitting || loadingOptions} startIcon={submitting ? : null} > - {submitting ? 'Synthesizing…' : 'Synthesize'} + {submitting ? 'Starting…' : 'Synthesize'} @@ -315,14 +611,16 @@ const SynthesizeModal = ({ open, onClose, onCreated }) => { SynthesizeModal.propTypes = { open: PropTypes.bool.isRequired, onClose: PropTypes.func.isRequired, - onCreated: PropTypes.func + onCreated: PropTypes.func, + existingDatasetNames: PropTypes.arrayOf(PropTypes.string) } // ── DatasetTab ──────────────────────────────────────────────────────────────── -const DatasetTab = ({ isVisible }) => { - const theme = useTheme() +const POLL_INTERVAL_MS = 5000 +const IN_PROGRESS_STATUSES = new Set(['pending', 'synthesizing']) +const DatasetTab = ({ isVisible }) => { const [datasets, setDatasets] = useState([]) const [isLoading, setIsLoading] = useState(true) const [error, setError] = useState(null) @@ -330,6 +628,13 @@ const DatasetTab = ({ isVisible }) => { const [orderBy, setOrderBy] = useState('created_at') const [newDatasetOpen, setNewDatasetOpen] = useState(false) const [synthesizeOpen, setSynthesizeOpen] = useState(false) + const [createMenuAnchor, setCreateMenuAnchor] = useState(null) + const [detailDatasetId, setDetailDatasetId] = useState(null) + const [detailOpen, setDetailOpen] = useState(false) + const [deleteConfirmId, setDeleteConfirmId] = useState(null) + const [deletingDataset, setDeletingDataset] = useState(false) + const [stoppingDataset, setStoppingDataset] = useState(null) + const pollTimerRef = useRef(null) const loadDatasets = useCallback(async () => { try { @@ -344,6 +649,68 @@ const DatasetTab = ({ isVisible }) => { } }, []) + // Poll individual in-progress datasets without a full list reload + const pollInProgress = useCallback(async (currentDatasets) => { + const inProgress = currentDatasets.filter((ds) => IN_PROGRESS_STATUSES.has(ds.status)) + if (inProgress.length === 0) return + + const updates = await Promise.allSettled( + inProgress.map((ds) => evaluationApi.getDataset(ds.id)) + ) + + setDatasets((prev) => { + const updated = [...prev] + updates.forEach((result, idx) => { + if (result.status === 'fulfilled') { + const fresh = result.value.data + const i = updated.findIndex((d) => d.id === fresh.id) + if (i !== -1) { + updated[i] = { + ...updated[i], + status: fresh.status, + error: fresh.error, + entry_count: fresh.entries ? fresh.entries.length : (updated[i].entry_count || 0), + completed_contexts: fresh.completed_contexts, + total_contexts: fresh.total_contexts, + completed_goldens: fresh.completed_goldens, + target_goldens: fresh.target_goldens, + updated_at: fresh.updated_at, + } + } + } + }) + return updated + }) + }, []) + + // Manage polling lifecycle + useEffect(() => { + const hasInProgress = datasets.some((ds) => IN_PROGRESS_STATUSES.has(ds.status)) + + if (hasInProgress && isVisible) { + if (!pollTimerRef.current) { + pollTimerRef.current = setInterval(() => { + setDatasets((current) => { + pollInProgress(current) + return current + }) + }, POLL_INTERVAL_MS) + } + } else { + if (pollTimerRef.current) { + clearInterval(pollTimerRef.current) + pollTimerRef.current = null + } + } + + return () => { + if (pollTimerRef.current) { + clearInterval(pollTimerRef.current) + pollTimerRef.current = null + } + } + }, [datasets, isVisible, pollInProgress]) + useEffect(() => { if (isVisible) loadDatasets() }, [isVisible, loadDatasets]) @@ -352,6 +719,64 @@ const DatasetTab = ({ isVisible }) => { setDatasets((prev) => [ds, ...prev]) } + const handleCreateMenuOpen = (event) => { + setCreateMenuAnchor(event.currentTarget) + } + + const handleCreateMenuClose = () => { + setCreateMenuAnchor(null) + } + + const handleOpenNewDataset = () => { + handleCreateMenuClose() + setNewDatasetOpen(true) + } + + const handleOpenSynthesize = () => { + handleCreateMenuClose() + setSynthesizeOpen(true) + } + + const handleOpenDataset = (id) => { + setDetailDatasetId(id) + setDetailOpen(true) + } + + const handleDatasetChanged = (patch) => { + setDatasets((prev) => prev.map((ds) => (ds.id === patch.id ? { ...ds, ...patch } : ds))) + } + + const handleDeleteDataset = async () => { + if (!deleteConfirmId) return + setDeletingDataset(true) + try { + const dsToDelete = datasets.find((ds) => ds.id === deleteConfirmId) + if (dsToDelete && IN_PROGRESS_STATUSES.has(dsToDelete.status)) { + await evaluationApi.stopDataset(deleteConfirmId).catch(() => {}) + } + await evaluationApi.deleteDataset(deleteConfirmId) + setDatasets((prev) => prev.filter((ds) => ds.id !== deleteConfirmId)) + setDeleteConfirmId(null) + } catch { + // ignore — user can retry + } finally { + setDeletingDataset(false) + } + } + + const handleStopDataset = async (e, id) => { + e.stopPropagation() + setStoppingDataset(id) + try { + await evaluationApi.stopDataset(id) + setDatasets((prev) => prev.map((ds) => (ds.id === id ? { ...ds, status: 'stopped' } : ds))) + } catch { + // ignore + } finally { + setStoppingDataset(null) + } + } + const handleRequestSort = (property) => { const isAsc = orderBy === property && order === 'asc' setOrder(isAsc ? 'desc' : 'asc') @@ -373,29 +798,52 @@ const DatasetTab = ({ isVisible }) => { const columns = [ { id: 'name', label: 'Name' }, { id: 'description', label: 'Description', sortable: false }, + { id: 'status', label: 'Status', sortable: false }, { id: 'entry_count', label: 'Entries' }, - { id: 'created_at', label: 'Created At' } + { id: 'created_at', label: 'Created At' }, + { id: 'actions', label: 'Actions', sortable: false } ] + const existingDatasetNames = datasets.map((ds) => ds.name).filter(Boolean) + + const isCreateMenuOpen = Boolean(createMenuAnchor) + return ( } - onClick={() => setNewDatasetOpen(true)} + endIcon={} + onClick={handleCreateMenuOpen} sx={{ borderRadius: 2, height: 40 }} + aria-controls={isCreateMenuOpen ? 'dataset-create-menu' : undefined} + aria-haspopup='menu' + aria-expanded={isCreateMenuOpen ? 'true' : undefined} > - Create New Dataset + Add Dataset - } - onClick={() => setSynthesizeOpen(true)} - sx={{ borderRadius: 2, height: 40 }} + - Synthesize Dataset - + + + + Import Dataset + + + + + + Synthesize Dataset + + + { {isLoading ? ( - + ) : sortedDatasets.length === 0 ? ( - + - No datasets yet. Create one manually or synthesize from a sandbox. + No datasets yet. Create one manually or synthesize from an uploaded source file. ) : ( sortedDatasets.map((ds) => ( - + ds.status === 'completed' && handleOpenDataset(ds.id)} + sx={{ cursor: ds.status === 'completed' ? 'pointer' : 'default' }} + > {ds.name} @@ -465,8 +918,79 @@ const DatasetTab = ({ isVisible }) => { {ds.description || '—'} + + + + {IN_PROGRESS_STATUSES.has(ds.status) && (() => { + const total = ds.target_goldens ?? 0 + const completed = ds.completed_goldens ?? 0 + const pct = total > 0 ? Math.round((completed / total) * 100) : null + return ( + + + {pct !== null && ( + + {completed}/{total} goldens ({pct}%) + + )} + + ) + })()} + + {ds.entry_count ?? '—'} {formatDate(ds.created_at)} + + + + + { e.stopPropagation(); handleOpenDataset(ds.id) }} + disabled={ds.status !== 'completed'} + > + + + + + {IN_PROGRESS_STATUSES.has(ds.status) && ( + + handleStopDataset(e, ds.id)} + disabled={stoppingDataset === ds.id} + > + {stoppingDataset === ds.id ? ( + + ) : ( + + )} + + + )} + + { e.stopPropagation(); setDeleteConfirmId(ds.id) }} + > + + + + + )) )} @@ -478,12 +1002,51 @@ const DatasetTab = ({ isVisible }) => { open={newDatasetOpen} onClose={() => setNewDatasetOpen(false)} onCreated={handleDatasetCreated} + existingDatasetNames={existingDatasetNames} /> setSynthesizeOpen(false)} onCreated={handleDatasetCreated} + existingDatasetNames={existingDatasetNames} + /> + setDetailOpen(false)} + onDatasetChanged={handleDatasetChanged} /> + + {/* Delete dataset confirmation */} + setDeleteConfirmId(null)} maxWidth='xs' fullWidth> + + Delete Dataset + + + + Are you sure you want to delete this dataset? This cannot be undone. + + {deleteConfirmId && IN_PROGRESS_STATUSES.has(datasets.find((ds) => ds.id === deleteConfirmId)?.status) && ( + + Synthesis is currently in progress and will be stopped before being deleted. + + )} + + + + + + ) } diff --git a/studio-frontend/packages/ui/src/views/evaluation/ExecutionTab.jsx b/studio-frontend/packages/ui/src/views/evaluation/ExecutionTab.jsx index e9289c0..dc6c396 100644 --- a/studio-frontend/packages/ui/src/views/evaluation/ExecutionTab.jsx +++ b/studio-frontend/packages/ui/src/views/evaluation/ExecutionTab.jsx @@ -12,6 +12,7 @@ import { DialogContent, DialogTitle, IconButton, + LinearProgress, Paper, Stack, Table, @@ -28,7 +29,7 @@ import { useTheme, styled } from '@mui/material/styles' import { tableCellClasses } from '@mui/material/TableCell' // icons -import { IconPlus, IconRefresh, IconEye, IconTrash } from '@tabler/icons-react' +import { IconPlus, IconRefresh, IconEye, IconTrash, IconPlayerStop } from '@tabler/icons-react' // API import evaluationApi from '@/api/evaluation' @@ -39,8 +40,9 @@ import RunDetailsModal from './RunDetailsModal' import { StyledButton } from '@/ui-component/button/StyledButton' -const POLL_INTERVAL_MS = 5000 +const POLL_INTERVAL_MS = 30000 const ACTIVE_STATUSES = ['pending', 'running'] +const CANCELLED_STATUSES = ['stopped', 'cancelled', 'canceled'] const StyledTableCell = styled(TableCell)(({ theme }) => ({ borderColor: theme.palette.grey[900] + 25, @@ -67,27 +69,32 @@ const StyledTableRow = styled(TableRow)(() => ({ } })) -const statusColor = (status) => { - switch (status) { - case 'completed': - return 'success' - case 'failed': - return 'error' - case 'running': - return 'primary' - case 'pending': - default: - return 'default' +const GRADE_CONFIG = [ + { grade: 'A', min: 0.90, color: '#2e7d32', label: 'Excellent', range: '≥ 0.90' }, + { grade: 'B', min: 0.75, color: '#558b2f', label: 'Good', range: '0.75 – 0.89' }, + { grade: 'C', min: 0.60, color: '#f57c00', label: 'Acceptable', range: '0.60 – 0.74' }, + { grade: 'D', min: 0.50, color: '#e64a19', label: 'Poor', range: '0.50 – 0.59' }, + { grade: 'E', min: 0.40, color: '#c62828', label: 'Very Poor', range: '0.40 – 0.49' }, + { grade: 'F', min: -Infinity, color: '#7f0000', label: 'Failed', range: '< 0.40 or no score' }, +] + +const getGrade = (avgScore) => { + if (avgScore === null || avgScore === undefined) return GRADE_CONFIG[GRADE_CONFIG.length - 1] + for (const cfg of GRADE_CONFIG) { + if (avgScore >= cfg.min) return cfg } + return GRADE_CONFIG[GRADE_CONFIG.length - 1] } const effectiveRunStatus = (run) => { - if (run.status === 'completed' && run.results?.some((r) => r.passed === false)) { - return 'failed' + if (CANCELLED_STATUSES.includes(run?.status)) { + return 'stopped' } - return run.status + return run?.status } +const isCancelledRun = (run) => CANCELLED_STATUSES.includes(run?.status) + const formatDate = (dateStr) => { if (!dateStr) return '—' try { @@ -130,6 +137,7 @@ const ExecutionTab = ({ isVisible }) => { const [deleteConfirmOpen, setDeleteConfirmOpen] = useState(false) const [runToDelete, setRunToDelete] = useState(null) const [deleting, setDeleting] = useState(false) + const [stoppingRuns, setStoppingRuns] = useState(new Set()) // Track interval ids per run_id const pollTimersRef = useRef({}) @@ -242,13 +250,36 @@ const ExecutionTab = ({ isVisible }) => { setDeleteConfirmOpen(true) } + const handleStopRun = async (e, run) => { + e.stopPropagation() + const runId = getRunId(run) + setStoppingRuns((prev) => new Set(prev).add(runId)) + try { + await evaluationApi.stopRun(runId) + setRuns((prev) => prev.map((r) => (getRunId(r) === runId ? { ...r, status: 'stopped' } : r))) + stopPolling(runId) + } catch (err) { + setError(err?.response?.data?.detail || 'Failed to stop run.') + } finally { + setStoppingRuns((prev) => { + const next = new Set(prev) + next.delete(runId) + return next + }) + } + } + const handleConfirmDelete = async () => { if (!runToDelete) return try { setDeleting(true) - await evaluationApi.deleteRun(getRunId(runToDelete)) - setRuns((prev) => prev.filter((r) => getRunId(r) !== getRunId(runToDelete))) - stopPolling(getRunId(runToDelete)) + const runId = getRunId(runToDelete) + if (ACTIVE_STATUSES.includes(runToDelete.status)) { + await evaluationApi.stopRun(runId).catch(() => {}) + } + await evaluationApi.deleteRun(runId) + setRuns((prev) => prev.filter((r) => getRunId(r) !== runId)) + stopPolling(runId) setDeleteConfirmOpen(false) setRunToDelete(null) } catch (err) { @@ -282,7 +313,7 @@ const ExecutionTab = ({ isVisible }) => { { id: 'dataset_id', label: 'Dataset', sortable: false }, { id: 'model_name', label: 'Judge Model', sortable: false }, { id: 'status', label: 'Status' }, - { id: 'score_summary', label: 'Score Summary', sortable: false }, + { id: 'eval_score', label: 'Eval Score', sortable: false }, { id: 'created_at', label: 'Created At' }, { id: 'actions', label: 'Actions', sortable: false } ] @@ -374,7 +405,7 @@ const ExecutionTab = ({ isVisible }) => { - {datasetMap[run.dataset_id] || run.dataset_id || '—'} + {run.dataset_name_snapshot ?? datasetMap[run.dataset_id] ?? (run.dataset_id ? String(run.dataset_id) : '—')} @@ -383,31 +414,93 @@ const ExecutionTab = ({ isVisible }) => { - - ) : undefined - } - /> + {(() => { + const effStatus = effectiveRunStatus(run) + const statusLabel = { + running: 'running', + pending: 'pending', + completed: 'completed', + failed: 'failed', + stopped: 'cancelled', + }[effStatus] ?? effStatus + const statusColor = { + running: 'primary', + pending: 'default', + completed: 'success', + failed: 'error', + stopped: 'default', + }[effStatus] ?? 'default' + return ( + : undefined} + /> + ) + })()} - + {(() => { + if (isCancelledRun(run)) { + return N/A + } + const effStatus = effectiveRunStatus(run) + if (ACTIVE_STATUSES.includes(effStatus)) { + const total = run.total_count ?? run.dataset_entries_snapshot?.length ?? 0 + const completed = run.completed_count ?? run.results?.length ?? 0 + const pct = total > 0 ? Math.round((completed / total) * 100) : null + return ( + + + {pct !== null && ( + + {completed}/{total} ({pct}%) + + )} + + ) + } const metricAvgs = computeMetricAverages(run) - if (metricAvgs.length === 0) return N/A + const validAvgs = metricAvgs.map((m) => m.avg).filter((a) => a !== null) + const overallAvg = validAvgs.length > 0 + ? validAvgs.reduce((acc, val) => acc + val, 0) / validAvgs.length + : null + const gradeCfg = getGrade(overallAvg) return ( - - {metricAvgs.map(({ metric, avg }) => ( - - - {metric}:{' '} - {avg !== null ? avg.toFixed(3) : 'N/A'} - + + + + Grade {gradeCfg.grade} - ))} + + {metricAvgs.length > 0 ? ( + + {metricAvgs.map(({ metric, avg }) => ( + + {metric}: {avg !== null ? avg.toFixed(3) : 'N/A'} + + ))} + + ) : ( + N/A + )} ) })()} @@ -428,6 +521,22 @@ const ExecutionTab = ({ isVisible }) => { + {ACTIVE_STATUSES.includes(run.status) && ( + + handleStopRun(e, run)} + disabled={stoppingRuns.has(getRunId(run))} + > + {stoppingRuns.has(getRunId(run)) ? ( + + ) : ( + + )} + + + )} { Are you sure you want to delete this evaluation run? This action cannot be undone. + {runToDelete && ACTIVE_STATUSES.includes(runToDelete.status) && ( + + This run is currently in progress and will be stopped before being deleted. + + )} {runToDelete && ( Run ID: {getRunId(runToDelete).substring(0, 12)}... diff --git a/studio-frontend/packages/ui/src/views/evaluation/RunDetailsModal.jsx b/studio-frontend/packages/ui/src/views/evaluation/RunDetailsModal.jsx index cd2eb3b..76e9bce 100644 --- a/studio-frontend/packages/ui/src/views/evaluation/RunDetailsModal.jsx +++ b/studio-frontend/packages/ui/src/views/evaluation/RunDetailsModal.jsx @@ -32,7 +32,7 @@ import { useTheme } from '@mui/material/styles' import { tableCellClasses } from '@mui/material/TableCell' // icons -import { IconX, IconAlertTriangle } from '@tabler/icons-react' +import { IconX, IconAlertTriangle, IconInfoCircle } from '@tabler/icons-react' // API import evaluationApi from '@/api/evaluation' @@ -56,15 +56,22 @@ const StyledTableRow = styled(TableRow)(() => ({ } })) -const statusColor = (status) => { - switch (status) { - case true: - return 'success' - case false: - return 'error' - default: - return 'default' +const GRADE_CONFIG = [ + { grade: 'A', min: 0.90, color: '#2e7d32', label: 'Excellent', range: '≥ 0.90' }, + { grade: 'B', min: 0.75, color: '#558b2f', label: 'Good', range: '0.75 – 0.89' }, + { grade: 'C', min: 0.60, color: '#f57c00', label: 'Acceptable', range: '0.60 – 0.74' }, + { grade: 'D', min: 0.50, color: '#e64a19', label: 'Poor', range: '0.50 – 0.59' }, + { grade: 'E', min: 0.40, color: '#c62828', label: 'Very Poor', range: '0.40 – 0.49' }, + { grade: 'F', min: -Infinity, color: '#7f0000', label: 'Failed', range: '< 0.40 or no score' }, +] +const CANCELLED_STATUSES = ['stopped', 'cancelled', 'canceled'] + +const getGrade = (avgScore) => { + if (avgScore === null || avgScore === undefined) return GRADE_CONFIG[GRADE_CONFIG.length - 1] + for (const cfg of GRADE_CONFIG) { + if (avgScore >= cfg.min) return cfg } + return GRADE_CONFIG[GRADE_CONFIG.length - 1] } const formatDate = (dateStr) => { @@ -146,10 +153,15 @@ const RunDetailsModal = ({ open, onClose, runId }) => { const theme = useTheme() const [tabValue, setTabValue] = useState(0) + const [entryTabValue, setEntryTabValue] = useState(0) const handleTabChange = (event, newValue) => { setTabValue(newValue) } + + const handleEntryTabChange = (event, newValue) => { + setEntryTabValue(newValue) + } const [run, setRun] = useState(null) const [loading, setLoading] = useState(false) @@ -164,8 +176,9 @@ const RunDetailsModal = ({ open, onClose, runId }) => { const [selectedEntry, setSelectedEntry] = useState(null) const [drawerOpen, setDrawerOpen] = useState(false) - const handleOpenDrawer = (result) => { - setSelectedEntry(result) + const handleOpenDrawer = (result, snapshotEntry) => { + setEntryTabValue(0) + setSelectedEntry({ ...result, _snapshotEntry: snapshotEntry }) setDrawerOpen(true) } @@ -175,12 +188,14 @@ const RunDetailsModal = ({ open, onClose, runId }) => { } const getEffectiveStatus = (runData) => { - if (runData.status === 'completed' && runData.results?.some((r) => r.passed === false)) { - return 'failed' + if (CANCELLED_STATUSES.includes(runData?.status)) { + return 'stopped' } - return runData.status + return runData?.status } + const isCancelledRun = (runData) => CANCELLED_STATUSES.includes(runData?.status) + const getOrderedMetricEntries = (runData, result) => { if (!result?.metric_scores) return [] @@ -212,6 +227,10 @@ const RunDetailsModal = ({ open, onClose, runId }) => { .filter(Boolean) } + const getExpectedOutput = (result, snapshotEntry) => { + return result?.expected_output ?? snapshotEntry?.expected_output ?? result?.entry?.expected_output ?? null + } + useEffect(() => { if (!open || !runId) return @@ -226,11 +245,9 @@ const RunDetailsModal = ({ open, onClose, runId }) => { setDataFilesError('') const res = await evaluationApi.getRun(runId) setRun(res.data) - try { - const dsRes = await evaluationApi.getDataset(res.data.dataset_id) - setDatasetName(dsRes.data.name) - } catch { - // non-critical + // Use snapshot first; fall back to live fetch only for old runs without snapshot + if (res.data.dataset_name_snapshot) { + setDatasetName(res.data.dataset_name_snapshot) } if ( applyConfigurationSnapshot(res.data.configuration_snapshot, { @@ -351,17 +368,32 @@ const RunDetailsModal = ({ open, onClose, runId }) => { Status - + {(() => { + const effStatus = getEffectiveStatus(run) + const statusLabel = { + running: 'running', + pending: 'pending', + completed: 'completed', + failed: 'failed', + stopped: 'cancelled', + }[effStatus] ?? effStatus + const statusColor = { + running: 'primary', + pending: 'default', + completed: 'success', + failed: 'error', + stopped: 'default', + }[effStatus] ?? 'default' + const isActive = run.status === 'running' || run.status === 'pending' + return ( + : undefined} + /> + ) + })()} @@ -404,7 +436,7 @@ const RunDetailsModal = ({ open, onClose, runId }) => { - Average Scores + Average Metric Scores {run.results && run.results.length > 0 ? ( @@ -454,12 +486,90 @@ const RunDetailsModal = ({ open, onClose, runId }) => { {/* Results Table */} {run.results && run.results.length > 0 && ( - - Evaluation Results ({run.results.length} entries) - + + + Evaluation Results ({run.results.length} entries) + + + {/* Grade Distribution Summary */} {(() => { + const gradeCounts = {} + GRADE_CONFIG.forEach((g) => { gradeCounts[g.grade] = 0 }) + run.results.forEach((result) => { + const entries = (Array.isArray(run.metrics) ? run.metrics : []) + .map((m) => [m, result.metric_scores?.[m]]) + .filter(([, s]) => Boolean(s)) + const validScores = entries + .map(([, s]) => s.score) + .filter((s) => s !== null && s !== undefined && typeof s === 'number') + const avg = validScores.length > 0 + ? validScores.reduce((a, b) => a + b, 0) / validScores.length + : null + gradeCounts[getGrade(avg).grade]++ + }) + return ( + + + Distribution: + + {GRADE_CONFIG.map((cfg) => { + const count = gradeCounts[cfg.grade] ?? 0 + return ( + + + Grade {cfg.grade} + + + {count} + + + ) + })} + + + Grading Scale (avg metric score) + + {GRADE_CONFIG.map((g) => ( + + {g.grade} + {' – '}{g.label}: {g.range} + + ))} + + } + > + + + + + + ) + })()} + {(() => { + const snapshotMap = {} + if (Array.isArray(run.dataset_entries_snapshot)) { + run.dataset_entries_snapshot.forEach((e) => { snapshotMap[e.id] = e }) + } const hasContext = run.results.some( - (r) => r.entry?.context && r.entry.context.length > 0 + (r) => { + const snapshotEntry = snapshotMap[r.entry_id] + return (r.entry?.context && r.entry.context.length > 0) + || (snapshotEntry?.context && snapshotEntry.context.length > 0) + } ) return ( @@ -467,95 +577,135 @@ const RunDetailsModal = ({ open, onClose, runId }) => { Entry - Input - {hasContext && Context} - Actual Output - Metrics - Status - Reason + Input + {hasContext && Context} + Actual Output + Expected Output + + + Eval Score + + + Reason - {run.results.map((result) => { - const orderedMetricEntries = getOrderedMetricEntries(run, result) - const failReasons = getResultReasons(run, result) - const failReasonText = failReasons.join('\n') - return ( - handleOpenDrawer(result)} - sx={{ cursor: 'pointer' }} - > - - - #{result.entry_id} - - - - - - {result.entry?.input || '—'} + {(() => { + return run.results.map((result) => { + const snapshotEntry = snapshotMap[result.entry_id] + const orderedMetricEntries = getOrderedMetricEntries(run, result) + const failReasons = getResultReasons(run, result) + const failReasonText = failReasons.join('\n') + const expectedOutput = getExpectedOutput(result, snapshotEntry) + return ( + handleOpenDrawer(result, snapshotEntry)} + sx={{ cursor: 'pointer' }} + > + + + #{result.entry_id} - ({ position: 'absolute', bottom: 0, left: 0, right: 0, height: '36px', background: `linear-gradient(to bottom, transparent, ${t.palette.background.paper})`, pointerEvents: 'none' })} /> - - - {hasContext && ( + - {result.entry?.context?.length > 0 ? result.entry.context.join('\n\n') : '—'} + {(snapshotEntry?.input ?? result.entry?.input) || '—'} ({ position: 'absolute', bottom: 0, left: 0, right: 0, height: '36px', background: `linear-gradient(to bottom, transparent, ${t.palette.background.paper})`, pointerEvents: 'none' })} /> - )} - - - - {result.actual_output || '—'} - - ({ position: 'absolute', bottom: 0, left: 0, right: 0, height: '36px', background: `linear-gradient(to bottom, transparent, ${t.palette.background.paper})`, pointerEvents: 'none' })} /> - - - - {orderedMetricEntries.length > 0 ? ( - - {orderedMetricEntries.map(([metricName, scores]) => ( - - - {metricName}: {formatMetricScore(scores.score)} - - - ))} - - ) : ( - '—' + {hasContext && ( + + + + {(() => { + const ctx = snapshotEntry?.context ?? result.entry?.context + return ctx?.length > 0 ? ctx.join('\n\n') : '—' + })()} + + ({ position: 'absolute', bottom: 0, left: 0, right: 0, height: '36px', background: `linear-gradient(to bottom, transparent, ${t.palette.background.paper})`, pointerEvents: 'none' })} /> + + )} - - - - - - {failReasons.length > 0 ? ( - - {failReasonText} - - ) : ( - '—' - )} - - - ) - })} + + + + {result.actual_output || '—'} + + ({ position: 'absolute', bottom: 0, left: 0, right: 0, height: '36px', background: `linear-gradient(to bottom, transparent, ${t.palette.background.paper})`, pointerEvents: 'none' })} /> + + + + + + {expectedOutput || '—'} + + ({ position: 'absolute', bottom: 0, left: 0, right: 0, height: '36px', background: `linear-gradient(to bottom, transparent, ${t.palette.background.paper})`, pointerEvents: 'none' })} /> + + + + {(() => { + const validScores = orderedMetricEntries + .map(([, scores]) => scores.score) + .filter((s) => s !== null && s !== undefined && typeof s === 'number') + const avgScore = validScores.length > 0 + ? validScores.reduce((a, b) => a + b, 0) / validScores.length + : null + const gradeCfg = getGrade(avgScore) + return ( + + + + Grade {gradeCfg.grade} + + + {orderedMetricEntries.length > 0 ? ( + + {orderedMetricEntries.map(([metricName, scores]) => ( + + {metricName}: {formatMetricScore(scores.score)} + + ))} + + ) : ( + + )} + + ) + })()} + + + {failReasons.length > 0 ? ( + + {failReasonText} + + ) : ( + '—' + )} + + + ) + }) + })()} @@ -691,78 +841,129 @@ const RunDetailsModal = ({ open, onClose, runId }) => { sx={{ zIndex: (theme) => theme.zIndex.modal + 10 }} PaperProps={{ sx: { width: { xs: '100%', sm: 600, md: 800 }, p: 3, bgcolor: theme.palette.mode === 'dark' ? 'grey.900' : 'background.paper' } }} > - {selectedEntry && ( - - - Entry #{selectedEntry.entry_id} Details - - - - - - - - - Input - - - {selectedEntry.entry?.input || '—'} + {selectedEntry && ( + + + Entry #{selectedEntry.entry_id} Details + + + + + + {(() => { + const entries = getOrderedMetricEntries(run, selectedEntry) + const validScores = entries + .map(([, s]) => s.score) + .filter((s) => s !== null && s !== undefined && typeof s === 'number') + const avgScore = validScores.length > 0 + ? validScores.reduce((a, b) => a + b, 0) / validScores.length + : null + const gradeCfg = getGrade(avgScore) + return ( + + + + Eval Score: - - - - {selectedEntry.entry?.context && selectedEntry.entry.context.length > 0 && ( - - Context - - {selectedEntry.entry.context.map((ctx, idx) => ( - - {ctx} + + + Grade {gradeCfg.grade} + + + + {entries.length > 0 ? ( + + {entries.map(([metricName, scoreDetails]) => ( + + {metricName}: {formatMetricScore(scoreDetails.score)} ))} - - - )} + + ) : ( + N/A + )} +
+ ) + })()} + + + + + + + 0)} /> + + + + + - - Actual Output + + + + + {(selectedEntry._snapshotEntry?.input ?? selectedEntry.entry?.input) || '—'} + + + + + + {(() => { + const ctxList = selectedEntry._snapshotEntry?.context ?? selectedEntry.entry?.context + return ctxList && ctxList.length > 0 && ( - - {selectedEntry.actual_output || '—'} - - - - - {selectedEntry.expected_output && ( - - Expected Output - - - {selectedEntry.expected_output} + {ctxList.map((ctx, idx) => ( + + {ctx} - - - )} - - - Execution Status - - + ))} + + ) + })()} + + + + + + {selectedEntry.actual_output || '—'} + + + - {getResultReasons(run, selectedEntry).length > 0 && ( - - Reasons - - - {getResultReasons(run, selectedEntry).join('\n')} - - - - )} - - + + {getExpectedOutput(selectedEntry, selectedEntry._snapshotEntry) && ( + + + {getExpectedOutput(selectedEntry, selectedEntry._snapshotEntry)} + + + )} + + + + {getResultReasons(run, selectedEntry).length > 0 && ( + + + {getResultReasons(run, selectedEntry).join('\n')} + + + )} +
- )} + + )} ) diff --git a/studio-frontend/packages/ui/src/views/finetuning/FileUploadArea.jsx b/studio-frontend/packages/ui/src/views/finetuning/FileUploadArea.jsx index e3c1d54..665d594 100644 --- a/studio-frontend/packages/ui/src/views/finetuning/FileUploadArea.jsx +++ b/studio-frontend/packages/ui/src/views/finetuning/FileUploadArea.jsx @@ -28,7 +28,10 @@ const FileUploadArea = ({ onFileUpload, acceptedTypes = ['.json', '.jsonl', '.csv'], maxSizeMB = 10, - error = null + error = null, + title = 'Drop your training dataset file here or click to browse', + subtitle, + buttonLabel = 'Choose File' }) => { const theme = useTheme() const fileInputRef = useRef(null) @@ -168,6 +171,8 @@ const FileUploadArea = ({ return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i] } + const helperText = subtitle || `Supported formats: ${acceptedTypes.join(', ')} (Max filesize: ${maxSizeMB}MB)` + return ( - Drop your training dataset file here or click to browse + {title} - Supported formats: {acceptedTypes.join(', ')} (Max filesize: {maxSizeMB}MB) + {helperText} @@ -311,7 +316,10 @@ FileUploadArea.propTypes = { onFileUpload: PropTypes.func.isRequired, acceptedTypes: PropTypes.arrayOf(PropTypes.string), maxSizeMB: PropTypes.number, - error: PropTypes.string + error: PropTypes.string, + title: PropTypes.string, + subtitle: PropTypes.string, + buttonLabel: PropTypes.string } export default FileUploadArea \ No newline at end of file diff --git a/studio-frontend/packages/ui/src/views/finetuning/FinetuningJobModal.jsx b/studio-frontend/packages/ui/src/views/finetuning/FinetuningJobModal.jsx index 03653df..dfd6fc8 100644 --- a/studio-frontend/packages/ui/src/views/finetuning/FinetuningJobModal.jsx +++ b/studio-frontend/packages/ui/src/views/finetuning/FinetuningJobModal.jsx @@ -28,7 +28,7 @@ import { useTheme } from '@mui/material/styles' import { IconX } from '@tabler/icons-react' // components -import FileUploadArea from './FileUploadArea' +import FileUploadArea from '@/ui-component/file/FileUploadArea' // API import finetuningApi from '@/api/finetuning' diff --git a/studio-frontend/pnpm-lock.yaml b/studio-frontend/pnpm-lock.yaml index 03a539a..3c72e54 100644 --- a/studio-frontend/pnpm-lock.yaml +++ b/studio-frontend/pnpm-lock.yaml @@ -185,6 +185,9 @@ importers: lodash: specifier: ^4.17.21 version: 4.17.21 + mammoth: + specifier: ^1.11.0 + version: 1.12.0 moment: specifier: ^2.29.3 version: 2.30.1 @@ -197,6 +200,9 @@ importers: mysql2: specifier: ^3.11.3 version: 3.15.2 + pdf-parse: + specifier: ^1.1.1 + version: 1.1.4 pg: specifier: ^8.11.1 version: 8.16.3 @@ -3698,6 +3704,10 @@ packages: '@webassemblyjs/wast-printer@1.14.1': resolution: {integrity: sha512-kPSSXE6De1XOR820C90RIo2ogvZG+c3KiHzqUoO/F34Y2shGzesfqv7o57xrxovZJH/MetF5UjroJ/R/3isoiw==} + '@xmldom/xmldom@0.8.13': + resolution: {integrity: sha512-KRYzxepc14G/CEpEGc3Yn+JKaAeT63smlDr+vjB8jRfgTBBI9wRj/nkQEO+ucV8p8I9bfKLWp37uHgFrbntPvw==} + engines: {node: '>=10.0.0'} + '@xmldom/xmldom@0.9.8': resolution: {integrity: sha512-p96FSY54r+WJ50FIOsCOjyj/wavs8921hG5+kVMmZgKcvIKxMXHTrjNJvRgWa/zuX3B6t2lijLNFaOyuxUH+2A==} engines: {node: '>=14.6'} @@ -4181,6 +4191,9 @@ packages: blob-util@2.0.2: resolution: {integrity: sha512-T7JQa+zsXXEa6/8ZhHcQEW1UFfVM49Ts65uBkFL6fz2QmrElqmbajIDJvuA0tEhRe5eIjpV9ZF+0RfZR9voJFQ==} + bluebird@3.4.7: + resolution: {integrity: sha512-iD3898SR7sWVRHbiQv+sHUtHnMvC1o3nW5rAcqnq3uOn07DSAppZYUkIGslDz6gXC7HfunPe7YVBgoEJASPcHA==} + bluebird@3.7.2: resolution: {integrity: sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==} @@ -5134,6 +5147,9 @@ packages: resolution: {integrity: sha512-uIFDxqpRZGZ6ThOk84hEfqWoHx2devRFvpTZcTHur85vImfaxUbTW9Ryh4CpCuDnToOP1CEtXKIgytHBPVff5A==} engines: {node: '>=0.3.1'} + dingbat-to-unicode@1.0.1: + resolution: {integrity: sha512-98l0sW87ZT58pU4i61wa2OHwxbiYSbuxsCBozaVnYX2iCnr3bLM3fIes1/ej7h1YdOKuKt/MLs706TVnALA65w==} + dir-glob@3.0.1: resolution: {integrity: sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==} engines: {node: '>=8'} @@ -5221,6 +5237,9 @@ packages: resolution: {integrity: sha512-uBq4egWHTcTt33a72vpSG0z3HnPuIl6NqYcTrKEg2azoEyl2hpW0zqlxysq2pK9HlDIHyHyakeYaYnSAwd8bow==} engines: {node: '>=12'} + duck@0.1.12: + resolution: {integrity: sha512-wkctla1O6VfP89gQ+J/yDesM0S7B7XLXjKGzXxMDVFg7uEn706niAtyYovKbyq1oT9YwDcly721/iUWoc8MVRg==} + dunder-proto@1.0.1: resolution: {integrity: sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==} engines: {node: '>= 0.4'} @@ -6322,6 +6341,9 @@ packages: resolution: {integrity: sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==} engines: {node: '>= 4'} + immediate@3.0.6: + resolution: {integrity: sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ==} + immer@11.1.4: resolution: {integrity: sha512-XREFCPo6ksxVzP4E0ekD5aMdf8WMwmdNaz6vuvxgI40UaEiu6q3p8X52aU6GdyvLY3XXX/8R7JOTXStz/nBbRw==} @@ -6923,6 +6945,9 @@ packages: resolution: {integrity: sha512-ZZow9HBI5O6EPgSJLUb8n2NKgmVWTwCvHGwFuJlMjvLFqlGG6pjirPhtdsseaLZjSibD8eegzmYpUZwoIlj2cQ==} engines: {node: '>=4.0'} + jszip@3.10.1: + resolution: {integrity: sha512-xXDvecyTpGLrqFrvkrUSoxxfJI5AH7U8zxxtVclpsUtMCq4JQ290LY8AW5c7Ggnr/Y/oK+bQMbqK2qmtk3pN4g==} + katex@0.16.25: resolution: {integrity: sha512-woHRUZ/iF23GBP1dkDQMh1QBad9dmr8/PAwNA54VrSOVYgI12MAcE14TqnDdQOdzyEonGzMepYnqBMYdsoAr8Q==} hasBin: true @@ -6986,6 +7011,9 @@ packages: resolution: {integrity: sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==} engines: {node: '>= 0.8.0'} + lie@3.3.0: + resolution: {integrity: sha512-UaiMJzeWRlEujzAuw5LokY1L5ecNQYZKfmyZ9L7wDHb/p5etKaxXhohBcrw0EYby+G/NA52vRSN4N39dxHAIwQ==} + lilconfig@2.1.0: resolution: {integrity: sha512-utWOt/GHzuUxnLKxB6dk81RoOeoNeHgbrXiuGk4yyF5qlRz+iIVWu56E2fqGHFrXz0QNUhLB/8nKqvRH66JKGQ==} engines: {node: '>=10'} @@ -7109,6 +7137,9 @@ packages: resolution: {integrity: sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==} hasBin: true + lop@0.4.2: + resolution: {integrity: sha512-RefILVDQ4DKoRZsJ4Pj22TxE3omDO47yFpkIBoDKzkqPRISs5U1cnAdg/5583YPkWPaLIYHOKRMQSvjFsO26cw==} + lower-case@2.0.2: resolution: {integrity: sha512-7fm3l3NAF9WfN6W3JOmf5drwpVqX78JtoGJ3A6W0a6ZnldM41w2fV5D490psKFTpMds8TJse/eHLFFsNHHjHgg==} @@ -7168,6 +7199,11 @@ packages: makeerror@1.0.12: resolution: {integrity: sha512-JmqCvUhmt43madlpFzG4BQzG2Z3m6tvQDNKdClZnO3VbIudJYmxsT0FNJMeiB2+JTSlTQTSbU8QdesVmwJcmLg==} + mammoth@1.12.0: + resolution: {integrity: sha512-cwnK1RIcRdDMi2HRx2EXGYlxqIEh0Oo3bLhorgnsVJi2UkbX1+jKxuBNR9PC5+JaX7EkmJxFPmo6mjLpqShI2w==} + engines: {node: '>=12.0.0'} + hasBin: true + map-stream@0.1.0: resolution: {integrity: sha512-CkYQrPYZfWnu/DAmVCpTSX/xHpKZ80eKh2lAkyA6AJTef6bW+6JpbQZN5rofum7da+SyN1bi5ctTm+lTfcCW3g==} @@ -7599,6 +7635,9 @@ packages: engines: {node: '>=10.5.0'} deprecated: Use your platform's native DOMException instead + node-ensure@0.0.0: + resolution: {integrity: sha512-DRI60hzo2oKN1ma0ckc6nQWlHU69RH6xN0sjQTjMpChPfTYvKZdcQFfdYK2RWbJcKyUizSIy/l8OTGxMAM1QDw==} + node-fetch@2.7.0: resolution: {integrity: sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==} engines: {node: 4.x || >=6.0.0} @@ -7767,6 +7806,9 @@ packages: resolution: {integrity: sha512-7x81NCL719oNbsq/3mh+hVrAWmFuEYUqrq/Iw3kUzH8ReypT9QQ0BLoJS7/G9k6N81XjW4qHWtjWwe/9eLy1EQ==} engines: {node: '>=12'} + option@0.2.4: + resolution: {integrity: sha512-pkEqbDyl8ou5cpq+VsnQbe/WlEy5qS7xPzMS1U55OCG9KPvwFD46zDbxQIj3egJSFc3D+XhYOPUzz49zQAVy7A==} + optionator@0.8.3: resolution: {integrity: sha512-+IW9pACdk3XWmmTXG8m3upGUJst5XRGzxMRjXzAuJ1XnIFNvfhjjIuYkDvysnPQ7qzqVzLt78BCruntqRhWQbA==} engines: {node: '>= 0.8.0'} @@ -7817,6 +7859,9 @@ packages: package-json-from-dist@1.0.1: resolution: {integrity: sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==} + pako@1.0.11: + resolution: {integrity: sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==} + param-case@3.0.4: resolution: {integrity: sha512-RXlj7zCYokReqWpOPH9oYivUzLYZ5vAPIfEmCTNViosC78F8F0H9y7T7gG2M39ymgutxF5gcFEsyZQSph9Bp3A==} @@ -7855,6 +7900,10 @@ packages: resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==} engines: {node: '>=8'} + path-is-absolute@1.0.1: + resolution: {integrity: sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==} + engines: {node: '>=0.10.0'} + path-key@1.0.0: resolution: {integrity: sha512-T3hWy7tyXlk3QvPFnT+o2tmXRzU4GkitkUWLp/WZ0S/FXd7XMx176tRurgTvHTNMJOQzTcesHNpBqetH86mQ9g==} engines: {node: '>=0.10.0'} @@ -7884,6 +7933,10 @@ packages: pause-stream@0.0.11: resolution: {integrity: sha512-e3FBlXLmN/D1S+zHzanP4E/4Z60oFAa3O051qt1pxa7DEJWKAyil6upYVXCWadEnuoqa4Pkc9oUx9zsxYeRv8A==} + pdf-parse@1.1.4: + resolution: {integrity: sha512-XRIRcLgk6ZnUbsHsYXExMw+krrPE81hJ6FQPLdBNhhBefqIQKXu/WeTgNBGSwPrfU0v+UCEwn7AoAUOsVKHFvQ==} + engines: {node: '>=6.8.1'} + pend@1.2.0: resolution: {integrity: sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg==} @@ -10107,6 +10160,9 @@ packages: underscore@1.12.1: resolution: {integrity: sha512-hEQt0+ZLDVUMhebKxL4x1BTtDY7bavVofhZ9KZ4aI26X9SRaE+Y3m83XUL1UP2jn8ynjndwCCpEHdUG+9pP1Tw==} + underscore@1.13.8: + resolution: {integrity: sha512-DXtD3ZtEQzc7M8m4cXotyHR+FAS18C64asBYY5vqZexfYryNNnDc02W4hKg3rdQuqOYas1jkseX0+nZXjTXnvQ==} + undici-types@7.14.0: resolution: {integrity: sha512-QQiYxHuyZ9gQUIrmPo3IA+hUl4KYk8uSA7cHrcKd/l3p1OTpZcM0Tbp9x7FAtXdAYhlasd60ncPpgu6ihG6TOA==} @@ -10669,6 +10725,10 @@ packages: resolution: {integrity: sha512-ICP2e+jsHvAj2E2lIHxa5tjXRlKDJo4IdvPvCXbXQGdzSfmSpNVyIKMvoZHjDY9DP0zV17iI85o90vRFXNccRw==} engines: {node: '>=12'} + xmlbuilder@10.1.1: + resolution: {integrity: sha512-OyzrcFLL/nb6fMGHbiRDuPup9ljBycsdCypwuyg5AAHvyWzGfChJpCXMG88AGTIMFhGZ9RccFN1e6lhg3hkwKg==} + engines: {node: '>=4.0'} + xmlchars@2.2.0: resolution: {integrity: sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==} @@ -14967,6 +15027,8 @@ snapshots: '@webassemblyjs/ast': 1.14.1 '@xtuc/long': 4.2.2 + '@xmldom/xmldom@0.8.13': {} + '@xmldom/xmldom@0.9.8': {} '@xtuc/ieee754@1.2.0': {} @@ -15507,6 +15569,8 @@ snapshots: blob-util@2.0.2: {} + bluebird@3.4.7: {} + bluebird@3.7.2: {} body-parser@2.0.2: @@ -16500,6 +16564,8 @@ snapshots: diff@5.2.0: {} + dingbat-to-unicode@1.0.1: {} + dir-glob@3.0.1: dependencies: path-type: 4.0.0 @@ -16598,6 +16664,10 @@ snapshots: dotenv@16.6.1: {} + duck@0.1.12: + dependencies: + underscore: 1.13.8 + dunder-proto@1.0.1: dependencies: call-bind-apply-helpers: 1.0.2 @@ -18077,6 +18147,8 @@ snapshots: ignore@5.3.2: {} + immediate@3.0.6: {} + immer@11.1.4: {} immer@9.0.21: {} @@ -18981,6 +19053,13 @@ snapshots: object.assign: 4.1.7 object.values: 1.2.1 + jszip@3.10.1: + dependencies: + lie: 3.3.0 + pako: 1.0.11 + readable-stream: 2.3.8 + setimmediate: 1.0.5 + katex@0.16.25: dependencies: commander: 8.3.0 @@ -19035,6 +19114,10 @@ snapshots: prelude-ls: 1.2.1 type-check: 0.4.0 + lie@3.3.0: + dependencies: + immediate: 3.0.6 + lilconfig@2.1.0: {} lilconfig@3.1.3: {} @@ -19169,6 +19252,12 @@ snapshots: dependencies: js-tokens: 4.0.0 + lop@0.4.2: + dependencies: + duck: 0.1.12 + option: 0.2.4 + underscore: 1.13.8 + lower-case@2.0.2: dependencies: tslib: 2.8.1 @@ -19251,6 +19340,19 @@ snapshots: dependencies: tmpl: 1.0.5 + mammoth@1.12.0: + dependencies: + '@xmldom/xmldom': 0.8.13 + argparse: 1.0.10 + base64-js: 1.5.1 + bluebird: 3.4.7 + dingbat-to-unicode: 1.0.1 + jszip: 3.10.1 + lop: 0.4.2 + path-is-absolute: 1.0.1 + underscore: 1.13.8 + xmlbuilder: 10.1.1 + map-stream@0.1.0: {} markdown-table@3.0.4: {} @@ -19843,6 +19945,8 @@ snapshots: node-domexception@1.0.0: {} + node-ensure@0.0.0: {} + node-fetch@2.7.0(encoding@0.1.13): dependencies: whatwg-url: 5.0.0 @@ -20039,6 +20143,8 @@ snapshots: is-docker: 2.2.1 is-wsl: 2.2.0 + option@0.2.4: {} + optionator@0.8.3: dependencies: deep-is: 0.1.4 @@ -20098,6 +20204,8 @@ snapshots: package-json-from-dist@1.0.1: {} + pako@1.0.11: {} + param-case@3.0.4: dependencies: dot-case: 3.0.4 @@ -20142,6 +20250,8 @@ snapshots: path-exists@4.0.0: {} + path-is-absolute@1.0.1: {} + path-key@1.0.0: {} path-key@3.1.1: {} @@ -20163,6 +20273,10 @@ snapshots: dependencies: through: 2.3.8 + pdf-parse@1.1.4: + dependencies: + node-ensure: 0.0.0 + pend@1.2.0: {} perfect-scrollbar@1.5.6: {} @@ -22725,6 +22839,8 @@ snapshots: underscore@1.12.1: {} + underscore@1.13.8: {} + undici-types@7.14.0: {} unicode-canonical-property-names-ecmascript@2.0.1: {} @@ -23437,6 +23553,8 @@ snapshots: xml-name-validator@4.0.0: {} + xmlbuilder@10.1.1: {} + xmlchars@2.2.0: {} xmlhttprequest-ssl@2.1.2: {} From 31acb1e4d20cb8a76f2c652860c3363ad803edf6 Mon Sep 17 00:00:00 2001 From: wwanarif Date: Thu, 23 Apr 2026 03:34:41 +0000 Subject: [PATCH 5/9] enable pulling judge model from UI Signed-off-by: wwanarif --- studio-eval/app/routers/eval_router.py | 8 +- studio-eval/app/services/ollama_service.py | 129 +++- .../server/src/routes/evaluation/index.ts | 5 + .../packages/ui/src/api/evaluation.js | 3 + .../src/views/evaluation/CreateRunModal.jsx | 29 +- .../ui/src/views/evaluation/DatasetTab.jsx | 25 +- .../ui/src/views/evaluation/ExecutionTab.jsx | 2 +- .../ui/src/views/evaluation/ModelSelect.jsx | 589 ++++++++++++++++++ 8 files changed, 750 insertions(+), 40 deletions(-) create mode 100644 studio-frontend/packages/ui/src/views/evaluation/ModelSelect.jsx diff --git a/studio-eval/app/routers/eval_router.py b/studio-eval/app/routers/eval_router.py index bfa049f..90aedde 100644 --- a/studio-eval/app/routers/eval_router.py +++ b/studio-eval/app/routers/eval_router.py @@ -24,7 +24,7 @@ SynthesizeFromDocRequest, ) from app.services import dataset_service, eval_service -from app.services.ollama_service import ensure_model, list_models +from app.services.ollama_service import ensure_model, get_pull_status, list_models logger = logging.getLogger(__name__) @@ -280,6 +280,12 @@ async def list_ollama_models(): raise HTTPException(status_code=502, detail=f"Failed to reach Ollama: {exc}") +@router.get("/models/pull/status") +async def get_model_pull_status(): + """Return current pull status for all models that have been requested.""" + return get_pull_status() + + @router.post("/models/pull", status_code=202) async def pull_model(payload: ModelPullRequest, background_tasks: BackgroundTasks): """Trigger an async pull of an Ollama model.""" diff --git a/studio-eval/app/services/ollama_service.py b/studio-eval/app/services/ollama_service.py index bf76f0b..f5a6325 100644 --- a/studio-eval/app/services/ollama_service.py +++ b/studio-eval/app/services/ollama_service.py @@ -1,6 +1,7 @@ import json import logging import os +from datetime import datetime, timezone from typing import Any, List import httpx @@ -93,7 +94,7 @@ def _parse_structured_response(schema, raw_response: str) -> Any: async def list_models() -> list: """Return the list of locally available Ollama models (GET /api/tags).""" - async with httpx.AsyncClient(timeout=30.0) as client: + async with httpx.AsyncClient(timeout=30.0, trust_env=False) as client: resp = await client.get(f"{OLLAMA_BASE_URL}/api/tags") resp.raise_for_status() return resp.json().get("models", []) @@ -103,26 +104,121 @@ async def is_model_available(model_name: str) -> bool: """True if *model_name* is already present in Ollama.""" try: models = await list_models() - target_base = model_name.split(":")[0] + normalized_target = model_name.strip() + target_with_default_tag = ( + normalized_target + if ":" in normalized_target + else f"{normalized_target}:latest" + ) for m in models: - name = m.get("name", "") - if name == model_name or name.split(":")[0] == target_base: + name = (m.get("name", "") or "").strip() + if name == normalized_target or name == target_with_default_tag: return True return False except Exception: return False +# --------------------------------------------------------------------------- +# Pull status tracker (in-memory; reset on pod restart) +# --------------------------------------------------------------------------- + +# model_name → pull-status snapshot used by the UI poller +_pull_status: dict[str, dict] = {} + + +def _utc_timestamp() -> str: + return datetime.now(timezone.utc).isoformat() + + +def _set_pull_status(model_name: str, **fields) -> None: + current = _pull_status.get(model_name, {}) + _pull_status[model_name] = { + **current, + **fields, + "updated_at": _utc_timestamp(), + } + + +def get_pull_status() -> dict[str, dict]: + """Return a shallow copy of the current pull status map.""" + return dict(_pull_status) + + async def pull_model(model_name: str) -> None: """Pull *model_name* from the Ollama registry; blocks until complete.""" logger.info("Pulling Ollama model: %s", model_name) - async with httpx.AsyncClient(timeout=OLLAMA_PULL_TIMEOUT) as client: - resp = await client.post( - f"{OLLAMA_BASE_URL}/api/pull", - json={"name": model_name, "stream": False}, + _set_pull_status( + model_name, + status="pulling", + detail="Preparing download", + error=None, + completed=None, + total=None, + digest=None, + ) + + try: + async with httpx.AsyncClient(timeout=OLLAMA_PULL_TIMEOUT, trust_env=False) as client: + async with client.stream( + "POST", + f"{OLLAMA_BASE_URL}/api/pull", + json={"name": model_name, "stream": True}, + ) as resp: + resp.raise_for_status() + + async for line in resp.aiter_lines(): + if not line: + continue + + event = json.loads(line) + detail = event.get("status") or "Pulling model" + completed = event.get("completed") + total = event.get("total") + digest = event.get("digest") + + _set_pull_status( + model_name, + status="pulling", + detail=detail, + error=None, + completed=completed, + total=total, + digest=digest, + ) + + if event.get("error"): + raise RuntimeError(str(event["error"])) + + if event.get("status") == "success": + _set_pull_status( + model_name, + status="ready", + detail="Pull complete", + error=None, + completed=completed, + total=total, + digest=digest, + ) + + logger.info("Model pulled successfully: %s", model_name) + current = _pull_status.get(model_name, {}) + if current.get("status") != "ready": + _set_pull_status( + model_name, + status="ready", + detail=current.get("detail") or "Pull complete", + error=None, + ) + except Exception as exc: + logger.error("Failed to pull model '%s': %s", model_name, exc) + _set_pull_status( + model_name, + status="error", + detail="Pull failed", + error=str(exc), ) - resp.raise_for_status() - logger.info("Model pulled successfully: %s", model_name) + raise async def ensure_model(model_name: str) -> None: @@ -132,6 +228,15 @@ async def ensure_model(model_name: str) -> None: await pull_model(model_name) else: logger.debug("Model '%s' already available", model_name) + _set_pull_status( + model_name, + status="ready", + detail="Model already available", + error=None, + completed=None, + total=None, + digest=None, + ) # --------------------------------------------------------------------------- @@ -186,7 +291,7 @@ def generate(self, prompt: str, schema=None) -> Any: if schema is not None: payload["format"] = _schema_to_ollama_format(schema) - with httpx.Client(timeout=self._timeout) as client: + with httpx.Client(timeout=self._timeout, trust_env=False) as client: resp = client.post(f"{self._base_url}/api/generate", json=payload) resp.raise_for_status() response_text = resp.json()["response"] @@ -207,7 +312,7 @@ async def a_generate(self, prompt: str, schema=None) -> Any: if schema is not None: payload["format"] = _schema_to_ollama_format(schema) - async with httpx.AsyncClient(timeout=self._timeout) as client: + async with httpx.AsyncClient(timeout=self._timeout, trust_env=False) as client: resp = await client.post( f"{self._base_url}/api/generate", json=payload ) diff --git a/studio-frontend/packages/server/src/routes/evaluation/index.ts b/studio-frontend/packages/server/src/routes/evaluation/index.ts index fd8086f..2983f4c 100644 --- a/studio-frontend/packages/server/src/routes/evaluation/index.ts +++ b/studio-frontend/packages/server/src/routes/evaluation/index.ts @@ -72,6 +72,11 @@ router.get('/models', (req: Request, res: Response, next: NextFunction) => proxy('get', 'studio-backend/evaluation/models', req, res, next) ) +// GET /api/v1/evaluation/models/pull/status → studio-backend/evaluation/models/pull/status +router.get('/models/pull/status', (req: Request, res: Response, next: NextFunction) => + proxy('get', 'studio-backend/evaluation/models/pull/status', req, res, next) +) + // POST /api/v1/evaluation/models/pull → studio-backend/evaluation/models/pull router.post('/models/pull', (req: Request, res: Response, next: NextFunction) => proxy('post', 'studio-backend/evaluation/models/pull', req, res, next) diff --git a/studio-frontend/packages/ui/src/api/evaluation.js b/studio-frontend/packages/ui/src/api/evaluation.js index 4d33913..fd5ac8e 100644 --- a/studio-frontend/packages/ui/src/api/evaluation.js +++ b/studio-frontend/packages/ui/src/api/evaluation.js @@ -37,6 +37,9 @@ const evaluationApi = { // Pull/register a new judge model pullModel: (payload) => client.post('/evaluation/models/pull', payload), + // Get pull progress for all models currently being pulled + getPullStatus: () => client.get('/evaluation/models/pull/status'), + // Check files in sandbox Data Management (dataprep) getSandboxDataManagementFiles: (payload) => client.post('/evaluation/sandbox-files', payload), diff --git a/studio-frontend/packages/ui/src/views/evaluation/CreateRunModal.jsx b/studio-frontend/packages/ui/src/views/evaluation/CreateRunModal.jsx index 6fea855..798fc16 100644 --- a/studio-frontend/packages/ui/src/views/evaluation/CreateRunModal.jsx +++ b/studio-frontend/packages/ui/src/views/evaluation/CreateRunModal.jsx @@ -34,6 +34,9 @@ import { IconX, IconChevronDown, IconChevronUp } from '@tabler/icons-react' import evaluationApi from '@/api/evaluation' import chatflowsApi from '@/api/chatflows' +// Components +import ModelSelect from './ModelSelect' + const METRICS = [ 'AnswerRelevancy', 'Faithfulness', @@ -158,6 +161,10 @@ const CreateRunModal = ({ open, onClose, onRunCreated }) => { const [temperature, setTemperature] = useState(0.4) const [maxTokens, setMaxTokens] = useState(100) + const fetchModels = () => { + evaluationApi.getModels().then((res) => setModels(res.data || [])).catch(() => {}) + } + useEffect(() => { if (!open) return setLoadingOptions(true) @@ -291,20 +298,14 @@ const CreateRunModal = ({ open, onClose, onRunCreated }) => { {/* Judge Model */} - - Judge Model - - + {/* Metrics */} diff --git a/studio-frontend/packages/ui/src/views/evaluation/DatasetTab.jsx b/studio-frontend/packages/ui/src/views/evaluation/DatasetTab.jsx index 0d1bfd6..4621f31 100644 --- a/studio-frontend/packages/ui/src/views/evaluation/DatasetTab.jsx +++ b/studio-frontend/packages/ui/src/views/evaluation/DatasetTab.jsx @@ -14,12 +14,10 @@ import { FormControlLabel, FormControl, IconButton, - InputLabel, LinearProgress, Menu, MenuItem, Paper, - Select, Stack, Switch, Table, @@ -43,6 +41,7 @@ import { IconChevronDown, IconEye, IconPlus, IconRefresh, IconTrash, IconWand, I import evaluationApi from '@/api/evaluation' import DatasetDetailDialog from './DatasetDetailDialog' import FileUploadArea from '@/ui-component/file/FileUploadArea' +import ModelSelect from './ModelSelect' import { StyledButton } from '@/ui-component/button/StyledButton' @@ -322,6 +321,10 @@ const SynthesizeModal = ({ open, onClose, onCreated, existingDatasetNames }) => const [submitting, setSubmitting] = useState(false) const [error, setError] = useState('') + const fetchModels = () => { + evaluationApi.getModels().then((res) => setModels(res.data || [])).catch(() => {}) + } + useEffect(() => { if (!open) return setLoadingOptions(true) @@ -454,16 +457,14 @@ const SynthesizeModal = ({ open, onClose, onCreated, existingDatasetNames }) => value={description} onChange={(e) => setDescription(e.target.value)} /> - - Judge Model - - + { { id: 'run_id', label: 'Run ID' }, { id: 'sandbox_id', label: 'Sandbox', sortable: false }, { id: 'dataset_id', label: 'Dataset', sortable: false }, - { id: 'model_name', label: 'Judge Model', sortable: false }, + { id: 'model_name', label: 'Generator Model', sortable: false }, { id: 'status', label: 'Status' }, { id: 'eval_score', label: 'Eval Score', sortable: false }, { id: 'created_at', label: 'Created At' }, diff --git a/studio-frontend/packages/ui/src/views/evaluation/ModelSelect.jsx b/studio-frontend/packages/ui/src/views/evaluation/ModelSelect.jsx new file mode 100644 index 0000000..c5cf40a --- /dev/null +++ b/studio-frontend/packages/ui/src/views/evaluation/ModelSelect.jsx @@ -0,0 +1,589 @@ +import { useState, useRef, useCallback, useEffect } from 'react' +import PropTypes from 'prop-types' +import { + Box, + CircularProgress, + ClickAwayListener, + Divider, + InputAdornment, + List, + ListItemButton, + Paper, + TextField, + Tooltip, + Typography +} from '@mui/material' +import { IconSearch, IconDownload, IconAlertCircle, IconCheck } from '@tabler/icons-react' +import evaluationApi from '@/api/evaluation' + +// Hide embedding models from judge/synthesize dropdowns +const HIDDEN_MODEL_PATTERN = /nomic.embed/i + +// How often to poll for pull completion (ms) +const POLL_INTERVAL_MS = 1200 + +const EMPTY_PULL_STATE = { + modelName: '', + status: '', + detail: '', + error: '', + completed: null, + total: null +} + +const deriveProgressValue = (completed, total) => { + if (!Number.isFinite(completed) || !Number.isFinite(total) || total <= 0) { + return null + } + + return Math.min(100, Math.max(0, (completed / total) * 100)) +} + +/** + * Searchable judge model selector with an inline "Pull New Model" action + * and real-time pull-progress polling. + * + * Props: + * - models {Array} – list of model objects with `name` (or `id`) field + * - value {string} – currently selected model name + * - onChange {function} – called with the new model name string + * - onModelsRefresh {function} – called after a successful pull so parent can re-fetch + * - disabled {boolean} – disables the control + */ +const ModelSelect = ({ models, value, onChange, onModelsRefresh, disabled, label }) => { + const [open, setOpen] = useState(false) + const [search, setSearch] = useState('') + + // Pull-progress state — persists even after dropdown closes + const [pullState, setPullState] = useState(EMPTY_PULL_STATE) + const [statusOffset, setStatusOffset] = useState(null) + const [isReadyFading, setIsReadyFading] = useState(false) + + const pollTimerRef = useRef(null) + const clearBannerTimerRef = useRef(null) + const fadeReadyTimerRef = useRef(null) + const anchorRef = useRef(null) + const inputRef = useRef(null) + + const visibleModels = models.filter((m) => !HIDDEN_MODEL_PATTERN.test(m.name || m.id || '')) + + const filteredModels = visibleModels.filter((m) => { + if (!search.trim()) return true + return (m.name || m.id || '').toLowerCase().includes(search.trim().toLowerCase()) + }) + + const searchTag = search.trim() + const showPull = Boolean(searchTag) + const alreadyInList = visibleModels.some( + (m) => (m.name || m.id || '').toLowerCase() === searchTag.toLowerCase() + ) + const progressValue = deriveProgressValue(pullState.completed, pullState.total) + const isPulling = pullState.status === 'pulling' + + // ── Polling ────────────────────────────────────────────────────────────── + + const stopPolling = useCallback(() => { + if (pollTimerRef.current) { + clearTimeout(pollTimerRef.current) + pollTimerRef.current = null + } + }, []) + + const stopClearBannerTimer = useCallback(() => { + if (clearBannerTimerRef.current) { + clearTimeout(clearBannerTimerRef.current) + clearBannerTimerRef.current = null + } + }, []) + + const stopFadeReadyTimer = useCallback(() => { + if (fadeReadyTimerRef.current) { + clearTimeout(fadeReadyTimerRef.current) + fadeReadyTimerRef.current = null + } + }, []) + + const clearPullStateSoon = useCallback(() => { + setIsReadyFading(false) + stopFadeReadyTimer() + stopClearBannerTimer() + fadeReadyTimerRef.current = setTimeout(() => { + setIsReadyFading(true) + }, 2200) + clearBannerTimerRef.current = setTimeout(() => { + setIsReadyFading(false) + setPullState(EMPTY_PULL_STATE) + }, 3000) + }, [stopClearBannerTimer, stopFadeReadyTimer]) + + const applyPullEntry = useCallback( + (modelName, entry) => { + if (!entry) return false + + stopClearBannerTimer() + + setPullState({ + modelName, + status: entry.status || '', + detail: entry.detail || '', + error: entry.error || '', + completed: Number.isFinite(entry.completed) ? entry.completed : null, + total: Number.isFinite(entry.total) ? entry.total : null + }) + + if (entry.status === 'ready') { + stopPolling() + onModelsRefresh && onModelsRefresh() + clearPullStateSoon() + return true + } + + if (entry.status === 'error') { + stopPolling() + return true + } + + return false + }, + [clearPullStateSoon, onModelsRefresh, stopClearBannerTimer, stopPolling] + ) + + const pollPullStatus = useCallback( + async (modelName) => { + try { + const res = await evaluationApi.getPullStatus() + const statusMap = res.data || {} + const entry = statusMap[modelName] + + if (!entry) { + pollTimerRef.current = setTimeout(() => pollPullStatus(modelName), POLL_INTERVAL_MS) + return + } + + const done = applyPullEntry(modelName, entry) + if (!done) { + pollTimerRef.current = setTimeout(() => pollPullStatus(modelName), POLL_INTERVAL_MS) + } + } catch { + pollTimerRef.current = setTimeout(() => pollPullStatus(modelName), POLL_INTERVAL_MS) + } + }, + [applyPullEntry] + ) + + const startPolling = useCallback( + (modelName) => { + stopPolling() + pollPullStatus(modelName) + }, + [pollPullStatus, stopPolling] + ) + + // Clean up on unmount + useEffect( + () => () => { + stopPolling() + stopClearBannerTimer() + stopFadeReadyTimer() + }, + [stopClearBannerTimer, stopFadeReadyTimer, stopPolling] + ) + + // ── Dropdown helpers ────────────────────────────────────────────────────── + + const handleOpen = useCallback(() => { + if (disabled) return + setSearch('') + setOpen(true) + }, [disabled]) + + const handleClickAway = useCallback(() => { + setOpen(false) + setSearch('') + }, []) + + const handleSelect = useCallback( + (modelName) => { + onChange(modelName) + setOpen(false) + setSearch('') + }, + [onChange] + ) + + // ── Pull action ─────────────────────────────────────────────────────────── + + const handlePull = useCallback(async () => { + if (!searchTag || isPulling) return + + setIsReadyFading(false) + stopFadeReadyTimer() + stopClearBannerTimer() + setPullState({ + modelName: searchTag, + status: 'pulling', + detail: 'Submitting pull request', + error: '', + completed: null, + total: null + }) + setOpen(false) + setSearch('') + // Pre-select so the field shows the model name immediately + onChange(searchTag) + + try { + await evaluationApi.pullModel({ model_name: searchTag }) + setPullState((current) => ({ + ...current, + detail: 'Waiting for Ollama progress…' + })) + startPolling(searchTag) + } catch (err) { + const msg = + err?.response?.data?.detail || + err?.response?.data?.message || + 'Failed to initiate model pull.' + setPullState({ + modelName: searchTag, + status: 'error', + detail: 'Pull failed', + error: msg, + completed: null, + total: null + }) + } + }, [isPulling, onChange, searchTag, startPolling, stopClearBannerTimer, stopFadeReadyTimer]) + + useEffect(() => { + if (!value) return + if (pullState.modelName) return + + let active = true + + const hydratePullState = async () => { + try { + const res = await evaluationApi.getPullStatus() + const entry = res.data?.[value] + if (!active || !entry) return + if (entry.status !== 'pulling') return + applyPullEntry(value, entry) + startPolling(value) + } catch { + // Ignore hydration failures; explicit pulls still start polling. + } + } + + hydratePullState() + + return () => { + active = false + } + }, [applyPullEntry, pullState.modelName, startPolling, value]) + + // Display: when open show search query; when closed show selected value + const inputValue = open ? search : value + const statusAnchorText = inputValue || pullState.modelName + const hasInlineStatus = isPulling || pullState.status === 'ready' || pullState.status === 'error' + + const updateStatusOffset = useCallback(() => { + if (!hasInlineStatus || !statusAnchorText || !anchorRef.current || !inputRef.current || typeof window === 'undefined') { + setStatusOffset(null) + return + } + + const canvas = document.createElement('canvas') + const context = canvas.getContext('2d') + if (!context) { + setStatusOffset(null) + return + } + + const wrapperRect = anchorRef.current.getBoundingClientRect() + const inputRect = inputRef.current.getBoundingClientRect() + const computedStyle = window.getComputedStyle(inputRef.current) + const indicatorSize = 22 + const gap = 8 + + context.font = computedStyle.font + const textWidth = context.measureText(statusAnchorText).width + const baseLeft = inputRect.left - wrapperRect.left + const minLeft = baseLeft + 2 + const maxLeft = wrapperRect.width - indicatorSize - 18 + const nextOffset = Math.min(Math.max(minLeft + textWidth + gap, minLeft), maxLeft) + + setStatusOffset(nextOffset) + }, [hasInlineStatus, statusAnchorText]) + + useEffect(() => { + updateStatusOffset() + }, [updateStatusOffset]) + + useEffect(() => { + if (!hasInlineStatus || typeof window === 'undefined') return undefined + + const handleResize = () => updateStatusOffset() + + window.addEventListener('resize', handleResize) + + return () => { + window.removeEventListener('resize', handleResize) + } + }, [hasInlineStatus, updateStatusOffset]) + + const renderInlineStatus = () => { + if (!hasInlineStatus || statusOffset === null) return null + + const indicatorSx = { + position: 'absolute', + left: statusOffset, + top: '50%', + transform: 'translateY(-50%)', + display: 'flex', + alignItems: 'center', + justifyContent: 'center', + width: 22, + height: 22, + zIndex: 2 + } + + if (isPulling) { + return ( + + + + {progressValue !== null && ( + + + {`${Math.round(progressValue)}%`} + + + )} + + + ) + } + + if (pullState.status === 'ready') { + return ( + + + + + + ) + } + + if (pullState.status === 'error') { + return ( + + + + + + + + ) + } + + return null + } + + return ( + + + setSearch(e.target.value)} + disabled={disabled} + inputRef={inputRef} + InputProps={{ + sx: hasInlineStatus + ? { + '& input': { + pr: 5 + } + } + : undefined, + startAdornment: ( + + + + ) + }} + /> + {renderInlineStatus()} + + {open && ( + + + {filteredModels.length === 0 && !showPull && ( + + + No models available. + + + )} + + {filteredModels.map((m) => { + const name = m.name || m.id || '' + return ( + handleSelect(name)} + sx={{ py: 1, px: 2 }} + > + + {name} + + + ) + })} + + + {/* Pull new model action */} + {showPull && ( + <> + + + {isPulling ? ( + + ) : ( + + + + )} + + + {alreadyInList ? 'Re-pull Model' : 'Pull New Model'} + + + {searchTag} + + + + + )} + + )} + + + ) +} + +ModelSelect.propTypes = { + models: PropTypes.arrayOf( + PropTypes.shape({ + name: PropTypes.string, + id: PropTypes.string + }) + ).isRequired, + value: PropTypes.string.isRequired, + onChange: PropTypes.func.isRequired, + onModelsRefresh: PropTypes.func, + disabled: PropTypes.bool, + label: PropTypes.string +} + +ModelSelect.defaultProps = { + onModelsRefresh: null, + disabled: false, + label: 'Model' +} + +export default ModelSelect From ea84e3c69e2f82ae6e91d5ed270b9cda62c819bc Mon Sep 17 00:00:00 2001 From: wwanarif Date: Tue, 12 May 2026 02:34:43 +0000 Subject: [PATCH 6/9] improving UX/UI and fixing bugs Co-authored-by: Copilot Signed-off-by: wwanarif --- .../docker-compose.local.yml | 1 - .../manifests/ollama-manifest.yaml | 10 +- studio-eval/app/models/eval_models.py | 5 +- studio-eval/app/routers/eval_router.py | 23 +- studio-eval/app/services/dataset_service.py | 156 ++++++- studio-eval/app/services/eval_service.py | 2 - .../app/services/response_collector.py | 11 +- studio-eval/tests/test_eval_service.py | 217 +++++++++- .../src/ui-component/file/FileUploadArea.jsx | 4 +- .../src/views/evaluation/CreateRunModal.jsx | 3 +- .../views/evaluation/DatasetDetailDialog.jsx | 198 ++++++++- .../ui/src/views/evaluation/DatasetTab.jsx | 80 +++- .../src/views/evaluation/RunDetailsModal.jsx | 64 ++- .../packages/ui/src/views/tracer/index.jsx | 382 ++++++++++-------- 14 files changed, 917 insertions(+), 239 deletions(-) diff --git a/setup-scripts/setup-development/docker-compose.local.yml b/setup-scripts/setup-development/docker-compose.local.yml index 4647296..217ec25 100644 --- a/setup-scripts/setup-development/docker-compose.local.yml +++ b/setup-scripts/setup-development/docker-compose.local.yml @@ -6,7 +6,6 @@ services: studio-frontend: - image: studio-frontend:local build: context: ../../studio-frontend additional_contexts: diff --git a/setup-scripts/setup-genai-studio/manifests/ollama-manifest.yaml b/setup-scripts/setup-genai-studio/manifests/ollama-manifest.yaml index db0de87..c88c405 100644 --- a/setup-scripts/setup-genai-studio/manifests/ollama-manifest.yaml +++ b/setup-scripts/setup-genai-studio/manifests/ollama-manifest.yaml @@ -57,6 +57,12 @@ spec: env: - name: OLLAMA_HOST value: "0.0.0.0" + - name: OLLAMA_KEEP_ALIVE + value: "24h" + - name: OLLAMA_MAX_LOADED_MODELS + value: "2" + - name: OLLAMA_NUM_PARALLEL + value: "2" - name: HTTP_PROXY value: "${HTTP_PROXY}" - name: HTTPS_PROXY @@ -66,10 +72,10 @@ spec: resources: requests: memory: "12Gi" - cpu: "2" + cpu: "4" limits: memory: "32Gi" - cpu: "8" + cpu: "16" volumeMounts: - name: ollama-models mountPath: /root/.ollama diff --git a/studio-eval/app/models/eval_models.py b/studio-eval/app/models/eval_models.py index 6216196..f1cd349 100644 --- a/studio-eval/app/models/eval_models.py +++ b/studio-eval/app/models/eval_models.py @@ -10,7 +10,6 @@ SUPPORTED_METRICS: Set[str] = { "AnswerRelevancy", "Faithfulness", - "Hallucination", } # --------------------------------------------------------------------------- @@ -191,7 +190,7 @@ class SynthesizeFromDocRequest(BaseModel): description: Optional[str] = None model_name: str embed_model_name: str = "nomic-embed-text" - target_goldens: int = 10 + target_goldens: Optional[int] = None max_goldens_per_context: int = 2 max_contexts: int = 5 max_context_length: int = 3 @@ -206,7 +205,7 @@ class SynthesizeFromDocRequest(BaseModel): @field_validator("target_goldens") @classmethod def target_goldens_positive(cls, v): - if v < 1: + if v is not None and v < 1: raise ValueError("target_goldens must be >= 1") return v diff --git a/studio-eval/app/routers/eval_router.py b/studio-eval/app/routers/eval_router.py index 90aedde..30a0654 100644 --- a/studio-eval/app/routers/eval_router.py +++ b/studio-eval/app/routers/eval_router.py @@ -30,6 +30,17 @@ router = APIRouter(prefix="/eval", tags=["eval"]) +ALLOWED_SYNTHESIS_FILE_EXTENSIONS = {".pdf", ".docx"} + +def _resolve_synthesis_file_ext(filename: Optional[str]) -> str: + file_ext = Path(filename).suffix.lower() if filename else ".pdf" + if file_ext not in ALLOWED_SYNTHESIS_FILE_EXTENSIONS: + allowed = ", ".join(sorted(ALLOWED_SYNTHESIS_FILE_EXTENSIONS)) + raise HTTPException( + status_code=400, + detail=f"Unsupported file type '{file_ext}'. Supported types: {allowed}", + ) + return file_ext # --------------------------------------------------------------------------- # Eval runs @@ -97,7 +108,7 @@ async def synthesize_dataset( model_name: str = Form(...), embed_model_name: str = Form("nomic-embed-text"), description: Optional[str] = Form(None), - num_goldens: int = Form(10), + num_goldens: Optional[int] = Form(None), max_goldens_per_document: int = Form(2), max_contexts: int = Form(5), min_contexts: int = Form(1), @@ -114,7 +125,7 @@ async def synthesize_dataset( and synthesize golden Q/A pairs with DeepEval in the background.""" try: file_bytes = await file.read() - file_ext = Path(file.filename).suffix.lower() if file.filename else ".pdf" + file_ext = _resolve_synthesis_file_ext(file.filename) payload = SynthesizeFromDocRequest( name=name, @@ -148,11 +159,17 @@ async def synthesize_dataset( completed_contexts=0, total_contexts=0, completed_goldens=0, - target_goldens=payload.target_goldens, + target_goldens=( + payload.target_goldens + if payload.target_goldens is not None + else payload.max_contexts * payload.max_goldens_per_context + ), created_at=dataset.created_at, updated_at=dataset.updated_at, entry_count=0, ) + except HTTPException: + raise except Exception as exc: logger.error("Failed to create synthesis job: %s", exc) raise HTTPException(status_code=500, detail=str(exc)) diff --git a/studio-eval/app/services/dataset_service.py b/studio-eval/app/services/dataset_service.py index 5c50538..40dc772 100644 --- a/studio-eval/app/services/dataset_service.py +++ b/studio-eval/app/services/dataset_service.py @@ -33,7 +33,6 @@ _synthesis_tasks: Dict[str, asyncio.Task] = {} _synthesis_processes: Dict[str, multiprocessing.Process] = {} - def request_dataset_cancellation(dataset_id: str) -> None: """Signal the background synthesis job for *dataset_id* to exit at its next checkpoint.""" _cancelled_datasets.add(dataset_id) @@ -107,6 +106,21 @@ def _goldens_to_records(goldens: list) -> List[Dict[str, Any]]: ] +def _update_golden_progress(db: Session, dataset_id: str, completed_goldens: int) -> None: + """Persist the current golden count so the UI can poll progress.""" + try: + dataset = db.query(EvalDataset).filter(EvalDataset.id == dataset_id).first() + if dataset and dataset.status == "synthesizing": + dataset.completed_goldens = completed_goldens + db.commit() + except Exception: + logger.debug("Failed to update golden progress for %s", dataset_id, exc_info=True) + try: + db.rollback() + except Exception: + pass + + def _start_synthesis_process( dataset_id: str, tmp_path: str, @@ -146,20 +160,75 @@ async def _wait_for_synthesis_process_result( dataset_id: str, process: multiprocessing.Process, result_queue, + db: Optional[Session] = None, ) -> Dict[str, Any]: - while process.is_alive(): + """Consume messages from the synthesis child process. + + The child sends: + * ``{"type": "progress", "completed_goldens": N, "batch": [...]}`` + after each context's goldens are generated. + * ``{"type": "done"}`` when generation finishes successfully. + * ``{"type": "error", "message": "..."}`` on failure. + + Legacy callers that send a single ``{"records": [...]}`` or + ``{"error": "..."}`` dict are still supported. + """ + all_records: List[Dict[str, Any]] = [] + + while True: if dataset_id in _cancelled_datasets: raise asyncio.CancelledError() - await asyncio.sleep(0.25) + try: + msg = await asyncio.to_thread(result_queue.get, True, 0.25) + except Empty: + if process.is_alive(): + continue + # Process exited without a message — fall through to drain. + break + + msg_type = msg.get("type") + + if msg_type == "progress": + batch = msg.get("batch") or [] + all_records.extend(batch) + completed = msg.get("completed_goldens", len(all_records)) + if db is not None: + _update_golden_progress(db, dataset_id, completed) + continue + + if msg_type == "done": + await asyncio.to_thread(process.join, 1) + return {"records": all_records} + + if msg_type == "error": + await asyncio.to_thread(process.join, 1) + return {"error": msg.get("message", "Unknown error")} + + # Legacy single-shot protocol (no "type" key). + await asyncio.to_thread(process.join, 1) + return msg + + # Process died — try to drain one last message. await asyncio.to_thread(process.join, 1) try: - return await asyncio.to_thread(result_queue.get, True, 1) - except Empty as exc: - if process.exitcode in (0, None): - raise RuntimeError("Synthesis process exited without returning results") from exc - raise RuntimeError(f"Synthesis process exited with code {process.exitcode}") from exc + msg = await asyncio.to_thread(result_queue.get, True, 1) + if msg.get("type") == "error": + return {"error": msg.get("message", "Unknown error")} + if msg.get("type") == "progress": + all_records.extend(msg.get("batch") or []) + elif "records" in msg or "error" in msg: + return msg + except Empty: + pass + + if all_records: + return {"records": all_records} + + if process.exitcode in (0, None): + raise RuntimeError("Synthesis process exited without returning results") + raise RuntimeError(f"Synthesis process exited with code {process.exitcode}") # --------------------------------------------------------------------------- @@ -301,9 +370,13 @@ def _run_synthesizer(payload: SynthesizeRequest, judge: OllamaJudge, contexts: L def _resolve_doc_generation_limits(payload: SynthesizeFromDocRequest) -> tuple[int, int]: - target_goldens = max(payload.target_goldens, 1) max_contexts = max(payload.max_contexts, 1) max_goldens_per_context = max(payload.max_goldens_per_context, 1) + + if payload.target_goldens is None: + return max_contexts, max_goldens_per_context + + target_goldens = max(payload.target_goldens, 1) best_plan: Optional[tuple[int, int]] = None best_total: Optional[int] = None @@ -331,8 +404,14 @@ def _run_synthesizer_from_docs( payload: SynthesizeFromDocRequest, judge: OllamaJudge, embedder: OllamaEmbeddingModel, + progress_queue=None, ) -> list: - """Blocking call to DeepEval Synthesizer.generate_goldens_from_docs — run via asyncio.to_thread.""" + """Blocking call to DeepEval Synthesizer.generate_goldens_from_docs. + + When *progress_queue* is provided the synthesizer processes one context + at a time and pushes a progress message after each context so the parent + process can update the DB (and therefore the UI) incrementally. + """ ensure_pydantic_v2_apis() from app.compat import patch_deepeval_document_chunker # noqa: PLC0415 patch_deepeval_document_chunker() @@ -343,7 +422,35 @@ def _run_synthesizer_from_docs( FiltrationConfig, ) - synthesizer = Synthesizer( + # When a progress queue is available, subclass the synthesizer so that + # ``generate_goldens_from_contexts`` runs one context at a time and + # sends each batch through the queue as a small message (avoiding the + # pipe-buffer deadlock that occurs when the full result set is sent in + # a single ``queue.put``). + if progress_queue is not None: + class _ProgressSynthesizer(Synthesizer): # noqa: N801 + def generate_goldens_from_contexts(self, contexts, *args, **kwargs): + all_goldens = [] + for context in contexts: + batch = super().generate_goldens_from_contexts( + [context], *args, **kwargs + ) + all_goldens.extend(batch) + try: + progress_queue.put({ + "type": "progress", + "completed_goldens": len(all_goldens), + "batch": _goldens_to_records(batch), + }) + except Exception: + pass + return all_goldens + + SynthesizerClass = _ProgressSynthesizer + else: + SynthesizerClass = Synthesizer + + synthesizer = SynthesizerClass( model=judge, async_mode=payload.async_mode, max_concurrent=payload.max_concurrent, @@ -379,12 +486,17 @@ def _run_synthesizer_from_docs_worker( embedder = OllamaEmbeddingModel(model_name=payload.embed_model_name) try: - goldens = _run_synthesizer_from_docs(tmp_path, payload, judge, embedder) - result_queue.put({"records": _goldens_to_records(goldens)}) + # progress_queue=result_queue makes the synthesizer send small + # per-context batches instead of one giant payload at the end, + # which avoids the pipe-buffer deadlock. + _run_synthesizer_from_docs( + tmp_path, payload, judge, embedder, progress_queue=result_queue, + ) + result_queue.put({"type": "done"}) except Exception as exc: message = str(exc).strip() or exc.__class__.__name__ logger.exception("Document synthesis worker failed for %s", tmp_path) - result_queue.put({"error": message}) + result_queue.put({"type": "error", "message": message}) async def synthesize_dataset(db: Session, payload: SynthesizeRequest) -> EvalDataset: @@ -491,7 +603,15 @@ async def run_synthesis_background( dataset.status = "synthesizing" dataset.total_contexts = 0 dataset.completed_contexts = 0 - dataset.target_goldens = payload.target_goldens + # When target_goldens is not set by the user, use the computed maximum + # (max_contexts × max_goldens_per_context) so the UI progress bar + # always has a denominator. DeepEval may produce fewer goldens than + # this estimate (quality filtering), so it is only an upper bound. + dataset.target_goldens = ( + payload.target_goldens + if payload.target_goldens is not None + else payload.max_contexts * payload.max_goldens_per_context + ) dataset.completed_goldens = 0 db.commit() @@ -515,7 +635,7 @@ async def run_synthesis_background( tmp_path = tmp.name logger.info( - "Background synthesis for dataset %s '%s': embed=%s, file=%s, target=%d", + "Background synthesis for dataset %s '%s': embed=%s, file=%s, target=%s", dataset_id, payload.name, payload.embed_model_name, tmp_path, payload.target_goldens, ) @@ -526,7 +646,9 @@ async def run_synthesis_background( return synthesis_process, result_queue = _start_synthesis_process(dataset_id, tmp_path, payload) - worker_result = await _wait_for_synthesis_process_result(dataset_id, synthesis_process, result_queue) + worker_result = await _wait_for_synthesis_process_result( + dataset_id, synthesis_process, result_queue, db=db, + ) if worker_result.get("error"): raise RuntimeError(worker_result["error"]) diff --git a/studio-eval/app/services/eval_service.py b/studio-eval/app/services/eval_service.py index e9f9370..f015f68 100644 --- a/studio-eval/app/services/eval_service.py +++ b/studio-eval/app/services/eval_service.py @@ -100,13 +100,11 @@ def _get_metric_classes() -> dict: from deepeval.metrics import ( # noqa: PLC0415 AnswerRelevancyMetric, FaithfulnessMetric, - HallucinationMetric, ) _METRIC_CLASSES = { "AnswerRelevancy": AnswerRelevancyMetric, "Faithfulness": FaithfulnessMetric, - "Hallucination": HallucinationMetric, } return _METRIC_CLASSES diff --git a/studio-eval/app/services/response_collector.py b/studio-eval/app/services/response_collector.py index eb58b23..a97f92e 100644 --- a/studio-eval/app/services/response_collector.py +++ b/studio-eval/app/services/response_collector.py @@ -48,7 +48,10 @@ def _parse_sse_token(payload: str) -> Optional[str]: except json.JSONDecodeError: return payload if payload else None if not isinstance(chunk, dict): - return None + # Raw token that happens to be a valid JSON literal (e.g. a number + # like 1 or 3.40). Return the original payload text to preserve + # exact formatting (e.g. trailing zeros). + return payload.strip() if payload.strip() else None choices = chunk.get("choices", []) if not choices: return None @@ -63,7 +66,11 @@ def _extract_sse_answer(body: str) -> Optional[str]: line = raw_line.rstrip("\r") if not line or not line.startswith("data:"): continue - payload = line[6:] # skip "data: " (preserve leading space in token) + # Strip the "data:" prefix. Per SSE spec, a single space after the + # colon is optional and should be ignored if present. + payload = line[5:] # skip "data:" + if payload.startswith(" "): + payload = payload[1:] # strip the optional single leading space if payload.strip() == "[DONE]": break token = _parse_sse_token(payload) diff --git a/studio-eval/tests/test_eval_service.py b/studio-eval/tests/test_eval_service.py index 5fdecbe..eabe352 100644 --- a/studio-eval/tests/test_eval_service.py +++ b/studio-eval/tests/test_eval_service.py @@ -1,9 +1,10 @@ +import asyncio + from app.services import eval_service from app.services import dataset_service from app.compat import ensure_pydantic_v2_apis from app.models.eval_models import SynthesizeFromDocRequest - def test_normalize_context_handles_json_string_list(): context = '["alpha", "beta"]' @@ -102,6 +103,46 @@ def terminate(self): dataset_service._synthesis_processes.pop(dataset_id, None) +def test_wait_for_synthesis_process_result_drains_queue_before_process_exit(): + class FakeProcess: + def __init__(self): + self.alive = True + self.exitcode = 0 + self.join_calls = [] + + def is_alive(self): + return self.alive + + def join(self, timeout=None): + self.join_calls.append(timeout) + self.alive = False + + class FakeQueue: + def __init__(self, process): + self.process = process + self.calls = [] + + def get(self, block=True, timeout=None): + self.calls.append((block, timeout)) + self.process.alive = False + return {"records": [{"input": "hello"}]} + + async def run_wait(process, result_queue): + return await asyncio.wait_for( + dataset_service._wait_for_synthesis_process_result("dataset-123", process, result_queue), + timeout=1, + ) + + process = FakeProcess() + result_queue = FakeQueue(process) + + result = asyncio.run(run_wait(process, result_queue)) + + assert result == {"records": [{"input": "hello"}]} + assert result_queue.calls == [(True, 0.25)] + assert process.join_calls == [1] + + def test_ensure_pydantic_v2_apis_aliases_invalid_collection_exception(monkeypatch): import chromadb.errors as chroma_errors @@ -206,4 +247,176 @@ def __init__(self, **kwargs): assert compat_calls == [True] assert chunker_patch_calls == [True] assert result[0]["max_goldens_per_context"] == 2 - assert result[0]["context_construction_config"].kwargs["max_contexts_per_document"] == 5 \ No newline at end of file + assert result[0]["context_construction_config"].kwargs["max_contexts_per_document"] == 5 + +# --------------------------------------------------------------------------- +# Progress-protocol tests +# --------------------------------------------------------------------------- + +def test_wait_for_synthesis_handles_progress_then_done(): + """Parent must collect batches from progress messages and return them when done.""" + + class FakeProcess: + def __init__(self): + self.alive = True + self.exitcode = 0 + + def is_alive(self): + return self.alive + + def join(self, timeout=None): + self.alive = False + + class FakeQueue: + def __init__(self, messages, process): + self._msgs = list(messages) + self._process = process + + def get(self, block=True, timeout=None): + if self._msgs: + msg = self._msgs.pop(0) + if not self._msgs: + self._process.alive = False + return msg + from queue import Empty + raise Empty + + messages = [ + {"type": "progress", "completed_goldens": 2, "batch": [{"input": "q1"}, {"input": "q2"}]}, + {"type": "progress", "completed_goldens": 4, "batch": [{"input": "q3"}, {"input": "q4"}]}, + {"type": "done"}, + ] + + process = FakeProcess() + queue = FakeQueue(messages, process) + + result = asyncio.run(asyncio.wait_for( + dataset_service._wait_for_synthesis_process_result("ds-1", process, queue), + timeout=2, + )) + + assert result == {"records": [{"input": "q1"}, {"input": "q2"}, {"input": "q3"}, {"input": "q4"}]} + + +def test_wait_for_synthesis_handles_error_message(): + """Parent must return the error from a typed error message.""" + + class FakeProcess: + def __init__(self): + self.alive = True + self.exitcode = 1 + + def is_alive(self): + return self.alive + + def join(self, timeout=None): + self.alive = False + + class FakeQueue: + def __init__(self, msg, process): + self._msg = msg + self._process = process + + def get(self, block=True, timeout=None): + if self._msg: + msg = self._msg + self._msg = None + self._process.alive = False + return msg + from queue import Empty + raise Empty + + process = FakeProcess() + queue = FakeQueue({"type": "error", "message": "boom"}, process) + + result = asyncio.run(asyncio.wait_for( + dataset_service._wait_for_synthesis_process_result("ds-2", process, queue), + timeout=2, + )) + + assert result == {"error": "boom"} + +def test_progress_synthesizer_sends_per_context_batches(monkeypatch): + """_run_synthesizer_from_docs with progress_queue must send per-context messages.""" + compat_calls = [] + chunker_patch_calls = [] + + def fake_ensure_pydantic_v2_apis(): + compat_calls.append(True) + + def fake_patch_deepeval_document_chunker(): + chunker_patch_calls.append(True) + + class FakeGolden: + def __init__(self, text): + self.input = text + self.expected_output = f"answer-{text}" + self.context = [f"ctx-{text}"] + + class FakeSynthesizer: + def __init__(self, **kwargs): + self.kwargs = kwargs + + def generate_goldens_from_docs(self, **kwargs): + # DeepEval internally calls self.generate_goldens_from_contexts + return self.generate_goldens_from_contexts( + [["ctx-a"], ["ctx-b"]], + max_goldens_per_context=kwargs.get("max_goldens_per_context", 2), + ) + + def generate_goldens_from_contexts(self, contexts, *args, **kwargs): + # Returns one golden per context. + goldens = [] + for ctx in contexts: + goldens.append(FakeGolden(ctx[0])) + return goldens + + class FakeConfig: + def __init__(self, **kwargs): + self.kwargs = kwargs + + import sys + from types import ModuleType + + synthesizer_module = ModuleType("deepeval.synthesizer") + config_module = ModuleType("deepeval.synthesizer.config") + compat_module = ModuleType("app.compat") + synthesizer_module.Synthesizer = FakeSynthesizer + config_module.ContextConstructionConfig = FakeConfig + config_module.EvolutionConfig = FakeConfig + config_module.FiltrationConfig = FakeConfig + compat_module.patch_deepeval_document_chunker = fake_patch_deepeval_document_chunker + + monkeypatch.setattr(dataset_service, "ensure_pydantic_v2_apis", fake_ensure_pydantic_v2_apis) + monkeypatch.setitem(sys.modules, "deepeval", ModuleType("deepeval")) + monkeypatch.setitem(sys.modules, "deepeval.synthesizer", synthesizer_module) + monkeypatch.setitem(sys.modules, "deepeval.synthesizer.config", config_module) + monkeypatch.setitem(sys.modules, "app.compat", compat_module) + + from queue import Queue + progress_queue = Queue() + + payload = SynthesizeFromDocRequest( + name="ds", model_name="m", target_goldens=4, + max_goldens_per_context=2, max_contexts=5, + ) + + dataset_service._run_synthesizer_from_docs( + "/tmp/fake.pdf", payload, "judge", "embedder", + progress_queue=progress_queue, + ) + + # The ProgressSynthesizer should have sent one message per context. + msgs = [] + while not progress_queue.empty(): + msgs.append(progress_queue.get_nowait()) + + assert len(msgs) == 2 # two contexts + assert msgs[0]["type"] == "progress" + assert msgs[0]["completed_goldens"] == 1 + assert msgs[1]["completed_goldens"] == 2 + # Each batch should have exactly one record (one golden per context). + assert len(msgs[0]["batch"]) == 1 + assert len(msgs[1]["batch"]) == 1 + assert msgs[0]["batch"][0]["input"] == "ctx-a" + assert msgs[1]["batch"][0]["input"] == "ctx-b" \ No newline at end of file diff --git a/studio-frontend/packages/ui/src/ui-component/file/FileUploadArea.jsx b/studio-frontend/packages/ui/src/ui-component/file/FileUploadArea.jsx index 665d594..24546e8 100644 --- a/studio-frontend/packages/ui/src/ui-component/file/FileUploadArea.jsx +++ b/studio-frontend/packages/ui/src/ui-component/file/FileUploadArea.jsx @@ -29,6 +29,7 @@ const FileUploadArea = ({ acceptedTypes = ['.json', '.jsonl', '.csv'], maxSizeMB = 10, error = null, + invalidTypeMessage, title = 'Drop your training dataset file here or click to browse', subtitle, buttonLabel = 'Choose File' @@ -50,7 +51,7 @@ const FileUploadArea = ({ // Check file type const fileExtension = '.' + file.name.split('.').pop().toLowerCase() if (!acceptedTypes.includes(fileExtension)) { - errors.push(`File type ${fileExtension} not supported. Accepted types: ${acceptedTypes.join(', ')}`) + errors.push(invalidTypeMessage || `File type ${fileExtension} not supported. Accepted types: ${acceptedTypes.join(', ')}`) } // Check file size @@ -317,6 +318,7 @@ FileUploadArea.propTypes = { acceptedTypes: PropTypes.arrayOf(PropTypes.string), maxSizeMB: PropTypes.number, error: PropTypes.string, + invalidTypeMessage: PropTypes.string, title: PropTypes.string, subtitle: PropTypes.string, buttonLabel: PropTypes.string diff --git a/studio-frontend/packages/ui/src/views/evaluation/CreateRunModal.jsx b/studio-frontend/packages/ui/src/views/evaluation/CreateRunModal.jsx index 798fc16..95658c8 100644 --- a/studio-frontend/packages/ui/src/views/evaluation/CreateRunModal.jsx +++ b/studio-frontend/packages/ui/src/views/evaluation/CreateRunModal.jsx @@ -39,8 +39,7 @@ import ModelSelect from './ModelSelect' const METRICS = [ 'AnswerRelevancy', - 'Faithfulness', - 'Hallucination' + 'Faithfulness' ] const sanitizeWorkflowNodes = (nodes = []) => diff --git a/studio-frontend/packages/ui/src/views/evaluation/DatasetDetailDialog.jsx b/studio-frontend/packages/ui/src/views/evaluation/DatasetDetailDialog.jsx index aaec8ef..28257c0 100644 --- a/studio-frontend/packages/ui/src/views/evaluation/DatasetDetailDialog.jsx +++ b/studio-frontend/packages/ui/src/views/evaluation/DatasetDetailDialog.jsx @@ -12,9 +12,12 @@ import { DialogActions, DialogContent, DialogTitle, + Divider, + Drawer, IconButton, Paper, Stack, + Tab, Table, TableBody, TableCell, @@ -22,6 +25,7 @@ import { TableHead, TablePagination, TableRow, + Tabs, TextField, Tooltip, Typography @@ -79,6 +83,28 @@ const textToContext = (text) => { .filter(Boolean) } +function TabPanel(props) { + const { children, value, index, ...other } = props + + return ( + + ) +} + +TabPanel.propTypes = { + children: PropTypes.node, + index: PropTypes.number.isRequired, + value: PropTypes.number.isRequired +} + // ── DatasetDetailDialog ─────────────────────────────────────────────────────── const DatasetDetailDialog = ({ open, datasetId, onClose, onDatasetChanged }) => { @@ -105,6 +131,11 @@ const DatasetDetailDialog = ({ open, datasetId, onClose, onDatasetChanged }) => // Pagination const [page, setPage] = useState(0) + // Focused row detail drawer + const [selectedEntry, setSelectedEntry] = useState(null) + const [drawerOpen, setDrawerOpen] = useState(false) + const [detailTabValue, setDetailTabValue] = useState(0) + // ── Data loading ────────────────────────────────────────────────────────── const loadDataset = useCallback(async (id) => { @@ -127,6 +158,9 @@ const DatasetDetailDialog = ({ open, datasetId, onClose, onDatasetChanged }) => setPage(0) setEditingRow(null) setConfirmDeleteId(null) + setSelectedEntry(null) + setDrawerOpen(false) + setDetailTabValue(0) setMetaError('') setRowError('') setDataset(null) @@ -165,6 +199,8 @@ const DatasetDetailDialog = ({ open, datasetId, onClose, onDatasetChanged }) => // ── Entry editing ───────────────────────────────────────────────────────── const handleEditRow = (entry) => { + setDrawerOpen(false) + setSelectedEntry(null) setRowError('') setEditingRow({ id: entry.id, @@ -175,6 +211,8 @@ const DatasetDetailDialog = ({ open, datasetId, onClose, onDatasetChanged }) => } const handleAddRow = () => { + setDrawerOpen(false) + setSelectedEntry(null) setRowError('') setEditingRow({ id: null, input: '', expected_output: '', contextText: '' }) // Navigate to the page where the new row will appear @@ -189,6 +227,21 @@ const DatasetDetailDialog = ({ open, datasetId, onClose, onDatasetChanged }) => setRowError('') } + const handleOpenDrawer = (entry, entryNumber) => { + setDetailTabValue(0) + setSelectedEntry({ ...entry, _entryNumber: entryNumber }) + setDrawerOpen(true) + } + + const handleCloseDrawer = () => { + setDrawerOpen(false) + setSelectedEntry(null) + } + + const handleDetailTabChange = (event, newValue) => { + setDetailTabValue(newValue) + } + const handleSaveRow = async () => { if (!editingRow.input.trim()) { setRowError('Input is required.') @@ -235,6 +288,9 @@ const DatasetDetailDialog = ({ open, datasetId, onClose, onDatasetChanged }) => await evaluationApi.deleteEntry(datasetId, entryId) const updatedEntries = (dataset?.entries || []).filter((e) => e.id !== entryId) setDataset((prev) => ({ ...prev, entries: updatedEntries })) + if (selectedEntry?.id === entryId) { + handleCloseDrawer() + } onDatasetChanged && onDatasetChanged({ id: datasetId, entry_count: updatedEntries.length }) setConfirmDeleteId(null) // Adjust page down if we removed the last entry on the current page @@ -407,7 +463,20 @@ const DatasetDetailDialog = ({ open, datasetId, onClose, onDatasetChanged }) => const isConfirmingDelete = confirmDeleteId === entry.id return ( - + handleOpenDrawer(entry, globalIdx) + : undefined + } + sx={ + !isEditing && !isConfirmingDelete + ? { cursor: 'pointer' } + : undefined + } + > {globalIdx} {isEditing ? ( @@ -475,7 +544,10 @@ const DatasetDetailDialog = ({ open, datasetId, onClose, onDatasetChanged }) => { + event.stopPropagation() + handleSaveRow() + }} disabled={savingRow} > {savingRow ? ( @@ -489,7 +561,10 @@ const DatasetDetailDialog = ({ open, datasetId, onClose, onDatasetChanged }) => { + event.stopPropagation() + handleCancelEdit() + }} disabled={savingRow} > @@ -515,7 +590,10 @@ const DatasetDetailDialog = ({ open, datasetId, onClose, onDatasetChanged }) => size='small' color='error' variant='contained' - onClick={() => handleDeleteEntry(entry.id)} + onClick={(event) => { + event.stopPropagation() + handleDeleteEntry(entry.id) + }} disabled={deletingEntry} startIcon={ deletingEntry ? ( @@ -530,7 +608,10 @@ const DatasetDetailDialog = ({ open, datasetId, onClose, onDatasetChanged }) => + + theme.zIndex.modal + 10 }} + PaperProps={{ + sx: { + width: { xs: '100%', sm: 520, md: 680 }, + p: 3, + bgcolor: 'background.paper' + } + }} + > + {selectedEntry && ( + + + + Entry #{selectedEntry._entryNumber} Details + + Entry ID: {selectedEntry.id} + + + + + + + + + + + + + + + + + + + + + + {selectedEntry.input || '—'} + + + + + + + + {selectedEntry.expected_output || '—'} + + + + + + + {selectedEntry.context && selectedEntry.context.length > 0 ? ( + selectedEntry.context.map((contextItem, index) => ( + + {contextItem} + + )) + ) : ( + + )} + + + + + )} + ) } diff --git a/studio-frontend/packages/ui/src/views/evaluation/DatasetTab.jsx b/studio-frontend/packages/ui/src/views/evaluation/DatasetTab.jsx index 4621f31..000c316 100644 --- a/studio-frontend/packages/ui/src/views/evaluation/DatasetTab.jsx +++ b/studio-frontend/packages/ui/src/views/evaluation/DatasetTab.jsx @@ -5,6 +5,7 @@ import PropTypes from 'prop-types' import { Box, Button, + Checkbox, Chip, CircularProgress, Dialog, @@ -46,7 +47,7 @@ import ModelSelect from './ModelSelect' import { StyledButton } from '@/ui-component/button/StyledButton' const DATASET_UPLOAD_TYPES = ['.json', '.jsonl'] -const SYNTHESIZE_UPLOAD_TYPES = ['.pdf', '.txt', '.docx'] +const SYNTHESIZE_UPLOAD_TYPES = ['.pdf', '.docx'] const DEFAULT_SYNTHESIS_OPTIONS = { targetGoldens: 10, @@ -234,7 +235,7 @@ const NewDatasetModal = ({ open, onClose, onCreated, existingDatasetNames }) => onCreated && onCreated(res.data) handleClose() } catch (err) { - setError(err?.response?.data?.message || 'Failed to create dataset.') + setError(err?.response?.data?.message || err?.response?.data?.detail || 'Failed to create dataset.') } finally { setSubmitting(false) } @@ -255,6 +256,7 @@ const NewDatasetModal = ({ open, onClose, onCreated, existingDatasetNames }) => acceptedTypes={DATASET_UPLOAD_TYPES} maxSizeMB={25} error={error === missingUploadError ? error : null} + invalidTypeMessage='Only JSON (.json) and JSONL (.jsonl) files are supported for dataset import.' title='Drop your dataset file here or click to browse' subtitle={`Accepted types: ${getAcceptedFileLabel(DATASET_UPLOAD_TYPES)}. JSON can be an array of entries or an object with an entries array.`} buttonLabel={file ? 'Replace File' : 'Choose File'} @@ -272,7 +274,7 @@ const NewDatasetModal = ({ open, onClose, onCreated, existingDatasetNames }) => size='small' fullWidth multiline - rows={2} + rows={1} value={description} onChange={(e) => setDescription(e.target.value)} /> @@ -316,6 +318,7 @@ const SynthesizeModal = ({ open, onClose, onCreated, existingDatasetNames }) => const [modelName, setModelName] = useState('') const [sourceFile, setSourceFile] = useState(null) const [options, setOptions] = useState(DEFAULT_SYNTHESIS_OPTIONS) + const [totalGoldensEnabled, setTotalGoldensEnabled] = useState(true) const [showAdvanced, setShowAdvanced] = useState(false) const [loadingOptions, setLoadingOptions] = useState(false) const [submitting, setSubmitting] = useState(false) @@ -342,6 +345,7 @@ const SynthesizeModal = ({ open, onClose, onCreated, existingDatasetNames }) => setModelName('') setSourceFile(null) setOptions(DEFAULT_SYNTHESIS_OPTIONS) + setTotalGoldensEnabled(true) setShowAdvanced(false) setError('') onClose() @@ -383,6 +387,12 @@ const SynthesizeModal = ({ open, onClose, onCreated, existingDatasetNames }) => return } + const parsedTargetGoldens = Number(options.targetGoldens) + if (totalGoldensEnabled && (!Number.isInteger(parsedTargetGoldens) || parsedTargetGoldens < 1 || parsedTargetGoldens > 200)) { + setError('Total Goldens must be an integer between 1 and 200.') + return + } + setSubmitting(true) setError('') try { @@ -391,7 +401,7 @@ const SynthesizeModal = ({ open, onClose, onCreated, existingDatasetNames }) => formData.append('name', datasetName.trim()) formData.append('model_name', modelName) formData.append('embed_model_name', 'nomic-embed-text') - formData.append('num_goldens', String(options.targetGoldens)) + if (totalGoldensEnabled) formData.append('num_goldens', String(parsedTargetGoldens)) formData.append('max_goldens_per_document', String(options.maxGoldensPerDocument)) formData.append('max_contexts', String(options.maxContexts)) formData.append('min_contexts', String(options.minContexts)) @@ -410,7 +420,7 @@ const SynthesizeModal = ({ open, onClose, onCreated, existingDatasetNames }) => onCreated && onCreated(res.data) handleClose() } catch (err) { - setError(err?.response?.data?.message || 'Failed to start synthesis.') + setError(err?.response?.data?.message || err?.response?.data?.detail || 'Failed to start synthesis.') } finally { setSubmitting(false) } @@ -436,6 +446,7 @@ const SynthesizeModal = ({ open, onClose, onCreated, existingDatasetNames }) => acceptedTypes={SYNTHESIZE_UPLOAD_TYPES} maxSizeMB={50} error={error === missingSrcError ? error : null} + invalidTypeMessage='Only PDF and Word (.docx) files are supported for dataset synthesis.' title='Drop your source document here or click to browse' subtitle={`Accepted types: ${getAcceptedFileLabel(SYNTHESIZE_UPLOAD_TYPES)}. Word uploads require .docx format.`} buttonLabel={sourceFile ? 'Replace File' : 'Choose File'} @@ -453,7 +464,7 @@ const SynthesizeModal = ({ open, onClose, onCreated, existingDatasetNames }) => size='small' fullWidth multiline - rows={2} + rows={1} value={description} onChange={(e) => setDescription(e.target.value)} /> @@ -465,16 +476,36 @@ const SynthesizeModal = ({ open, onClose, onCreated, existingDatasetNames }) => disabled={submitting} label='Generator Model' /> - updateOption('targetGoldens', Number(e.target.value))} - helperText='The synthesized dataset will contain exactly this many goldens.' - /> + + setTotalGoldensEnabled(e.target.checked)} + size='small' + /> + } + label='Set Total Goldens' + /> + { + const nextValue = e.target.value + updateOption('targetGoldens', nextValue === '' ? '' : Number(nextValue)) + }} + disabled={!totalGoldensEnabled} + helperText={ + totalGoldensEnabled + ? 'The synthesized dataset will contain exactly this many goldens.' + : 'Goldens will be up to Max Contexts × Max Goldens Per Document (see Advanced Settings), reduced by quality filtering.' + } + /> + - - + handleLanguageChange('en')} selected={selectedLanguage === 'en'}> + English + + handleLanguageChange('zh')} selected={selectedLanguage === 'zh'}> + 中文 + + keycloak.logout({ redirectUri: window.location.origin })} From 0bda88e9c8b22a68247708580298059866db2964 Mon Sep 17 00:00:00 2001 From: wwanarif Date: Fri, 15 May 2026 02:37:33 +0000 Subject: [PATCH 9/9] fix finetuning playwright Signed-off-by: wwanarif --- .../playwright/studio-e2e/005_test_finetuning_rerank.spec.ts | 5 +++-- .../studio-e2e/006_test_finetuning_embedding.spec.ts | 5 +++-- .../studio-e2e/007_test_finetuning_reasoning.spec.ts | 5 +++-- 3 files changed, 9 insertions(+), 6 deletions(-) diff --git a/tests/playwright/studio-e2e/005_test_finetuning_rerank.spec.ts b/tests/playwright/studio-e2e/005_test_finetuning_rerank.spec.ts index 9f934ad..ed112a5 100644 --- a/tests/playwright/studio-e2e/005_test_finetuning_rerank.spec.ts +++ b/tests/playwright/studio-e2e/005_test_finetuning_rerank.spec.ts @@ -62,7 +62,8 @@ test('005_test_finetuning_rerank', async ({ browser, baseURL }) => { await expect(page.locator('div').filter({ hasText: 'Fine-tuning JobsCreate New' }).nth(3)).toContainText('rerank'); await waitForStatusText(page, 'td.MuiTableCell-root div.MuiChip-root', 'succeeded', 20, 60000); - await page.locator('button').nth(5).click(); + await page.locator('tbody tr').first().getByRole('button').nth(2).click(); await page.getByRole('menuitem', { name: 'Delete Job' }).click(); - await page.getByRole('button', { name: 'Delete', exact: true }).click(); + await page.getByRole('button', { name: 'Delete' }).click(); + await page.waitForTimeout(5000); }); \ No newline at end of file diff --git a/tests/playwright/studio-e2e/006_test_finetuning_embedding.spec.ts b/tests/playwright/studio-e2e/006_test_finetuning_embedding.spec.ts index 7886c38..891c5c0 100644 --- a/tests/playwright/studio-e2e/006_test_finetuning_embedding.spec.ts +++ b/tests/playwright/studio-e2e/006_test_finetuning_embedding.spec.ts @@ -62,7 +62,8 @@ test('006_test_finetuning_embedding', async ({ browser, baseURL }) => { await expect(page.locator('div').filter({ hasText: 'Fine-tuning JobsCreate New' }).nth(3)).toContainText('embedding'); await waitForStatusText(page, 'td.MuiTableCell-root div.MuiChip-root', 'succeeded', 20, 60000); - await page.locator('button').nth(5).click(); + await page.locator('tbody tr').first().getByRole('button').nth(2).click(); await page.getByRole('menuitem', { name: 'Delete Job' }).click(); - await page.getByRole('button', { name: 'Delete', exact: true }).click(); + await page.getByRole('button', { name: 'Delete' }).click(); + await page.waitForTimeout(5000); }); \ No newline at end of file diff --git a/tests/playwright/studio-e2e/007_test_finetuning_reasoning.spec.ts b/tests/playwright/studio-e2e/007_test_finetuning_reasoning.spec.ts index 72472ba..414dd64 100644 --- a/tests/playwright/studio-e2e/007_test_finetuning_reasoning.spec.ts +++ b/tests/playwright/studio-e2e/007_test_finetuning_reasoning.spec.ts @@ -62,7 +62,8 @@ test('007_test_finetuning_reasoning', async ({ browser, baseURL }) => { await expect(page.locator('div').filter({ hasText: 'Fine-tuning JobsCreate New' }).nth(3)).toContainText('reasoning'); await waitForStatusText(page, 'td.MuiTableCell-root div.MuiChip-root', 'succeeded', 20, 60000); - await page.locator('button').nth(5).click(); + await page.locator('tbody tr').first().getByRole('button').nth(2).click(); await page.getByRole('menuitem', { name: 'Delete Job' }).click(); - await page.getByRole('button', { name: 'Delete', exact: true }).click(); + await page.getByRole('button', { name: 'Delete' }).click(); + await page.waitForTimeout(5000); }); \ No newline at end of file