diff --git a/.dockerignore b/.dockerignore index 0d22350d..491f3727 100644 --- a/.dockerignore +++ b/.dockerignore @@ -3,11 +3,14 @@ .gitattributes dive-dsa-slicer/ scripts/ +!server/scripts/build_release_assets.sh testutils/ # Documentation docs/ *.md README* +!server/README.md +!test_deployment/README.md # Development files .vscode/ @@ -25,6 +28,7 @@ __pycache__/ build/ develop-eggs/ dist/ +!server/dist/ downloads/ eggs/ .eggs/ @@ -87,10 +91,13 @@ Thumbs.db /tmp/ /var/tmp/ -# Docker +# Docker (compose/Dockerfiles are read from the host; keep entrypoints in context) Dockerfile* +!test_deployment/Dockerfile docker-compose*.yml +!test_deployment/docker-compose.yml .dockerignore +!test_deployment/entrypoint_*.sh # CI/CD .github/ diff --git a/.env.Default b/.env.Default index 8d2fb221..a6cb411a 100644 --- a/.env.Default +++ b/.env.Default @@ -1,2 +1,52 @@ -# Add in your userId:groupId for docker slicer cli plugin access -DSA_USER="userId:groupId" \ No newline at end of file +# DIVE-DSA Configuration Template +# Create a copy of this file called .env + +# Docker Compose Stack configuration +# +#COMPOSE_PROJECT_NAME=dive-dsa +#COMPOSE_PROFILES=gpu + +# DSA user for slicer CLI docker socket access (required) +DSA_USER="userId:groupId" + +# MongoDB Configuration +# +#MONGO_DB_PATH=/opt/dive/mongo/data/db + +# Web server configuration +# +#GIRDER_ADMIN_USER=admin +#GIRDER_ADMIN_PASS=letmein + +# Girder Worker / Celery (Girder 5 — replaces CELERY_BROKER_URL and WORKER_API_URL) +# +#RABBITMQ_DEFAULT_USER=guest +#RABBITMQ_DEFAULT_PASS=guest +#RABBITMQ_DEFAULT_VHOST=default +#GIRDER_WORKER_BROKER=amqp://guest:guest@rabbit/default +#GIRDER_WORKER_BACKEND=rpc://guest:guest@localhost/ +#GIRDER_SETTING_WORKER_API_URL=http://girder:8080/api/v1 +#GIRDER_NOTIFICATION_REDIS_URL=redis://redis:6379 + +# Docker image tag +#TAG=latest + +# RabbitMQ User Queue configs +# +#RABBITMQ_MANAGEMENT_USERNAME=guest +#RABBITMQ_MANAGEMENT_PASSWORD=guest +#RABBITMQ_MANAGEMENT_VHOST=default +#RABBITMQ_MANAGEMENT_URL="http://rabbit:15672/" +#RABBITMQ_MANAGEMENT_BROKER_URL_TEMPLATE="amqp://{}:{}@rabbit/default" + +# Production data bind paths +# +#PUBLIC_DATA_PATH=./docker/girder_data + +# Worker Concurrency +# +#DEFAULT_WORKER_CONCURRENCY=3 + +# Split or standalone worker: remote Girder API (replaces legacy WORKER_API_URL) +#GIRDER_SETTING_WORKER_API_URL=https://your-server.example.com/api/v1 +#SOCK_PATH=/var/run/docker.sock diff --git a/.github/workflows/CI.yml b/.github/workflows/CI.yml index d4ee4035..7b98cd54 100644 --- a/.github/workflows/CI.yml +++ b/.github/workflows/CI.yml @@ -69,3 +69,34 @@ jobs: - name: Run tests run: tox -e testunit working-directory: server + + build-wheel: + name: Build dive_server wheel + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - uses: actions/setup-node@v4 + with: + node-version: '24' + cache: npm + cache-dependency-path: | + client/package-lock.json + server/dive_server/web_client/package-lock.json + + - uses: astral-sh/setup-uv@v6 + with: + enable-cache: true + cache-dependency-glob: server/uv.lock + + - name: Build and verify wheel + run: bash server/scripts/build_wheel.sh + + - name: Upload wheel artifact + uses: actions/upload-artifact@v4 + with: + name: dive-dsa-wheel + path: server/dist/dive_dsa-*.whl + if-no-files-found: error diff --git a/.github/workflows/release-dive-server.yml b/.github/workflows/release-dive-server.yml new file mode 100644 index 00000000..59763146 --- /dev/null +++ b/.github/workflows/release-dive-server.yml @@ -0,0 +1,41 @@ +name: PyPI Publish dive-dsa +on: + workflow_dispatch: + release: + types: [published] + +permissions: + id-token: write # Required for PyPI trusted publishing (OIDC) + contents: read + actions: write # Upload wheel artifact + +jobs: + publish: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v6 + with: + fetch-depth: 0 + # Prefer the release tag so uv-dynamic-versioning yields a clean PEP 440 version. + ref: ${{ github.event.release.tag_name || github.ref }} + + - uses: actions/setup-node@v6 + with: + node-version: '24' + package-manager-cache: false + + - uses: astral-sh/setup-uv@v6 + with: + enable-cache: false + + - name: Build, verify, and publish wheel + # build_wheel.sh refuses versions with +local (PyPI), cleans dist/, then uv publish. + # OIDC trusted publishing provides credentials in GITHUB_ACTIONS; no token needed. + run: bash server/scripts/build_wheel.sh --publish + + - name: Upload wheel artifact + uses: actions/upload-artifact@v4 + with: + name: dive-dsa-wheel + path: server/dist/dive_dsa-*.whl + if-no-files-found: error diff --git a/.gitignore b/.gitignore index 4c49bd78..c967b3cb 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,3 @@ .env +# Synced at runtime in development containers +server/dive_server/dive_client/ \ No newline at end of file diff --git a/client/.env.production b/client/.env.production index c835fe83..3f0405d6 100644 --- a/client/.env.production +++ b/client/.env.production @@ -1 +1 @@ -VUE_APP_STATIC_PATH=./static/dive/ +VUE_APP_STATIC_PATH=/dive/static diff --git a/client/dive-common/components/Viewer.vue b/client/dive-common/components/Viewer.vue index a4002f32..48a51e64 100644 --- a/client/dive-common/components/Viewer.vue +++ b/client/dive-common/components/Viewer.vue @@ -842,6 +842,12 @@ export default defineComponent({ description: 'Slicer Tasks', component: SlicerTaskRunnerVue, }); + } else { + context.register({ + description: 'Slicer Tasks', + component: SlicerTaskRunnerVue, + width: 500, + }); } if (!configurationManager.getUISetting('UIGroupManager')) { diff --git a/client/dive-common/use/useRequest.ts b/client/dive-common/use/useRequest.ts index d947cd9e..505a2a73 100644 --- a/client/dive-common/use/useRequest.ts +++ b/client/dive-common/use/useRequest.ts @@ -1,5 +1,5 @@ import { reactive, shallowRef, toRefs } from 'vue'; -import { AxiosResponse } from '@girder/components/node_modules/axios'; +import { AxiosResponse } from 'axios'; import { getResponseError } from 'vue-media-annotator/utils'; export default function useRequest() { diff --git a/client/dive-common/vue-utilities/prompt-service/index.ts b/client/dive-common/vue-utilities/prompt-service/index.ts index 003b34c4..bdcef911 100644 --- a/client/dive-common/vue-utilities/prompt-service/index.ts +++ b/client/dive-common/vue-utilities/prompt-service/index.ts @@ -65,14 +65,41 @@ class PromptService { positiveButton = 'Confirm', negativeButton = 'Cancel', confirm = false, + maxWidth = '400px', }: PromptParams): Promise { return new Promise((resolve) => { if (!this.component.show) { - this.set(title, text, positiveButton, negativeButton, confirm, resolve); + this.set( + title, + text, + positiveButton, + negativeButton, + confirm, + resolve, + undefined, + undefined, + undefined, + undefined, + undefined, + maxWidth, + ); } else { const unwatch = watch(this.component.show, () => { unwatch(); - this.set(title, text, positiveButton, negativeButton, confirm, resolve); + this.set( + title, + text, + positiveButton, + negativeButton, + confirm, + resolve, + undefined, + undefined, + undefined, + undefined, + undefined, + maxWidth, + ); }); } }); diff --git a/client/package-lock.json b/client/package-lock.json index 87e451e6..bbf2ab24 100644 --- a/client/package-lock.json +++ b/client/package-lock.json @@ -1,19 +1,19 @@ { "name": "dive-dsa", - "version": "1.11.34", + "version": "1.11.35", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "dive-dsa", - "version": "1.11.34", + "version": "1.11.35", "license": "Apache-2.0", "dependencies": { "@flatten-js/interval-tree": "^1.0.11", - "@girder/components": "^3.2.0", + "@girder/components": "github:girder/girder_web_components#girder-5-axios-upgrade", "@mdi/font": "^6.2.95", "@mdi/js": "^7.4.47", - "axios": "^1.6.7", + "axios": "^1.17.0", "color-hash": "^1.0.3", "core-js": "^3.22.2", "csv-stringify": "^5.6.0", @@ -34,7 +34,6 @@ "vuex": "^3.0.1" }, "devDependencies": { - "@types/axios": "^0.14.0", "@types/body-parser": "^1.19.0", "@types/color-hash": "^1.0.0", "@types/cors": "^2.8.9", @@ -104,7 +103,7 @@ "tsc-alias": "^1.2.0", "tslib": "^2.6.2", "typescript": "~4.3.5", - "vite": "^6.3.5", + "vite": "^6.4.3", "vue-cli-plugin-vuetify": "^2.4.3", "vue-jest": "^3.0.7", "vue-template-compiler": "^2.7.0", @@ -146,6 +145,7 @@ "version": "2.3.0", "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.3.0.tgz", "integrity": "sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==", + "devOptional": true, "license": "Apache-2.0", "dependencies": { "@jridgewell/gen-mapping": "^0.3.5", @@ -159,6 +159,7 @@ "version": "7.29.0", "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.29.0.tgz", "integrity": "sha512-9NhCeYjq9+3uxgdtp20LSiJXJvN0FeCtNGpJxuMFZ1Kv3cWUNb6DOhJwUvcVCzKGR66cw4njwM6hrJLqgOwbcw==", + "devOptional": true, "license": "MIT", "dependencies": { "@babel/helper-validator-identifier": "^7.28.5", @@ -173,6 +174,7 @@ "version": "7.28.0", "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.28.0.tgz", "integrity": "sha512-60X7qkglvrap8mn1lh2ebxXdZYtUcpd7gsmy9kLaBJ4i/WdY8PqTSdxyA8qraikqKQK5C1KRBKXqznrVapyNaw==", + "devOptional": true, "license": "MIT", "engines": { "node": ">=6.9.0" @@ -182,6 +184,7 @@ "version": "7.28.0", "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.28.0.tgz", "integrity": "sha512-UlLAnTPrFdNGoFtbSXwcGFQBtQZJCNjaN6hQNP3UPvuNXT1i82N26KL3dZeIpNalWywr9IuQuncaAfUaS1g6sQ==", + "devOptional": true, "license": "MIT", "dependencies": { "@ampproject/remapping": "^2.2.0", @@ -212,12 +215,14 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", + "devOptional": true, "license": "MIT" }, "node_modules/@babel/core/node_modules/json5": { "version": "2.2.3", "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", + "devOptional": true, "license": "MIT", "bin": { "json5": "lib/cli.js" @@ -230,6 +235,7 @@ "version": "6.3.1", "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "devOptional": true, "license": "ISC", "bin": { "semver": "bin/semver.js" @@ -239,6 +245,7 @@ "version": "7.29.1", "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.29.1.tgz", "integrity": "sha512-qsaF+9Qcm2Qv8SRIMMscAvG4O3lJ0F1GuMo5HR/Bp02LopNgnZBC/EkbevHFeGs4ls/oPz9v+Bsmzbkbe+0dUw==", + "devOptional": true, "license": "MIT", "dependencies": { "@babel/parser": "^7.29.0", @@ -255,6 +262,7 @@ "version": "3.1.0", "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz", "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==", + "devOptional": true, "license": "MIT", "bin": { "jsesc": "bin/jsesc" @@ -267,6 +275,7 @@ "version": "7.27.3", "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.27.3.tgz", "integrity": "sha512-fXSwMQqitTGeHLBC08Eq5yXz2m37E4pJX1qAU1+2cNedz/ifv/bVXft90VeSav5nFO61EcNgwr0aJxbyPaWBPg==", + "devOptional": true, "license": "MIT", "dependencies": { "@babel/types": "^7.27.3" @@ -279,6 +288,7 @@ "version": "7.27.2", "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.27.2.tgz", "integrity": "sha512-2+1thGUUWWjLTYTHZWK1n8Yga0ijBz1XAhUXcKy81rd5g6yh7hGqMp45v7cadSbEHc9G3OTv45SyneRN3ps4DQ==", + "devOptional": true, "license": "MIT", "dependencies": { "@babel/compat-data": "^7.27.2", @@ -295,6 +305,7 @@ "version": "5.1.1", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", + "devOptional": true, "license": "ISC", "dependencies": { "yallist": "^3.0.2" @@ -304,6 +315,7 @@ "version": "6.3.1", "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "devOptional": true, "license": "ISC", "bin": { "semver": "bin/semver.js" @@ -313,12 +325,14 @@ "version": "3.1.1", "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", + "devOptional": true, "license": "ISC" }, "node_modules/@babel/helper-create-class-features-plugin": { "version": "7.27.1", "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.27.1.tgz", "integrity": "sha512-QwGAmuvM17btKU5VqXfb+Giw4JcN0hjuufz3DYnpeVDvZLAObloM77bhMXiqry3Iio+Ai4phVRDwl6WU10+r5A==", + "devOptional": true, "license": "MIT", "dependencies": { "@babel/helper-annotate-as-pure": "^7.27.1", @@ -340,6 +354,7 @@ "version": "6.3.1", "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "devOptional": true, "license": "ISC", "bin": { "semver": "bin/semver.js" @@ -349,6 +364,7 @@ "version": "7.27.1", "resolved": "https://registry.npmjs.org/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.27.1.tgz", "integrity": "sha512-uVDC72XVf8UbrH5qQTc18Agb8emwjTiZrQE11Nv3CuBEZmVvTwwE9CBUEvHku06gQCAyYf8Nv6ja1IN+6LMbxQ==", + "devOptional": true, "license": "MIT", "dependencies": { "@babel/helper-annotate-as-pure": "^7.27.1", @@ -366,6 +382,7 @@ "version": "6.3.1", "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "devOptional": true, "license": "ISC", "bin": { "semver": "bin/semver.js" @@ -375,6 +392,7 @@ "version": "0.6.5", "resolved": "https://registry.npmjs.org/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.6.5.tgz", "integrity": "sha512-uJnGFcPsWQK8fvjgGP5LZUZZsYGIoPeRjSF5PGwrelYgq7Q15/Ft9NGFp1zglwgIv//W0uG4BevRuSJRyylZPg==", + "devOptional": true, "license": "MIT", "dependencies": { "@babel/helper-compilation-targets": "^7.27.2", @@ -391,6 +409,7 @@ "version": "7.28.0", "resolved": "https://registry.npmjs.org/@babel/helper-globals/-/helper-globals-7.28.0.tgz", "integrity": "sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw==", + "devOptional": true, "license": "MIT", "engines": { "node": ">=6.9.0" @@ -400,6 +419,7 @@ "version": "7.27.1", "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.27.1.tgz", "integrity": "sha512-E5chM8eWjTp/aNoVpcbfM7mLxu9XGLWYise2eBKGQomAk/Mb4XoxyqXTZbuTohbsl8EKqdlMhnDI2CCLfcs9wA==", + "devOptional": true, "license": "MIT", "dependencies": { "@babel/traverse": "^7.27.1", @@ -413,6 +433,7 @@ "version": "7.28.6", "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.28.6.tgz", "integrity": "sha512-l5XkZK7r7wa9LucGw9LwZyyCUscb4x37JWTPz7swwFE/0FMQAGpiWUZn8u9DzkSBWEcK25jmvubfpw2dnAMdbw==", + "devOptional": true, "license": "MIT", "dependencies": { "@babel/traverse": "^7.28.6", @@ -426,6 +447,7 @@ "version": "7.28.6", "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.28.6.tgz", "integrity": "sha512-67oXFAYr2cDLDVGLXTEABjdBJZ6drElUSI7WKp70NrpyISso3plG9SAGEF6y7zbha/wOzUByWWTJvEDVNIUGcA==", + "devOptional": true, "license": "MIT", "dependencies": { "@babel/helper-module-imports": "^7.28.6", @@ -443,6 +465,7 @@ "version": "7.27.1", "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.27.1.tgz", "integrity": "sha512-URMGH08NzYFhubNSGJrpUEphGKQwMQYBySzat5cAByY1/YgIRkULnIy3tAMeszlL/so2HbeilYloUmSpd7GdVw==", + "devOptional": true, "license": "MIT", "dependencies": { "@babel/types": "^7.27.1" @@ -455,6 +478,7 @@ "version": "7.28.6", "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.28.6.tgz", "integrity": "sha512-S9gzZ/bz83GRysI7gAD4wPT/AI3uCnY+9xn+Mx/KPs2JwHJIz1W8PZkg2cqyt3RNOBM8ejcXhV6y8Og7ly/Dug==", + "devOptional": true, "license": "MIT", "engines": { "node": ">=6.9.0" @@ -464,6 +488,7 @@ "version": "7.27.1", "resolved": "https://registry.npmjs.org/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.27.1.tgz", "integrity": "sha512-7fiA521aVw8lSPeI4ZOD3vRFkoqkJcS+z4hFo82bFSH/2tNd6eJ5qCVMS5OzDmZh/kaHQeBaeyxK6wljcPtveA==", + "devOptional": true, "license": "MIT", "dependencies": { "@babel/helper-annotate-as-pure": "^7.27.1", @@ -481,6 +506,7 @@ "version": "7.27.1", "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.27.1.tgz", "integrity": "sha512-7EHz6qDZc8RYS5ElPoShMheWvEgERonFCs7IAonWLLUTXW59DP14bCZt89/GKyreYn8g3S83m21FelHKbeDCKA==", + "devOptional": true, "license": "MIT", "dependencies": { "@babel/helper-member-expression-to-functions": "^7.27.1", @@ -498,6 +524,7 @@ "version": "7.27.1", "resolved": "https://registry.npmjs.org/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.27.1.tgz", "integrity": "sha512-Tub4ZKEXqbPjXgWLl2+3JpQAYBJ8+ikpQ2Ocj/q/r0LwE3UhENh7EUabyHjz2kCEsrRY83ew2DQdHluuiDQFzg==", + "devOptional": true, "license": "MIT", "dependencies": { "@babel/traverse": "^7.27.1", @@ -529,6 +556,7 @@ "version": "7.27.1", "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.27.1.tgz", "integrity": "sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==", + "devOptional": true, "license": "MIT", "engines": { "node": ">=6.9.0" @@ -538,6 +566,7 @@ "version": "7.27.1", "resolved": "https://registry.npmjs.org/@babel/helper-wrap-function/-/helper-wrap-function-7.27.1.tgz", "integrity": "sha512-NFJK2sHUvrjo8wAU/nQTWU890/zB2jj0qBcCbZbbf+005cAsv6tMjXz31fBign6M5ov1o0Bllu+9nbqkfsjjJQ==", + "devOptional": true, "license": "MIT", "dependencies": { "@babel/template": "^7.27.1", @@ -552,6 +581,7 @@ "version": "7.28.2", "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.28.2.tgz", "integrity": "sha512-/V9771t+EgXz62aCcyofnQhGM8DQACbRhvzKFsXKC9QM+5MadF8ZmIm0crDMaz3+o0h0zXfJnd4EhbYbxsrcFw==", + "devOptional": true, "license": "MIT", "dependencies": { "@babel/template": "^7.27.2", @@ -580,6 +610,7 @@ "version": "7.27.1", "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-firefox-class-in-computed-class-key/-/plugin-bugfix-firefox-class-in-computed-class-key-7.27.1.tgz", "integrity": "sha512-QPG3C9cCVRQLxAVwmefEmwdTanECuUBMQZ/ym5kiw3XKCGA7qkuQLcjWWHcrD/GKbn/WmJwaezfuuAOcyKlRPA==", + "devOptional": true, "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.27.1", @@ -596,6 +627,7 @@ "version": "7.27.1", "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-safari-class-field-initializer-scope/-/plugin-bugfix-safari-class-field-initializer-scope-7.27.1.tgz", "integrity": "sha512-qNeq3bCKnGgLkEXUuFry6dPlGfCdQNZbn7yUAPCInwAJHMU7THJfrBSozkcWq5sNM6RcF3S8XyQL2A52KNR9IA==", + "devOptional": true, "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" @@ -611,6 +643,7 @@ "version": "7.27.1", "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression/-/plugin-bugfix-safari-id-destructuring-collision-in-function-expression-7.27.1.tgz", "integrity": "sha512-g4L7OYun04N1WyqMNjldFwlfPCLVkgB54A/YCXICZYBsvJJE3kByKv9c9+R/nAfmIfjl2rKYLNyMHboYbZaWaA==", + "devOptional": true, "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" @@ -626,6 +659,7 @@ "version": "7.27.1", "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/-/plugin-bugfix-v8-spread-parameters-in-optional-chaining-7.27.1.tgz", "integrity": "sha512-oO02gcONcD5O1iTLi/6frMJBIwWEHceWGSGqrpCmEL8nogiS6J9PBlE48CaK20/Jx1LuRml9aDftLgdjXT8+Cw==", + "devOptional": true, "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.27.1", @@ -643,6 +677,7 @@ "version": "7.27.1", "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly/-/plugin-bugfix-v8-static-class-fields-redefine-readonly-7.27.1.tgz", "integrity": "sha512-6BpaYGDavZqkI6yT+KSPdpZFfpnd68UKXbcjI9pJ13pvHhPrCKWOOLp+ysvMeA+DxnhuPpgIaRpxRxo5A9t5jw==", + "devOptional": true, "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.27.1", @@ -659,6 +694,7 @@ "version": "7.18.6", "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-class-properties/-/plugin-proposal-class-properties-7.18.6.tgz", "integrity": "sha512-cumfXOF0+nzZrrN8Rf0t7M+tF6sZc7vhQwYQck9q1/5w2OExlD+b4v4RpMJFaV1Z7WcDRgO6FqvxqxGlwo+RHQ==", + "dev": true, "license": "MIT", "dependencies": { "@babel/helper-create-class-features-plugin": "^7.18.6", @@ -675,6 +711,7 @@ "version": "7.28.0", "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-decorators/-/plugin-proposal-decorators-7.28.0.tgz", "integrity": "sha512-zOiZqvANjWDUaUS9xMxbMcK/Zccztbe/6ikvUXaG9nsPH3w6qh5UaPGAnirI/WhIbZ8m3OHU0ReyPrknG+ZKeg==", + "dev": true, "license": "MIT", "dependencies": { "@babel/helper-create-class-features-plugin": "^7.27.1", @@ -692,6 +729,7 @@ "version": "7.21.0-placeholder-for-preset-env.2", "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-private-property-in-object/-/plugin-proposal-private-property-in-object-7.21.0-placeholder-for-preset-env.2.tgz", "integrity": "sha512-SOSkfJDddaM7mak6cPEpswyTRnuRltl429hMraQEglW+OkovnCzsiszTmsrlY//qLFjCpQDFRvjdm2wA5pPm9w==", + "devOptional": true, "license": "MIT", "engines": { "node": ">=6.9.0" @@ -759,6 +797,7 @@ "version": "7.27.1", "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-decorators/-/plugin-syntax-decorators-7.27.1.tgz", "integrity": "sha512-YMq8Z87Lhl8EGkmb0MwYkt36QnxC+fzCgrl66ereamPlYToRpIk5nUjKUY3QKLWq8mwUB1BgbeXcTJhZOCDg5A==", + "dev": true, "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" @@ -774,6 +813,7 @@ "version": "7.8.3", "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-dynamic-import/-/plugin-syntax-dynamic-import-7.8.3.tgz", "integrity": "sha512-5gdGbFon+PszYzqs83S3E5mpi7/y/8M9eC90MRTZfduQOYW76ig6SOSPNe41IG5LoP3FGBn2N0RjVDSQiS94kQ==", + "dev": true, "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.8.0" @@ -786,6 +826,7 @@ "version": "7.27.1", "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-assertions/-/plugin-syntax-import-assertions-7.27.1.tgz", "integrity": "sha512-UT/Jrhw57xg4ILHLFnzFpPDlMbcdEicaAtjPQpbj9wa8T4r5KVWCimHcL/460g8Ht0DMxDyjsLgiWSkVjnwPFg==", + "devOptional": true, "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" @@ -801,6 +842,7 @@ "version": "7.27.1", "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-attributes/-/plugin-syntax-import-attributes-7.27.1.tgz", "integrity": "sha512-oFT0FrKHgF53f4vOsZGi2Hh3I35PfSmVs4IBFLFj4dnafP+hIWDLg3VyKmUHfLoLHlyxY4C7DGtmHuJgn+IGww==", + "devOptional": true, "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" @@ -842,6 +884,7 @@ "version": "7.27.1", "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.27.1.tgz", "integrity": "sha512-y8YTNIeKoyhGd9O0Jiyzyyqk8gdjnumGTQPsz0xOZOQ2RmkVJeZ1vmmfIvFEKqucBG6axJGBZDE/7iI5suUI/w==", + "dev": true, "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" @@ -983,6 +1026,7 @@ "version": "7.18.6", "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-unicode-sets-regex/-/plugin-syntax-unicode-sets-regex-7.18.6.tgz", "integrity": "sha512-727YkEAPwSIQTv5im8QHz3upqp92JTWhidIC81Tdx4VJYIte/VndKf1qKrfnnhPLiPghStWfvC/iFaMCQu7Nqg==", + "devOptional": true, "license": "MIT", "dependencies": { "@babel/helper-create-regexp-features-plugin": "^7.18.6", @@ -999,6 +1043,7 @@ "version": "7.27.1", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.27.1.tgz", "integrity": "sha512-8Z4TGic6xW70FKThA5HYEKKyBpOOsucTOD1DjU3fZxDg+K3zBJcXMFnt/4yQiZnf5+MiOMSXQ9PaEK/Ilh1DeA==", + "devOptional": true, "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" @@ -1014,6 +1059,7 @@ "version": "7.28.0", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-generator-functions/-/plugin-transform-async-generator-functions-7.28.0.tgz", "integrity": "sha512-BEOdvX4+M765icNPZeidyADIvQ1m1gmunXufXxvRESy/jNNyfovIqUyE7MVgGBjWktCoJlzvFA1To2O4ymIO3Q==", + "devOptional": true, "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.27.1", @@ -1031,6 +1077,7 @@ "version": "7.27.1", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.27.1.tgz", "integrity": "sha512-NREkZsZVJS4xmTr8qzE5y8AfIPqsdQfRuUiLRTEzb7Qii8iFWCyDKaUV2c0rCuh4ljDZ98ALHP/PetiBV2nddA==", + "devOptional": true, "license": "MIT", "dependencies": { "@babel/helper-module-imports": "^7.27.1", @@ -1048,6 +1095,7 @@ "version": "7.27.1", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.27.1.tgz", "integrity": "sha512-cnqkuOtZLapWYZUYM5rVIdv1nXYuFVIltZ6ZJ7nIj585QsjKM5dhL2Fu/lICXZ1OyIAFc7Qy+bvDAtTXqGrlhg==", + "devOptional": true, "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" @@ -1063,6 +1111,7 @@ "version": "7.28.0", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.28.0.tgz", "integrity": "sha512-gKKnwjpdx5sER/wl0WN0efUBFzF/56YZO0RJrSYP4CljXnP31ByY7fol89AzomdlLNzI36AvOTmYHsnZTCkq8Q==", + "devOptional": true, "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" @@ -1078,6 +1127,7 @@ "version": "7.27.1", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-class-properties/-/plugin-transform-class-properties-7.27.1.tgz", "integrity": "sha512-D0VcalChDMtuRvJIu3U/fwWjf8ZMykz5iZsg77Nuj821vCKI3zCyRLwRdWbsuJ/uRwZhZ002QtCqIkwC/ZkvbA==", + "devOptional": true, "license": "MIT", "dependencies": { "@babel/helper-create-class-features-plugin": "^7.27.1", @@ -1094,6 +1144,7 @@ "version": "7.27.1", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-class-static-block/-/plugin-transform-class-static-block-7.27.1.tgz", "integrity": "sha512-s734HmYU78MVzZ++joYM+NkJusItbdRcbm+AGRgJCt3iA+yux0QpD9cBVdz3tKyrjVYWRl7j0mHSmv4lhV0aoA==", + "devOptional": true, "license": "MIT", "dependencies": { "@babel/helper-create-class-features-plugin": "^7.27.1", @@ -1110,6 +1161,7 @@ "version": "7.28.0", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.28.0.tgz", "integrity": "sha512-IjM1IoJNw72AZFlj33Cu8X0q2XK/6AaVC3jQu+cgQ5lThWD5ajnuUAml80dqRmOhmPkTH8uAwnpMu9Rvj0LTRA==", + "devOptional": true, "license": "MIT", "dependencies": { "@babel/helper-annotate-as-pure": "^7.27.3", @@ -1130,6 +1182,7 @@ "version": "7.27.1", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.27.1.tgz", "integrity": "sha512-lj9PGWvMTVksbWiDT2tW68zGS/cyo4AkZ/QTp0sQT0mjPopCmrSkzxeXkznjqBxzDI6TclZhOJbBmbBLjuOZUw==", + "devOptional": true, "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.27.1", @@ -1146,6 +1199,7 @@ "version": "7.28.0", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.28.0.tgz", "integrity": "sha512-v1nrSMBiKcodhsyJ4Gf+Z0U/yawmJDBOTpEB3mcQY52r9RIyPneGyAS/yM6seP/8I+mWI3elOMtT5dB8GJVs+A==", + "devOptional": true, "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.27.1", @@ -1162,6 +1216,7 @@ "version": "7.27.1", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.27.1.tgz", "integrity": "sha512-gEbkDVGRvjj7+T1ivxrfgygpT7GUd4vmODtYpbs0gZATdkX8/iSnOtZSxiZnsgm1YjTgjI6VKBGSJJevkrclzw==", + "devOptional": true, "license": "MIT", "dependencies": { "@babel/helper-create-regexp-features-plugin": "^7.27.1", @@ -1178,6 +1233,7 @@ "version": "7.27.1", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.27.1.tgz", "integrity": "sha512-MTyJk98sHvSs+cvZ4nOauwTTG1JeonDjSGvGGUNHreGQns+Mpt6WX/dVzWBHgg+dYZhkC4X+zTDfkTU+Vy9y7Q==", + "devOptional": true, "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" @@ -1193,6 +1249,7 @@ "version": "7.27.1", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-duplicate-named-capturing-groups-regex/-/plugin-transform-duplicate-named-capturing-groups-regex-7.27.1.tgz", "integrity": "sha512-hkGcueTEzuhB30B3eJCbCYeCaaEQOmQR0AdvzpD4LoN0GXMWzzGSuRrxR2xTnCrvNbVwK9N6/jQ92GSLfiZWoQ==", + "devOptional": true, "license": "MIT", "dependencies": { "@babel/helper-create-regexp-features-plugin": "^7.27.1", @@ -1209,6 +1266,7 @@ "version": "7.27.1", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dynamic-import/-/plugin-transform-dynamic-import-7.27.1.tgz", "integrity": "sha512-MHzkWQcEmjzzVW9j2q8LGjwGWpG2mjwaaB0BNQwst3FIjqsg8Ct/mIZlvSPJvfi9y2AC8mi/ktxbFVL9pZ1I4A==", + "devOptional": true, "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" @@ -1224,6 +1282,7 @@ "version": "7.28.0", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-explicit-resource-management/-/plugin-transform-explicit-resource-management-7.28.0.tgz", "integrity": "sha512-K8nhUcn3f6iB+P3gwCv/no7OdzOZQcKchW6N389V6PD8NUWKZHzndOd9sPDVbMoBsbmjMqlB4L9fm+fEFNVlwQ==", + "devOptional": true, "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.27.1", @@ -1240,6 +1299,7 @@ "version": "7.27.1", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.27.1.tgz", "integrity": "sha512-uspvXnhHvGKf2r4VVtBpeFnuDWsJLQ6MF6lGJLC89jBR1uoVeqM416AZtTuhTezOfgHicpJQmoD5YUakO/YmXQ==", + "devOptional": true, "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" @@ -1255,6 +1315,7 @@ "version": "7.27.1", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-export-namespace-from/-/plugin-transform-export-namespace-from-7.27.1.tgz", "integrity": "sha512-tQvHWSZ3/jH2xuq/vZDy0jNn+ZdXJeM8gHvX4lnJmsc3+50yPlWdZXIc5ay+umX+2/tJIqHqiEqcJvxlmIvRvQ==", + "devOptional": true, "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" @@ -1270,6 +1331,7 @@ "version": "7.27.1", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.27.1.tgz", "integrity": "sha512-BfbWFFEJFQzLCQ5N8VocnCtA8J1CLkNTe2Ms2wocj75dd6VpiqS5Z5quTYcUoo4Yq+DN0rtikODccuv7RU81sw==", + "devOptional": true, "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.27.1", @@ -1286,6 +1348,7 @@ "version": "7.27.1", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.27.1.tgz", "integrity": "sha512-1bQeydJF9Nr1eBCMMbC+hdwmRlsv5XYOMu03YSWFwNs0HsAmtSxxF1fyuYPqemVldVyFmlCU7w8UE14LupUSZQ==", + "devOptional": true, "license": "MIT", "dependencies": { "@babel/helper-compilation-targets": "^7.27.1", @@ -1303,6 +1366,7 @@ "version": "7.27.1", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-json-strings/-/plugin-transform-json-strings-7.27.1.tgz", "integrity": "sha512-6WVLVJiTjqcQauBhn1LkICsR2H+zm62I3h9faTDKt1qP4jn2o72tSvqMwtGFKGTpojce0gJs+76eZ2uCHRZh0Q==", + "devOptional": true, "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" @@ -1318,6 +1382,7 @@ "version": "7.27.1", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-literals/-/plugin-transform-literals-7.27.1.tgz", "integrity": "sha512-0HCFSepIpLTkLcsi86GG3mTUzxV5jpmbv97hTETW3yzrAij8aqlD36toB1D0daVFJM8NK6GvKO0gslVQmm+zZA==", + "devOptional": true, "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" @@ -1333,6 +1398,7 @@ "version": "7.27.1", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-logical-assignment-operators/-/plugin-transform-logical-assignment-operators-7.27.1.tgz", "integrity": "sha512-SJvDs5dXxiae4FbSL1aBJlG4wvl594N6YEVVn9e3JGulwioy6z3oPjx/sQBO3Y4NwUu5HNix6KJ3wBZoewcdbw==", + "devOptional": true, "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" @@ -1348,6 +1414,7 @@ "version": "7.27.1", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.27.1.tgz", "integrity": "sha512-hqoBX4dcZ1I33jCSWcXrP+1Ku7kdqXf1oeah7ooKOIiAdKQ+uqftgCFNOSzA5AMS2XIHEYeGFg4cKRCdpxzVOQ==", + "devOptional": true, "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" @@ -1363,6 +1430,7 @@ "version": "7.27.1", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.27.1.tgz", "integrity": "sha512-iCsytMg/N9/oFq6n+gFTvUYDZQOMK5kEdeYxmxt91fcJGycfxVP9CnrxoliM0oumFERba2i8ZtwRUCMhvP1LnA==", + "devOptional": true, "license": "MIT", "dependencies": { "@babel/helper-module-transforms": "^7.27.1", @@ -1379,6 +1447,7 @@ "version": "7.27.1", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.27.1.tgz", "integrity": "sha512-OJguuwlTYlN0gBZFRPqwOGNWssZjfIUdS7HMYtN8c1KmwpwHFBwTeFZrg9XZa+DFTitWOW5iTAG7tyCUPsCCyw==", + "devOptional": true, "license": "MIT", "dependencies": { "@babel/helper-module-transforms": "^7.27.1", @@ -1395,6 +1464,7 @@ "version": "7.29.4", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.29.4.tgz", "integrity": "sha512-N7QmZ0xRZfjHOfZeQLJjwgX2zS9pdGHSVl/cjSGlo4dXMqvurfxXDMKY4RqEKzPozV78VMcd0lxyG13mlbKc4w==", + "devOptional": true, "license": "MIT", "dependencies": { "@babel/helper-module-transforms": "^7.28.6", @@ -1413,6 +1483,7 @@ "version": "7.27.1", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.27.1.tgz", "integrity": "sha512-iQBE/xC5BV1OxJbp6WG7jq9IWiD+xxlZhLrdwpPkTX3ydmXdvoCpyfJN7acaIBZaOqTfr76pgzqBJflNbeRK+w==", + "devOptional": true, "license": "MIT", "dependencies": { "@babel/helper-module-transforms": "^7.27.1", @@ -1429,6 +1500,7 @@ "version": "7.27.1", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.27.1.tgz", "integrity": "sha512-SstR5JYy8ddZvD6MhV0tM/j16Qds4mIpJTOd1Yu9J9pJjH93bxHECF7pgtc28XvkzTD6Pxcm/0Z73Hvk7kb3Ng==", + "devOptional": true, "license": "MIT", "dependencies": { "@babel/helper-create-regexp-features-plugin": "^7.27.1", @@ -1445,6 +1517,7 @@ "version": "7.27.1", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.27.1.tgz", "integrity": "sha512-f6PiYeqXQ05lYq3TIfIDu/MtliKUbNwkGApPUvyo6+tc7uaR4cPjPe7DFPr15Uyycg2lZU6btZ575CuQoYh7MQ==", + "devOptional": true, "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" @@ -1460,6 +1533,7 @@ "version": "7.27.1", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-nullish-coalescing-operator/-/plugin-transform-nullish-coalescing-operator-7.27.1.tgz", "integrity": "sha512-aGZh6xMo6q9vq1JGcw58lZ1Z0+i0xB2x0XaauNIUXd6O1xXc3RwoWEBlsTQrY4KQ9Jf0s5rgD6SiNkaUdJegTA==", + "devOptional": true, "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" @@ -1475,6 +1549,7 @@ "version": "7.27.1", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-numeric-separator/-/plugin-transform-numeric-separator-7.27.1.tgz", "integrity": "sha512-fdPKAcujuvEChxDBJ5c+0BTaS6revLV7CJL08e4m3de8qJfNIuCc2nc7XJYOjBoTMJeqSmwXJ0ypE14RCjLwaw==", + "devOptional": true, "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" @@ -1490,6 +1565,7 @@ "version": "7.28.0", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-rest-spread/-/plugin-transform-object-rest-spread-7.28.0.tgz", "integrity": "sha512-9VNGikXxzu5eCiQjdE4IZn8sb9q7Xsk5EXLDBKUYg1e/Tve8/05+KJEtcxGxAgCY5t/BpKQM+JEL/yT4tvgiUA==", + "devOptional": true, "license": "MIT", "dependencies": { "@babel/helper-compilation-targets": "^7.27.2", @@ -1509,6 +1585,7 @@ "version": "7.27.1", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.27.1.tgz", "integrity": "sha512-SFy8S9plRPbIcxlJ8A6mT/CxFdJx/c04JEctz4jf8YZaVS2px34j7NXRrlGlHkN/M2gnpL37ZpGRGVFLd3l8Ng==", + "devOptional": true, "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.27.1", @@ -1525,6 +1602,7 @@ "version": "7.27.1", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-optional-catch-binding/-/plugin-transform-optional-catch-binding-7.27.1.tgz", "integrity": "sha512-txEAEKzYrHEX4xSZN4kJ+OfKXFVSWKB2ZxM9dpcE3wT7smwkNmXo5ORRlVzMVdJbD+Q8ILTgSD7959uj+3Dm3Q==", + "devOptional": true, "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" @@ -1540,6 +1618,7 @@ "version": "7.27.1", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-optional-chaining/-/plugin-transform-optional-chaining-7.27.1.tgz", "integrity": "sha512-BQmKPPIuc8EkZgNKsv0X4bPmOoayeu4F1YCwx2/CfmDSXDbp7GnzlUH+/ul5VGfRg1AoFPsrIThlEBj2xb4CAg==", + "devOptional": true, "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.27.1", @@ -1556,6 +1635,7 @@ "version": "7.27.7", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.27.7.tgz", "integrity": "sha512-qBkYTYCb76RRxUM6CcZA5KRu8K4SM8ajzVeUgVdMVO9NN9uI/GaVmBg/WKJJGnNokV9SY8FxNOVWGXzqzUidBg==", + "devOptional": true, "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" @@ -1571,6 +1651,7 @@ "version": "7.27.1", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-private-methods/-/plugin-transform-private-methods-7.27.1.tgz", "integrity": "sha512-10FVt+X55AjRAYI9BrdISN9/AQWHqldOeZDUoLyif1Kn05a56xVBXb8ZouL8pZ9jem8QpXaOt8TS7RHUIS+GPA==", + "devOptional": true, "license": "MIT", "dependencies": { "@babel/helper-create-class-features-plugin": "^7.27.1", @@ -1587,6 +1668,7 @@ "version": "7.27.1", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-private-property-in-object/-/plugin-transform-private-property-in-object-7.27.1.tgz", "integrity": "sha512-5J+IhqTi1XPa0DXF83jYOaARrX+41gOewWbkPyjMNRDqgOCqdffGh8L3f/Ek5utaEBZExjSAzcyjmV9SSAWObQ==", + "devOptional": true, "license": "MIT", "dependencies": { "@babel/helper-annotate-as-pure": "^7.27.1", @@ -1604,6 +1686,7 @@ "version": "7.27.1", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.27.1.tgz", "integrity": "sha512-oThy3BCuCha8kDZ8ZkgOg2exvPYUlprMukKQXI1r1pJ47NCvxfkEy8vK+r/hT9nF0Aa4H1WUPZZjHTFtAhGfmQ==", + "devOptional": true, "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" @@ -1619,6 +1702,7 @@ "version": "7.28.1", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.28.1.tgz", "integrity": "sha512-P0QiV/taaa3kXpLY+sXla5zec4E+4t4Aqc9ggHlfZ7a2cp8/x/Gv08jfwEtn9gnnYIMvHx6aoOZ8XJL8eU71Dg==", + "devOptional": true, "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" @@ -1634,6 +1718,7 @@ "version": "7.27.1", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regexp-modifiers/-/plugin-transform-regexp-modifiers-7.27.1.tgz", "integrity": "sha512-TtEciroaiODtXvLZv4rmfMhkCv8jx3wgKpL68PuiPh2M4fvz5jhsA7697N1gMvkvr/JTF13DrFYyEbY9U7cVPA==", + "devOptional": true, "license": "MIT", "dependencies": { "@babel/helper-create-regexp-features-plugin": "^7.27.1", @@ -1650,6 +1735,7 @@ "version": "7.27.1", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.27.1.tgz", "integrity": "sha512-V2ABPHIJX4kC7HegLkYoDpfg9PVmuWy/i6vUM5eGK22bx4YVFD3M5F0QQnWQoDs6AGsUWTVOopBiMFQgHaSkVw==", + "devOptional": true, "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" @@ -1665,6 +1751,7 @@ "version": "7.28.0", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.28.0.tgz", "integrity": "sha512-dGopk9nZrtCs2+nfIem25UuHyt5moSJamArzIoh9/vezUQPmYDOzjaHDCkAzuGJibCIkPup8rMT2+wYB6S73cA==", + "dev": true, "license": "MIT", "dependencies": { "@babel/helper-module-imports": "^7.27.1", @@ -1685,6 +1772,7 @@ "version": "6.3.1", "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, "license": "ISC", "bin": { "semver": "bin/semver.js" @@ -1694,6 +1782,7 @@ "version": "7.27.1", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.27.1.tgz", "integrity": "sha512-N/wH1vcn4oYawbJ13Y/FxcQrWk63jhfNa7jef0ih7PHSIHX2LB7GWE1rkPrOnka9kwMxb6hMl19p7lidA+EHmQ==", + "devOptional": true, "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" @@ -1709,6 +1798,7 @@ "version": "7.27.1", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-spread/-/plugin-transform-spread-7.27.1.tgz", "integrity": "sha512-kpb3HUqaILBJcRFVhFUs6Trdd4mkrzcGXss+6/mxUd273PfbWqSDHRzMT2234gIg2QYfAjvXLSquP1xECSg09Q==", + "devOptional": true, "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.27.1", @@ -1725,6 +1815,7 @@ "version": "7.27.1", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.27.1.tgz", "integrity": "sha512-lhInBO5bi/Kowe2/aLdBAawijx+q1pQzicSgnkB6dUPc1+RC8QmJHKf2OjvU+NZWitguJHEaEmbV6VWEouT58g==", + "devOptional": true, "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" @@ -1740,6 +1831,7 @@ "version": "7.27.1", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.27.1.tgz", "integrity": "sha512-fBJKiV7F2DxZUkg5EtHKXQdbsbURW3DZKQUWphDum0uRP6eHGGa/He9mc0mypL680pb+e/lDIthRohlv8NCHkg==", + "devOptional": true, "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" @@ -1755,6 +1847,7 @@ "version": "7.27.1", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.27.1.tgz", "integrity": "sha512-RiSILC+nRJM7FY5srIyc4/fGIwUhyDuuBSdWn4y6yT6gm652DpCHZjIipgn6B7MQ1ITOUnAKWixEUjQRIBIcLw==", + "devOptional": true, "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" @@ -1770,6 +1863,7 @@ "version": "7.27.1", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.27.1.tgz", "integrity": "sha512-Ysg4v6AmF26k9vpfFuTZg8HRfVWzsh1kVfowA23y9j/Gu6dOuahdUVhkLqpObp3JIv27MLSii6noRnuKN8H0Mg==", + "devOptional": true, "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" @@ -1785,6 +1879,7 @@ "version": "7.27.1", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-property-regex/-/plugin-transform-unicode-property-regex-7.27.1.tgz", "integrity": "sha512-uW20S39PnaTImxp39O5qFlHLS9LJEmANjMG7SxIhap8rCHqu0Ik+tLEPX5DKmHn6CsWQ7j3lix2tFOa5YtL12Q==", + "devOptional": true, "license": "MIT", "dependencies": { "@babel/helper-create-regexp-features-plugin": "^7.27.1", @@ -1801,6 +1896,7 @@ "version": "7.27.1", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.27.1.tgz", "integrity": "sha512-xvINq24TRojDuyt6JGtHmkVkrfVV3FPT16uytxImLeBZqW3/H52yN+kM1MGuyPkIQxrzKwPHs5U/MP3qKyzkGw==", + "devOptional": true, "license": "MIT", "dependencies": { "@babel/helper-create-regexp-features-plugin": "^7.27.1", @@ -1817,6 +1913,7 @@ "version": "7.27.1", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-sets-regex/-/plugin-transform-unicode-sets-regex-7.27.1.tgz", "integrity": "sha512-EtkOujbc4cgvb0mlpQefi4NTPBzhSIevblFevACNLUspmrALgmEBdL/XfnyyITfd8fKBZrZys92zOWcik7j9Tw==", + "devOptional": true, "license": "MIT", "dependencies": { "@babel/helper-create-regexp-features-plugin": "^7.27.1", @@ -1833,6 +1930,7 @@ "version": "7.28.0", "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.28.0.tgz", "integrity": "sha512-VmaxeGOwuDqzLl5JUkIRM1X2Qu2uKGxHEQWh+cvvbl7JuJRgKGJSfsEF/bUaxFhJl/XAyxBe7q7qSuTbKFuCyg==", + "devOptional": true, "license": "MIT", "dependencies": { "@babel/compat-data": "^7.28.0", @@ -1917,6 +2015,7 @@ "version": "6.3.1", "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "devOptional": true, "license": "ISC", "bin": { "semver": "bin/semver.js" @@ -1926,6 +2025,7 @@ "version": "0.1.6-no-external-plugins", "resolved": "https://registry.npmjs.org/@babel/preset-modules/-/preset-modules-0.1.6-no-external-plugins.tgz", "integrity": "sha512-HrcgcIESLm9aIR842yhJ5RWan/gebQUJ6E/E5+rf0y9o6oj7w0Br+sWuL6kEQ/o/AdfvR1Je9jG18/gnpwjEyA==", + "devOptional": true, "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.0.0", @@ -1960,6 +2060,7 @@ "version": "7.28.6", "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.28.6.tgz", "integrity": "sha512-YA6Ma2KsCdGb+WC6UpBVFJGXL58MDA6oyONbjyF/+5sBgxY/dwkhLogbMT2GXXyU84/IhRw/2D1Os1B/giz+BQ==", + "devOptional": true, "license": "MIT", "dependencies": { "@babel/code-frame": "^7.28.6", @@ -1974,6 +2075,7 @@ "version": "7.29.0", "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.29.0.tgz", "integrity": "sha512-4HPiQr0X7+waHfyXPZpWPfWL/J7dcN1mx9gL6WdQVMbPnF3+ZhSMs8tCxN7oHddJE9fhNE7+lxdnlyemKfJRuA==", + "devOptional": true, "license": "MIT", "dependencies": { "@babel/code-frame": "^7.29.0", @@ -2556,22 +2658,19 @@ "license": "MIT" }, "node_modules/@girder/components": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/@girder/components/-/components-3.3.0.tgz", - "integrity": "sha512-CMK8jIWP0lcDex7WSx3yfUOjsQ1gR8Aj5hxqqr6YIN1TMuL8YHMvkM8CFZDQj6WBoXbVASwpq9j0RI4TPXKzfA==", + "version": "3.2.0", + "resolved": "git+ssh://git@github.com/girder/girder_web_components.git#cdd764ce58a63941269062382074ac44b7dae616", "license": "Apache-2.0", "dependencies": { "@mdi/font": "^5.3.45", - "@vue/babel-preset-app": "^5.0.8", - "axios": "^0.21.2", + "axios": "^1.17.0", "js-cookie": "^2.2.0", "markdown-it": "^12.3.2", "moment": "^2.29.4", - "postcss": "^8.5.6", "qs": "^6.9.4", - "vue": "^2.7.16", + "vue": "^2.6.12", "vue-async-computed": "^3.4.1", - "vuetify": "^2.7.2" + "vuetify": "^2.3.1" } }, "node_modules/@girder/components/node_modules/@mdi/font": { @@ -2580,15 +2679,6 @@ "integrity": "sha512-jswRF6q3eq8NWpWiqct6q+6Fg/I7nUhrxYJfiEM8JJpap0wVJLQdbKtyS65GdlK7S7Ytnx3TTi/bmw+tBhkGmg==", "license": "Apache-2.0" }, - "node_modules/@girder/components/node_modules/axios": { - "version": "0.21.4", - "resolved": "https://registry.npmjs.org/axios/-/axios-0.21.4.tgz", - "integrity": "sha512-ut5vewkiu8jjGBdqpM44XxjuCjq9LAKeHVmoVfHVzy8eHgxxq8SbAVQNovDA8mVi05kP0Ea/n/UzcSHcTJQfNg==", - "license": "MIT", - "dependencies": { - "follow-redirects": "^1.14.0" - } - }, "node_modules/@girder/components/node_modules/js-cookie": { "version": "2.2.1", "resolved": "https://registry.npmjs.org/js-cookie/-/js-cookie-2.2.1.tgz", @@ -3126,6 +3216,7 @@ "version": "0.3.12", "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.12.tgz", "integrity": "sha512-OuLGC46TjB5BbN1dH8JULVVZY4WTdkF7tV9Ys6wLL1rubZnCMstOhNHueU5bLCrnRuDhKPDM4g6sw4Bel5Gzqg==", + "devOptional": true, "license": "MIT", "dependencies": { "@jridgewell/sourcemap-codec": "^1.5.0", @@ -3136,6 +3227,7 @@ "version": "3.1.2", "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", + "devOptional": true, "license": "MIT", "engines": { "node": ">=6.0.0" @@ -3156,12 +3248,14 @@ "version": "1.5.4", "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.4.tgz", "integrity": "sha512-VT2+G1VQs/9oz078bLrYbecdZKs912zQlkelYpuf+SXF+QvZDYJlbx/LSx+meSAwdDFnF8FVXW92AVjjkVmgFw==", + "devOptional": true, "license": "MIT" }, "node_modules/@jridgewell/trace-mapping": { "version": "0.3.29", "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.29.tgz", "integrity": "sha512-uw6guiW/gcAGPDhLmd77/6lW8QLeiV5RUTsAX46Db6oLhGaVj4lhnPwb184s1bkc8kdVg/+h988dro8GRDpmYQ==", + "devOptional": true, "license": "MIT", "dependencies": { "@jridgewell/resolve-uri": "^3.1.0", @@ -4152,17 +4246,6 @@ "node": ">= 6" } }, - "node_modules/@types/axios": { - "version": "0.14.4", - "resolved": "https://registry.npmjs.org/@types/axios/-/axios-0.14.4.tgz", - "integrity": "sha512-9JgOaunvQdsQ/qW2OPmE5+hCeUB52lQSolecrFrthct55QekhmXEwT203s20RL+UHtCQc15y3VXpby9E7Kkh/g==", - "deprecated": "This is a stub types definition. axios provides its own type definitions, so you do not need this installed.", - "dev": true, - "license": "MIT", - "dependencies": { - "axios": "*" - } - }, "node_modules/@types/babel__core": { "version": "7.20.5", "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz", @@ -5483,18 +5566,21 @@ "version": "1.4.0", "resolved": "https://registry.npmjs.org/@vue/babel-helper-vue-jsx-merge-props/-/babel-helper-vue-jsx-merge-props-1.4.0.tgz", "integrity": "sha512-JkqXfCkUDp4PIlFdDQ0TdXoIejMtTHP67/pvxlgeY+u5k3LEdKuWZ3LK6xkxo52uDoABIVyRwqVkfLQJhk7VBA==", + "dev": true, "license": "MIT" }, "node_modules/@vue/babel-helper-vue-transform-on": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/@vue/babel-helper-vue-transform-on/-/babel-helper-vue-transform-on-1.4.0.tgz", "integrity": "sha512-mCokbouEQ/ocRce/FpKCRItGo+013tHg7tixg3DUNS+6bmIchPt66012kBMm476vyEIJPafrvOf4E5OYj3shSw==", + "dev": true, "license": "MIT" }, "node_modules/@vue/babel-plugin-jsx": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/@vue/babel-plugin-jsx/-/babel-plugin-jsx-1.4.0.tgz", "integrity": "sha512-9zAHmwgMWlaN6qRKdrg1uKsBKHvnUU+Py+MOCTuYZBoZsopa90Di10QRjB+YPnVss0BZbG/H5XFwJY1fTxJWhA==", + "dev": true, "license": "MIT", "dependencies": { "@babel/helper-module-imports": "^7.25.9", @@ -5520,6 +5606,7 @@ "version": "1.4.0", "resolved": "https://registry.npmjs.org/@vue/babel-plugin-resolve-type/-/babel-plugin-resolve-type-1.4.0.tgz", "integrity": "sha512-4xqDRRbQQEWHQyjlYSgZsWj44KfiF6D+ktCuXyZ8EnVDYV3pztmXJDf1HveAjUAXxAnR8daCQT51RneWWxtTyQ==", + "dev": true, "license": "MIT", "dependencies": { "@babel/code-frame": "^7.26.2", @@ -5539,6 +5626,7 @@ "version": "1.4.0", "resolved": "https://registry.npmjs.org/@vue/babel-plugin-transform-vue-jsx/-/babel-plugin-transform-vue-jsx-1.4.0.tgz", "integrity": "sha512-Fmastxw4MMx0vlgLS4XBX0XiBbUFzoMGeVXuMV08wyOfXdikAFqBTuYPR0tlk+XskL19EzHc39SgjrPGY23JnA==", + "dev": true, "license": "MIT", "dependencies": { "@babel/helper-module-imports": "^7.0.0", @@ -5556,6 +5644,7 @@ "version": "5.0.9", "resolved": "https://registry.npmjs.org/@vue/babel-preset-app/-/babel-preset-app-5.0.9.tgz", "integrity": "sha512-0rKOF4s/AhaRMJLybxOCgXfwtYhO3pwDSL/q/W8wRs1LzmHAc77FyTXWlun6VyKiSKwSdtH7CvOiWqq+DfofdA==", + "dev": true, "license": "MIT", "dependencies": { "@babel/core": "^7.12.16", @@ -5593,6 +5682,7 @@ "version": "7.28.2", "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.28.2.tgz", "integrity": "sha512-KHp2IflsnGywDjBWDkR9iEqiWSpc8GIi0lgTT3mOElT0PP1tG26P4tmFI2YvAdzgq9RGyoHZQEIEdZy6Ec5xCA==", + "dev": true, "license": "MIT", "engines": { "node": ">=6.9.0" @@ -5602,6 +5692,7 @@ "version": "1.4.0", "resolved": "https://registry.npmjs.org/@vue/babel-preset-jsx/-/babel-preset-jsx-1.4.0.tgz", "integrity": "sha512-QmfRpssBOPZWL5xw7fOuHNifCQcNQC1PrOo/4fu6xlhlKJJKSA3HqX92Nvgyx8fqHZTUGMPHmFA+IDqwXlqkSA==", + "dev": true, "license": "MIT", "dependencies": { "@vue/babel-helper-vue-jsx-merge-props": "^1.4.0", @@ -5627,6 +5718,7 @@ "version": "1.4.0", "resolved": "https://registry.npmjs.org/@vue/babel-sugar-composition-api-inject-h/-/babel-sugar-composition-api-inject-h-1.4.0.tgz", "integrity": "sha512-VQq6zEddJHctnG4w3TfmlVp5FzDavUSut/DwR0xVoe/mJKXyMcsIibL42wPntozITEoY90aBV0/1d2KjxHU52g==", + "dev": true, "license": "MIT", "dependencies": { "@babel/plugin-syntax-jsx": "^7.2.0" @@ -5639,6 +5731,7 @@ "version": "1.4.0", "resolved": "https://registry.npmjs.org/@vue/babel-sugar-composition-api-render-instance/-/babel-sugar-composition-api-render-instance-1.4.0.tgz", "integrity": "sha512-6ZDAzcxvy7VcnCjNdHJ59mwK02ZFuP5CnucloidqlZwVQv5CQLijc3lGpR7MD3TWFi78J7+a8J56YxbCtHgT9Q==", + "dev": true, "license": "MIT", "dependencies": { "@babel/plugin-syntax-jsx": "^7.2.0" @@ -5651,6 +5744,7 @@ "version": "1.4.0", "resolved": "https://registry.npmjs.org/@vue/babel-sugar-functional-vue/-/babel-sugar-functional-vue-1.4.0.tgz", "integrity": "sha512-lTEB4WUFNzYt2In6JsoF9sAYVTo84wC4e+PoZWSgM6FUtqRJz7wMylaEhSRgG71YF+wfLD6cc9nqVeXN2rwBvw==", + "dev": true, "license": "MIT", "dependencies": { "@babel/plugin-syntax-jsx": "^7.2.0" @@ -5663,6 +5757,7 @@ "version": "1.4.0", "resolved": "https://registry.npmjs.org/@vue/babel-sugar-inject-h/-/babel-sugar-inject-h-1.4.0.tgz", "integrity": "sha512-muwWrPKli77uO2fFM7eA3G1lAGnERuSz2NgAxuOLzrsTlQl8W4G+wwbM4nB6iewlKbwKRae3nL03UaF5ffAPMA==", + "dev": true, "license": "MIT", "dependencies": { "@babel/plugin-syntax-jsx": "^7.2.0" @@ -5675,6 +5770,7 @@ "version": "1.4.0", "resolved": "https://registry.npmjs.org/@vue/babel-sugar-v-model/-/babel-sugar-v-model-1.4.0.tgz", "integrity": "sha512-0t4HGgXb7WHYLBciZzN5s0Hzqan4Ue+p/3FdQdcaHAb7s5D9WZFGoSxEZHrR1TFVZlAPu1bejTKGeAzaaG3NCQ==", + "dev": true, "license": "MIT", "dependencies": { "@babel/plugin-syntax-jsx": "^7.2.0", @@ -5692,6 +5788,7 @@ "version": "1.4.0", "resolved": "https://registry.npmjs.org/@vue/babel-sugar-v-on/-/babel-sugar-v-on-1.4.0.tgz", "integrity": "sha512-m+zud4wKLzSKgQrWwhqRObWzmTuyzl6vOP7024lrpeJM4x2UhQtRDLgYjXAw9xBXjCwS0pP9kXjg91F9ZNo9JA==", + "dev": true, "license": "MIT", "dependencies": { "@babel/plugin-syntax-jsx": "^7.2.0", @@ -6042,6 +6139,7 @@ "version": "3.5.18", "resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.5.18.tgz", "integrity": "sha512-3slwjQrrV1TO8MoXgy3aynDQ7lslj5UqDxuHnrzHtpON5CBinhWjJETciPngpin/T3OuW3tXUf86tEurusnztw==", + "dev": true, "license": "MIT", "dependencies": { "@babel/parser": "^7.28.0", @@ -6055,6 +6153,7 @@ "version": "4.5.0", "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==", + "dev": true, "license": "BSD-2-Clause", "engines": { "node": ">=0.12" @@ -6067,6 +6166,7 @@ "version": "3.5.18", "resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.5.18.tgz", "integrity": "sha512-RMbU6NTU70++B1JyVJbNbeFkK+A+Q7y9XKE2EM4NLGm2WFR8x9MbAtWxPPLdm0wUkuZv9trpwfSlL6tjdIa1+A==", + "dev": true, "license": "MIT", "dependencies": { "@vue/compiler-core": "3.5.18", @@ -6077,6 +6177,7 @@ "version": "3.5.18", "resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.5.18.tgz", "integrity": "sha512-5aBjvGqsWs+MoxswZPoTB9nSDb3dhd1x30xrrltKujlCxo48j8HGDNj3QPhF4VIS0VQDUrA1xUfp2hEa+FNyXA==", + "dev": true, "license": "MIT", "dependencies": { "@babel/parser": "^7.28.0", @@ -6094,6 +6195,7 @@ "version": "3.5.18", "resolved": "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.5.18.tgz", "integrity": "sha512-xM16Ak7rSWHkM3m22NlmcdIM+K4BMyFARAfV9hYFl+SFuRzrZ3uGMNW05kA5pmeMa0X9X963Kgou7ufdbpOP9g==", + "dev": true, "license": "MIT", "dependencies": { "@vue/compiler-dom": "3.5.18", @@ -6195,6 +6297,7 @@ "version": "3.5.18", "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.5.18.tgz", "integrity": "sha512-cZy8Dq+uuIXbxCZpuLd2GJdeSO/lIzIspC2WtkqIpje5QyFbvLaI5wZtdUjLHjGZrlVX6GilejatWwVYYRc8tA==", + "dev": true, "license": "MIT" }, "node_modules/@vue/test-utils": { @@ -7119,28 +7222,28 @@ } }, "node_modules/axios": { - "version": "1.18.1", - "resolved": "https://registry.npmjs.org/axios/-/axios-1.18.1.tgz", - "integrity": "sha512-3nTvFlvpn9Zu/RkHUqtc7/+al4UpRW5az71ap5zccp6e8RAYEzhMTecX8Dz1wWDYrPpUoB1HAQEGEAEvUr7S9g==", + "version": "1.19.0", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.19.0.tgz", + "integrity": "sha512-ht/iuYZXEjFxLH/Hkezgd7m6JKlHHXEUSneaDz8uZe1Gj5QZtCnpyDsckvAiEnT89OEbCLmnte4R4sn7P0EKFw==", "license": "MIT", "dependencies": { "follow-redirects": "^1.16.0", - "form-data": "^4.0.5", + "form-data": "^4.0.6", "https-proxy-agent": "^5.0.1", "proxy-from-env": "^2.1.0" } }, "node_modules/axios/node_modules/form-data": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.5.tgz", - "integrity": "sha512-8RipRLol37bNs2bhoV67fiTEvdTrbMUYcFTiy3+wuuOnUog2QBHCZWXDRijWQfAkhBj2Uf5UnVaiWwA5vdd82w==", + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.6.tgz", + "integrity": "sha512-vKatAh4SlVfgbv+YtmhiRjhEMJsYpsG1Y2rMQtR+SVSbytsSD1YGzDIcrAJmdFec88u/+VoGmxnl+80gL1tRCQ==", "license": "MIT", "dependencies": { "asynckit": "^0.4.0", "combined-stream": "^1.0.8", "es-set-tostringtag": "^2.1.0", - "hasown": "^2.0.2", - "mime-types": "^2.1.12" + "hasown": "^2.0.4", + "mime-types": "^2.1.35" }, "engines": { "node": ">= 6" @@ -7455,6 +7558,7 @@ "version": "2.3.3", "resolved": "https://registry.npmjs.org/babel-plugin-dynamic-import-node/-/babel-plugin-dynamic-import-node-2.3.3.tgz", "integrity": "sha512-jZVI+s9Zg3IqA/kdi0i6UDCybUI3aSBLnglhYbSSjKlV7yF1F/5LWv8MakQmvYpnbJDS6fcBL2KzHSxNCMtWSQ==", + "dev": true, "license": "MIT", "dependencies": { "object.assign": "^4.1.0" @@ -7497,6 +7601,7 @@ "version": "0.4.14", "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.4.14.tgz", "integrity": "sha512-Co2Y9wX854ts6U8gAAPXfn0GmAyctHuK8n0Yhfjd6t30g7yvKjspvvOo9yG+z52PZRgFErt7Ka2pYnXCjLKEpg==", + "devOptional": true, "license": "MIT", "dependencies": { "@babel/compat-data": "^7.27.7", @@ -7511,6 +7616,7 @@ "version": "6.3.1", "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "devOptional": true, "license": "ISC", "bin": { "semver": "bin/semver.js" @@ -7520,6 +7626,7 @@ "version": "0.13.0", "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.13.0.tgz", "integrity": "sha512-U+GNwMdSFgzVmfhNm8GJUX88AadB3uo9KpJqS3FaqNIPKgySuvMb+bHPsOmmuWyIcuqZj/pzt1RUIUZns4y2+A==", + "devOptional": true, "license": "MIT", "dependencies": { "@babel/helper-define-polyfill-provider": "^0.6.5", @@ -7533,6 +7640,7 @@ "version": "0.6.5", "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.6.5.tgz", "integrity": "sha512-ISqQ2frbiNU9vIJkzg7dlPpznPZ4jOiUQ1uSmB0fEHeowtN3COYRsXr/xexn64NpU13P06jc/L5TgiJXOgrbEg==", + "devOptional": true, "license": "MIT", "dependencies": { "@babel/helper-define-polyfill-provider": "^0.6.5" @@ -7785,6 +7893,7 @@ "version": "2.10.10", "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.10.10.tgz", "integrity": "sha512-sUoJ3IMxx4AyRqO4MLeHlnGDkyXRoUG0/AI9fjK+vS72ekpV0yWVY7O0BVjmBcRtkNcsAO2QDZ4tdKKGoI6YaQ==", + "devOptional": true, "license": "Apache-2.0", "bin": { "baseline-browser-mapping": "dist/cli.cjs" @@ -7986,6 +8095,7 @@ "version": "4.28.1", "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.28.1.tgz", "integrity": "sha512-ZC5Bd0LgJXgwGqUknZY/vkUQ04r8NXnJZ3yYi4vDmSiZmC/pdSN0NbNRPxZpbtO4uAfDUAFffO8IZoM3Gj8IkA==", + "devOptional": true, "funding": [ { "type": "opencollective", @@ -8083,6 +8193,7 @@ "version": "1.0.8", "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.8.tgz", "integrity": "sha512-oKlSFMcMwpUg2ednkhQ454wfWiU/ul3CkJe/PEHcTKuiX6RpbehUiFMXu13HalGZxfUwCQzZG747YXBn1im9ww==", + "dev": true, "license": "MIT", "dependencies": { "call-bind-apply-helpers": "^1.0.0", @@ -8160,6 +8271,7 @@ "version": "5.3.1", "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", + "dev": true, "license": "MIT", "engines": { "node": ">=6" @@ -8192,6 +8304,7 @@ "version": "1.0.30001781", "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001781.tgz", "integrity": "sha512-RdwNCyMsNBftLjW6w01z8bKEvT6e/5tpPVEgtn22TiLGlstHOVecsX2KHFkD5e/vRnIE4EGzpuIODb3mtswtkw==", + "devOptional": true, "funding": [ { "type": "opencollective", @@ -8842,6 +8955,7 @@ "version": "3.44.0", "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.44.0.tgz", "integrity": "sha512-JepmAj2zfl6ogy34qfWtcE7nHKAJnKsQFRn++scjVS2bZFllwptzw61BZcZFYBPpUznLfAvh0LGhxKppk04ClA==", + "devOptional": true, "license": "MIT", "dependencies": { "browserslist": "^4.25.1" @@ -9927,6 +10041,7 @@ "version": "1.1.4", "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz", "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==", + "devOptional": true, "license": "MIT", "dependencies": { "es-define-property": "^1.0.0", @@ -9954,6 +10069,7 @@ "version": "1.2.1", "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.2.1.tgz", "integrity": "sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==", + "devOptional": true, "license": "MIT", "dependencies": { "define-data-property": "^1.0.1", @@ -10355,6 +10471,7 @@ "version": "1.5.321", "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.321.tgz", "integrity": "sha512-L2C7Q279W2D/J4PLZLk7sebOILDSWos7bMsMNN06rK482umHUrh/3lM8G7IlHFOYip2oAg5nha1rCMxr/rs6ZQ==", + "devOptional": true, "license": "ISC" }, "node_modules/emittery": { @@ -10676,6 +10793,7 @@ "version": "3.2.0", "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", + "devOptional": true, "license": "MIT", "engines": { "node": ">=6" @@ -11357,12 +11475,14 @@ "version": "2.0.2", "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz", "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==", + "dev": true, "license": "MIT" }, "node_modules/esutils": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "devOptional": true, "license": "BSD-2-Clause", "engines": { "node": ">=0.10.0" @@ -11659,9 +11779,9 @@ "license": "MIT" }, "node_modules/fast-uri": { - "version": "3.1.4", - "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.4.tgz", - "integrity": "sha512-8JnbkQ4juDyvYs4mgFGQqg4yCYtFDtUtmp2QIQq11ZZe5CFQ5wcqm1rqDgAh/QdMySuBnPzMUiJUNZG5N/AiQw==", + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.2.tgz", + "integrity": "sha512-rVjf7ArG3LTk+FS6Yw81V1DLuZl1bRbNrev6Tmd/9RaroeeRRJhAt7jg/6YFxbvAQXUCavSoZhPPj6oOx+5KjQ==", "devOptional": true, "funding": [ { @@ -12215,6 +12335,7 @@ "version": "1.0.0-beta.2", "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", + "devOptional": true, "license": "MIT", "engines": { "node": ">=6.9.0" @@ -12667,6 +12788,7 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz", "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==", + "devOptional": true, "license": "MIT", "dependencies": { "es-define-property": "^1.0.0" @@ -12726,9 +12848,9 @@ "license": "MIT" }, "node_modules/hasown": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", - "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.4.tgz", + "integrity": "sha512-T2UbfbBEF32wiepXIsMlTW9+dDYC6wMh/t/vYA4tuOMKqWz/n3vr1NFSxQiyP+zk2mXsoMA/i/7qV6LKut1t1A==", "license": "MIT", "dependencies": { "function-bind": "^1.1.2" @@ -12911,6 +13033,7 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/html-tags/-/html-tags-2.0.0.tgz", "integrity": "sha512-+Il6N8cCo2wB/Vd3gqy/8TZhTD3QvcVeQLCnZiGkGCH3JP28IgGAY41giccp2W4R3jfyJPAP318FQTa1yU7K7g==", + "dev": true, "license": "MIT", "engines": { "node": ">=4" @@ -13151,9 +13274,9 @@ } }, "node_modules/immutable": { - "version": "5.1.9", - "resolved": "https://registry.npmjs.org/immutable/-/immutable-5.1.9.tgz", - "integrity": "sha512-m8nVez3rwrgmWxtLMt1ZYXB2Lv7OKYn/disyxAlSDYAlKSlFoPPfIAmAM/M5xqL4m4C/wAPw7S2/CNaUii1Hxg==", + "version": "5.1.5", + "resolved": "https://registry.npmjs.org/immutable/-/immutable-5.1.5.tgz", + "integrity": "sha512-t7xcm2siw+hlUM68I+UEOK+z84RzmN59as9DZ7P1l0994DKUWV7UXBMQZVxaoMSRQ+PBZbHCOoBt7a2wxOMt+A==", "dev": true, "license": "MIT" }, @@ -13426,6 +13549,7 @@ "version": "2.16.1", "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.1.tgz", "integrity": "sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==", + "devOptional": true, "license": "MIT", "dependencies": { "hasown": "^2.0.2" @@ -15334,6 +15458,7 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "devOptional": true, "license": "MIT" }, "node_modules/js-yaml": { @@ -15785,6 +15910,7 @@ "version": "4.0.8", "resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz", "integrity": "sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==", + "devOptional": true, "license": "MIT" }, "node_modules/lodash.defaultsdeep": { @@ -15798,6 +15924,7 @@ "version": "4.1.1", "resolved": "https://registry.npmjs.org/lodash.kebabcase/-/lodash.kebabcase-4.1.1.tgz", "integrity": "sha512-N8XRTIMMqqDgSy4VLKPnJ/+hpGZN+PHQiJnSenYqPaVV/NCqEogTnAdZLQiGKhxX+JCs8waWq2t1XHWKOmlY8g==", + "dev": true, "license": "MIT" }, "node_modules/lodash.mapvalues": { @@ -15976,6 +16103,7 @@ "version": "0.30.17", "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.17.tgz", "integrity": "sha512-sNPKHvyjVf7gyjwS4xGTaW/mCnF8wnjtifKBEhxfZ7E/S8tQ0rssrwGNn6q8JH/ohItJfSQp9mBtQYuTlH5QnA==", + "dev": true, "license": "MIT", "dependencies": { "@jridgewell/sourcemap-codec": "^1.5.0" @@ -16555,6 +16683,7 @@ "version": "2.0.36", "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.36.tgz", "integrity": "sha512-TdC8FSgHz8Mwtw9g5L4gR/Sh9XhSP/0DEkQxfEFXOpiul5IiHgHan2VhYYb6agDSfp4KuvltmGApc8HMgUrIkA==", + "devOptional": true, "license": "MIT" }, "node_modules/nopt": { @@ -16748,6 +16877,7 @@ "version": "1.1.1", "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", + "devOptional": true, "license": "MIT", "engines": { "node": ">= 0.4" @@ -16757,6 +16887,7 @@ "version": "4.1.7", "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.7.tgz", "integrity": "sha512-nK28WOo+QIjBkDduTINE4JkF/UJJKyf2EJxvJKfblDpyg0Q+pkOHNTL0Qwy6NP6FhE/EnzV73BxxqcJaXY9anw==", + "dev": true, "license": "MIT", "dependencies": { "call-bind": "^1.0.8", @@ -17241,6 +17372,7 @@ "version": "1.0.7", "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", + "devOptional": true, "license": "MIT" }, "node_modules/path-scurry": { @@ -18767,12 +18899,14 @@ "version": "1.4.2", "resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.4.2.tgz", "integrity": "sha512-zrceR/XhGYU/d/opr2EKO7aRHUeiBI8qjtfHqADTwZd6Szfy16la6kqD0MIUs5z5hx6AaKa+PixpPrR289+I0A==", + "devOptional": true, "license": "MIT" }, "node_modules/regenerate-unicode-properties": { "version": "10.2.0", "resolved": "https://registry.npmjs.org/regenerate-unicode-properties/-/regenerate-unicode-properties-10.2.0.tgz", "integrity": "sha512-DqHn3DwbmmPVzeKj9woBadqmXxLvQoQIwu7nopMc72ztvxVmVk2SBhSnx67zuye5TP+lJsb/TBQsjLKhnDf3MA==", + "devOptional": true, "license": "MIT", "dependencies": { "regenerate": "^1.4.2" @@ -18813,6 +18947,7 @@ "version": "6.2.0", "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-6.2.0.tgz", "integrity": "sha512-H66BPQMrv+V16t8xtmq+UC0CBpiTBA60V8ibS1QVReIp8T1z8hwFxqcGzm9K6lgsN7sB5edVH8a+ze6Fqm4weA==", + "devOptional": true, "license": "MIT", "dependencies": { "regenerate": "^1.4.2", @@ -18830,12 +18965,14 @@ "version": "0.8.0", "resolved": "https://registry.npmjs.org/regjsgen/-/regjsgen-0.8.0.tgz", "integrity": "sha512-RvwtGe3d7LvWiDQXeQw8p5asZUmfU1G/l6WbUXeHta7Y2PEIvBTwH6E2EfmYUK8pxcxEdEmaomqyp0vZZ7C+3Q==", + "devOptional": true, "license": "MIT" }, "node_modules/regjsparser": { "version": "0.12.0", "resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.12.0.tgz", "integrity": "sha512-cnE+y8bz4NhMjISKbgeVJtqNbtf5QpjZP+Bslo+UqkIt9QPnX9q095eiRRASJG1/tz6dlNr6Z5NsBiWYokp6EQ==", + "devOptional": true, "license": "BSD-2-Clause", "dependencies": { "jsesc": "~3.0.2" @@ -18848,6 +18985,7 @@ "version": "3.0.2", "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.0.2.tgz", "integrity": "sha512-xKqzzWXDttJuOcawBt4KnKHHIf5oQ/Cxax+0PWFG+DFDgHNAdi+TXECADI+RYiFUMmx8792xsMbbgXj4CwnP4g==", + "devOptional": true, "license": "MIT", "bin": { "jsesc": "bin/jsesc" @@ -18981,6 +19119,7 @@ "version": "1.22.10", "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.10.tgz", "integrity": "sha512-NPRy+/ncIMeDlTAsuqwKIiferiawhefFJtkNSW0qZJEqMEb+qBt/77B/jGeeek+F0uOeN05CDa6HXbbIgtVX4w==", + "devOptional": true, "license": "MIT", "dependencies": { "is-core-module": "^2.16.0", @@ -19844,6 +19983,7 @@ "version": "1.2.2", "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz", "integrity": "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==", + "dev": true, "license": "MIT", "dependencies": { "define-data-property": "^1.1.4", @@ -20764,6 +20904,7 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", + "devOptional": true, "license": "MIT", "engines": { "node": ">= 0.4" @@ -20775,12 +20916,13 @@ "node_modules/svg-tags": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/svg-tags/-/svg-tags-1.0.0.tgz", - "integrity": "sha512-ovssysQTa+luh7A5Weu3Rta6FJlFBBbInjOh722LIt6klpU2/HtdUbszju/G4devcvk8PGt7FCLv5wftu3THUA==" + "integrity": "sha512-ovssysQTa+luh7A5Weu3Rta6FJlFBBbInjOh722LIt6klpU2/HtdUbszju/G4devcvk8PGt7FCLv5wftu3THUA==", + "dev": true }, "node_modules/svgo": { - "version": "2.8.3", - "resolved": "https://registry.npmjs.org/svgo/-/svgo-2.8.3.tgz", - "integrity": "sha512-5EZD0pafXX6PphdwOGCiVLDSaV1xyuQao2blHajHLsPxr07q4mmEjdtXEWgG07ae2mIz8Ex2CDXNCTiXhy3Khw==", + "version": "2.8.2", + "resolved": "https://registry.npmjs.org/svgo/-/svgo-2.8.2.tgz", + "integrity": "sha512-TyzE4NVGLUFy+H/Uy4N6c3G0HEeprsVfge6Lmq+0FdQQ/zqoVYB62IsBZORsiL+o96s6ff/V6/3UQo/C0cgCAA==", "dev": true, "license": "MIT", "dependencies": { @@ -21592,6 +21734,7 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-2.0.1.tgz", "integrity": "sha512-dA8WbNeb2a6oQzAQ55YlT5vQAWGV9WXOsi3SskE3bcCdM0P4SDd+24zS/OCacdRq5BkdsRj9q3Pg6YyQoxIGqg==", + "devOptional": true, "license": "MIT", "engines": { "node": ">=4" @@ -21601,6 +21744,7 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/unicode-match-property-ecmascript/-/unicode-match-property-ecmascript-2.0.0.tgz", "integrity": "sha512-5kaZCrbp5mmbz5ulBkDkbY0SsPOjKqVS35VpL9ulMPfSl0J0Xsm+9Evphv9CoIZFwre7aJoa94AY6seMKGVN5Q==", + "devOptional": true, "license": "MIT", "dependencies": { "unicode-canonical-property-names-ecmascript": "^2.0.0", @@ -21614,6 +21758,7 @@ "version": "2.2.0", "resolved": "https://registry.npmjs.org/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-2.2.0.tgz", "integrity": "sha512-4IehN3V/+kkr5YeSSDDQG8QLqO26XpL2XP3GQtqwlT/QYSECAwFztxVHjlbh0+gjJ3XmNLS0zDsbgs9jWKExLg==", + "devOptional": true, "license": "MIT", "engines": { "node": ">=4" @@ -21623,6 +21768,7 @@ "version": "2.1.0", "resolved": "https://registry.npmjs.org/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-2.1.0.tgz", "integrity": "sha512-6t3foTQI9qne+OZoVQB/8x8rk2k1eVy1gRXhV3oFQ5T6R1dqQ1xtin3XqSlx3+ATBkliTaR/hHyJBm+LVPNM8w==", + "devOptional": true, "license": "MIT", "engines": { "node": ">=4" @@ -21652,6 +21798,7 @@ "version": "1.2.3", "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.2.3.tgz", "integrity": "sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w==", + "devOptional": true, "funding": [ { "type": "opencollective", diff --git a/client/package.json b/client/package.json index 2f3e487e..ade04c8a 100644 --- a/client/package.json +++ b/client/package.json @@ -34,10 +34,10 @@ "types": "lib/types/index.d.ts", "dependencies": { "@flatten-js/interval-tree": "^1.0.11", - "@girder/components": "^3.2.0", + "@girder/components": "github:girder/girder_web_components#girder-5-axios-upgrade", "@mdi/font": "^6.2.95", "@mdi/js": "^7.4.47", - "axios": "^1.6.7", + "axios": "^1.17.0", "color-hash": "^1.0.3", "core-js": "^3.22.2", "csv-stringify": "^5.6.0", @@ -58,7 +58,6 @@ "vuex": "^3.0.1" }, "devDependencies": { - "@types/axios": "^0.14.0", "@types/body-parser": "^1.19.0", "@types/color-hash": "^1.0.0", "@types/cors": "^2.8.9", @@ -128,7 +127,7 @@ "tsc-alias": "^1.2.0", "tslib": "^2.6.2", "typescript": "~4.3.5", - "vite": "^6.3.5", + "vite": "^6.4.3", "vue-cli-plugin-vuetify": "^2.4.3", "vue-jest": "^3.0.7", "vue-template-compiler": "^2.7.0", diff --git a/client/platform/web-girder/api/dataset.service.ts b/client/platform/web-girder/api/dataset.service.ts index e155c289..ba5d59a3 100644 --- a/client/platform/web-girder/api/dataset.service.ts +++ b/client/platform/web-girder/api/dataset.service.ts @@ -139,6 +139,11 @@ async function importAnnotationFile(parentId: string, path: string, file?: HTMLF skipJobs = true; } const final = await postProcess(parentId, skipJobs, false, additive, additivePrepend, maskLogic); + if ((final.data?.job_ids || []).length) { + // Lazy import avoids circular deps with the Vuex store module graph. + const { default: store } = await import('platform/web-girder/store'); + store.dispatch('Jobs/updateJobs'); + } return final.status === 200; } } diff --git a/client/platform/web-girder/api/divemetadata.service.ts b/client/platform/web-girder/api/divemetadata.service.ts index 9fc481fa..b2fccfb4 100644 --- a/client/platform/web-girder/api/divemetadata.service.ts +++ b/client/platform/web-girder/api/divemetadata.service.ts @@ -224,6 +224,7 @@ export interface createDiveMetadataRecursiveResponse { export interface IndexDiveMetadataFolderResponse { results: string; metadataFolderId: string; + folderId?: string; rootFolderId: string; added: number; existing: number; @@ -232,6 +233,125 @@ export interface IndexDiveMetadataFolderResponse { combineMetadataFolders: boolean; } +/** Girder job returned immediately by metadata ingest endpoints. */ +export interface DiveMetadataIngestJob { + _id: string; + title?: string; + type?: string; + status: number; + log?: string; + progress?: { current: number; total: number }; + meta?: { + diveMetadataIngestResult?: Record; + }; +} + +const TERMINAL_JOB_STATUSES = new Set([ + // Girder JobStatus: SUCCESS=3, ERROR=4, CANCELED=5 (numeric values from @girder/components) + 3, 4, 5, +]); + +async function waitForMetadataIngestJob( + jobId: string, + options?: { + intervalMs?: number; + onProgress?: (job: DiveMetadataIngestJob) => void; + }, +): Promise> { + const intervalMs = options?.intervalMs ?? 2000; + + const finishFromJob = (job: DiveMetadataIngestJob): Record => { + if (job.status !== 3) { + const logTail = (job.log || '').trim().split('\n').slice(-5).join('\n'); + throw new Error( + logTail || `Metadata job ${jobId} failed (status=${job.status})`, + ); + } + return (job.meta?.diveMetadataIngestResult || {}) as Record; + }; + + const fetchJob = async (): Promise => { + const { data: job } = await girderRest.get(`job/${jobId}`); + options?.onProgress?.(job); + return job; + }; + + return new Promise>((resolve, reject) => { + let settled = false; + let pollTimer: ReturnType | null = null; + + const cleanup = () => { + if (pollTimer) { + clearInterval(pollTimer); + pollTimer = null; + } + girderRest.$off('message:job_status', onJobMessage); + }; + + const settle = (fn: () => void) => { + if (settled) { + return; + } + settled = true; + cleanup(); + try { + fn(); + } catch (err) { + reject(err); + } + }; + + const handleTerminal = async () => { + try { + // Status events often omit log/meta; re-fetch for the ingest result payload. + const job = await fetchJob(); + if (!TERMINAL_JOB_STATUSES.has(job.status)) { + return; + } + settle(() => { + resolve(finishFromJob(job)); + }); + } catch (err) { + settle(() => { + reject(err); + }); + } + }; + + const onJobMessage = ({ data: job }: { data: DiveMetadataIngestJob }) => { + if (job._id !== jobId) { + return; + } + options?.onProgress?.(job); + if (TERMINAL_JOB_STATUSES.has(job.status)) { + handleTerminal(); + } + }; + + girderRest.$on('message:job_status', onJobMessage); + + const pollOnce = async () => { + try { + const job = await fetchJob(); + if (!TERMINAL_JOB_STATUSES.has(job.status)) { + return; + } + settle(() => { + resolve(finishFromJob(job)); + }); + } catch (err) { + settle(() => { + reject(err); + }); + } + }; + + // Immediate check + polling fallback if websocket events are missed. + pollOnce(); + pollTimer = setInterval(pollOnce, intervalMs); + }); +} + function createDiveMetadataFolder( parentFolder: string, name: string, @@ -245,7 +365,7 @@ function createDiveMetadataFolder( import: true, keys: ['width', 'height', 'display_aspect_ratio', 'nb_frames', 'duration'], }, ) { - return girderRest.post(`dive_metadata/create_metadata_folder/${parentFolder}`, null, { + return girderRest.post(`dive_metadata/create_metadata_folder/${parentFolder}`, null, { params: { name, rootFolderId, @@ -257,6 +377,14 @@ function createDiveMetadataFolder( }); } +async function createDiveMetadataFolderAndWait( + ...args: Parameters +): Promise { + const { data: job } = await createDiveMetadataFolder(...args); + const result = await waitForMetadataIngestJob(job._id); + return result as unknown as createDiveMetadataResponse; +} + function createDiveMetadataRecursive( resourceId: string, resourceType: 'folder' | 'collection', @@ -271,7 +399,7 @@ function createDiveMetadataRecursive( import: true, keys: ['width', 'height', 'display_aspect_ratio', 'nb_frames', 'duration'], }, ) { - return girderRest.post('dive_metadata/create_metadata_recursive', null, { + return girderRest.post('dive_metadata/create_metadata_recursive', null, { params: { resourceId, resourceType, @@ -285,6 +413,14 @@ function createDiveMetadataRecursive( }); } +async function createDiveMetadataRecursiveAndWait( + ...args: Parameters +): Promise { + const { data: job } = await createDiveMetadataRecursive(...args); + const result = await waitForMetadataIngestJob(job._id); + return result as unknown as createDiveMetadataRecursiveResponse; +} + function indexDiveMetadataFromFolder( metadataFolderId: string, rootFolderId: string, @@ -294,7 +430,7 @@ function indexDiveMetadataFromFolder( import: true, keys: ['width', 'height', 'display_aspect_ratio', 'nb_frames', 'duration'], }, ) { - return girderRest.post( + return girderRest.post( `dive_metadata/${metadataFolderId}/index_folder`, null, { @@ -308,6 +444,14 @@ function indexDiveMetadataFromFolder( ); } +async function indexDiveMetadataFromFolderAndWait( + ...args: Parameters +): Promise { + const { data: job } = await indexDiveMetadataFromFolder(...args); + const result = await waitForMetadataIngestJob(job._id); + return result as unknown as IndexDiveMetadataFolderResponse; +} + function modifyDiveMetadataPermission(rootMetadataFolder: string, key: string, unlocked: boolean) { return girderRest.patch(`dive_metadata/${rootMetadataFolder}/modify_key_permission`, null, { params: { @@ -471,63 +615,21 @@ async function putDiveMetadataLastModified(folderId:string, rootId: string) { return girderRest.put(`dive_metadata/${folderId}/last_modified`, null, { params: { rootId } }); } -export interface BulkMetadataImportRowResult { - matcher?: string; - status: 'success' | 'partial_success' | 'error' | 'not_found'; - datasetId?: string; - updatedKeys?: string[]; - errors?: string[]; - error?: string; -} - -export interface BulkMetadataImportSummary { - updatedCount: number; - notFoundCount: number; - errorCount: number; - totalCount: number; - results: BulkMetadataImportRowResult[]; -} - -function summarizeBulkImportResults( - results: BulkMetadataImportRowResult[], -): BulkMetadataImportSummary { - let updatedCount = 0; - let notFoundCount = 0; - let errorCount = 0; - results.forEach((row) => { - if (row.status === 'success' || row.status === 'partial_success') { - updatedCount += 1; - } else if (row.status === 'not_found') { - notFoundCount += 1; - } else { - errorCount += 1; - } - }); - return { - updatedCount, - notFoundCount, - errorCount, - totalCount: results.length, - results, - }; -} - async function processImportedFile(rootId:string, replace = false) { - return girderRest.post(`dive_metadata/bulk_update_file/${rootId}`, null, { params: { replace } }); + return girderRest.post( + `dive_metadata/bulk_update_file/${rootId}`, + null, + { params: { replace } }, + ); } interface HTMLFile extends File { webkitRelativePath?: string; } -async function importMetadataFile( - parentId: string, - path: string, - file?: HTMLFile, - replace = false, -): Promise { +async function importMetadataFile(parentId: string, path: string, file?: HTMLFile, replace = false) { if (file === undefined) { - return false; + return false as const; } const resp = await girderRest.post('/file', null, { params: { @@ -547,13 +649,11 @@ async function importMetadataFile( headers: { 'Content-Type': 'application/octet-stream' }, }); if (uploadResponse.status === 200) { - const final = await processImportedFile(parentId, replace); - if (final.status === 200) { - return summarizeBulkImportResults(final.data as BulkMetadataImportRowResult[]); - } + const { data: job } = await processImportedFile(parentId, replace); + return job; } } - return false; + return false as const; } export { @@ -562,8 +662,12 @@ export { getDiveDatasetMetadataRow, createDiveMetadataClone, createDiveMetadataFolder, + createDiveMetadataFolderAndWait, createDiveMetadataRecursive, + createDiveMetadataRecursiveAndWait, indexDiveMetadataFromFolder, + indexDiveMetadataFromFolderAndWait, + waitForMetadataIngestJob, modifyDiveMetadataPermission, addDiveMetadataKey, updateDiveMetadataKeyDescription, @@ -579,5 +683,4 @@ export { putDiveMetadataLastModified, processImportedFile, importMetadataFile, - summarizeBulkImportResults, }; diff --git a/client/platform/web-girder/api/index.ts b/client/platform/web-girder/api/index.ts index 960997b9..e9ff7e96 100644 --- a/client/platform/web-girder/api/index.ts +++ b/client/platform/web-girder/api/index.ts @@ -1,5 +1,5 @@ /* eslint-disable @typescript-eslint/no-explicit-any */ -import { AxiosRequestConfig, AxiosResponse } from '@girder/components/node_modules/axios'; +import { AxiosRequestConfig, AxiosResponse } from 'axios'; import girderRest from 'platform/web-girder/plugins/girder'; export * from './annotation.service'; diff --git a/client/platform/web-girder/main.ts b/client/platform/web-girder/main.ts index 96798317..2815f9e6 100644 --- a/client/platform/web-girder/main.ts +++ b/client/platform/web-girder/main.ts @@ -41,4 +41,6 @@ Promise.all([ /** Start notification stream if everything else succeeds */ registerNotifications(girderRest).connect(); + // Jobs.init may have run before auth; refresh running-job UI now that user is loaded. + store.dispatch('Jobs/updateJobs').catch(() => { /* ignore */ }); }); diff --git a/client/platform/web-girder/store/Jobs.ts b/client/platform/web-girder/store/Jobs.ts index 60ee72ae..861b4d97 100644 --- a/client/platform/web-girder/store/Jobs.ts +++ b/client/platform/web-girder/store/Jobs.ts @@ -14,6 +14,34 @@ const NonRunningStates = [ JobStatus.SUCCESS.value, ]; +type DiveGirderJob = GirderJob & { type?: string; title?: string; dataset_id?: string }; + +function datasetIdOf(job: DiveGirderJob): string | null { + if (job.dataset_id == null || job.dataset_id === '') { + return null; + } + return String(job.dataset_id); +} + +function applyJobUpdate( + commit: (type: string, payload?: unknown) => void, + job: DiveGirderJob, +) { + commit('setJobState', { jobId: job._id, value: job.status }); + const datasetId = datasetIdOf(job); + if (datasetId) { + commit('setDatasetStatus', { datasetId, status: job.status, jobId: job._id }); + if (NonRunningStates.includes(job.status)) { + commit('setCompleteJobsInfo', { + datasetId, + type: job.type, + title: job.title, + success: job.status === JobStatus.SUCCESS.value, + }); + } + } +} + const jobModule: Module = { namespaced: true, state: { @@ -63,43 +91,51 @@ const jobModule: Module = { removeCompleteJob({ commit }, { datasetId }: {datasetId: string}) { commit('removeCompleteJobsInfo', { datasetId }); }, + /** + * Optimistically mark jobs as running so Jobs tab / folder spinners update + * immediately after postprocess. Live status still comes from websockets + * (same as ../dive useJobs), not from polling. + */ + trackJobs({ commit }, { + jobIds, datasetId, status = JobStatus.QUEUED.value, + }: { jobIds: string[]; datasetId?: string; status?: number }) { + jobIds.forEach((jobId) => { + const id = String(jobId); + commit('setJobState', { jobId: id, value: status }); + if (datasetId) { + commit('setDatasetStatus', { datasetId: String(datasetId), status, jobId: id }); + } + }); + }, updateJobs({ commit }) { const getJobs = async () => { - const { data: runningJobs } = await girderRest.get('/job', { - params: { statuses: `[${JobStatus.RUNNING.value}, ${JobStatus.QUEUED.value}, ${JobStatus.INACTIVE.value}]` }, + const { data: runningJobs } = await girderRest.get('/job', { + params: { + statuses: `[${JobStatus.RUNNING.value}, ${JobStatus.QUEUED.value}, ${JobStatus.INACTIVE.value}]`, + }, }); - const updateJob = (job: GirderJob & {type?: string; title?: string}) => { - commit('setJobState', { jobId: job._id, value: job.status }); - if (typeof job.dataset_id === 'string') { - commit('setDatasetStatus', { datasetId: job.dataset_id, status: job.status, jobId: job._id }); - if (NonRunningStates.includes(job.status)) { - commit('setCompleteJobsInfo', { - datasetId: job.dataset_id, - type: job.type, - title: job.title, - success: job.status === JobStatus.SUCCESS.value, - }); - } - } - }; - runningJobs.forEach(updateJob); + runningJobs.forEach((job) => applyJobUpdate(commit, job)); }; - getJobs(); + return getJobs(); }, }, }; +/** + * Match ../dive useJobs.initJobs: seed from GET /job, then rely on + * girderRest `message:job_status` websocket events (no polling). + * Every status event refreshes the data browser so Launch Annotator appears + * when convert sets meta.annotate. + */ export async function init(store: Store) { - const { data: runningJobs } = await girderRest.get('/job', { - params: { statuses: `[${JobStatus.RUNNING.value}, ${JobStatus.QUEUED.value}, ${JobStatus.INACTIVE.value}]` }, - }); - function updateJob(job: GirderJob & {type?: string; title?: string}) { + function updateJob(job: DiveGirderJob) { store.commit('Jobs/setJobState', { jobId: job._id, value: job.status }); - if (typeof job.dataset_id === 'string') { - store.commit('Jobs/setDatasetStatus', { datasetId: job.dataset_id, status: job.status, jobId: job._id }); + const datasetId = datasetIdOf(job); + if (datasetId) { + store.commit('Jobs/setDatasetStatus', { datasetId, status: job.status, jobId: job._id }); if (job.type !== undefined && NonRunningStates.includes(job.status)) { store.commit('Jobs/setCompleteJobsInfo', { - datasetId: job.dataset_id, + datasetId, type: job.type, title: job.title, success: job.status === JobStatus.SUCCESS.value, @@ -108,12 +144,21 @@ export async function init(store: Store) { } } - runningJobs.forEach(updateJob); - - girderRest.$on('message:job_status', ({ data: job }: { data: GirderJob }) => { + girderRest.$on('message:job_status', ({ data: job }: { data: DiveGirderJob }) => { updateJob(job); eventBus.$emit('refresh-data-browser'); }); + + try { + const { data: runningJobs } = await girderRest.get('/job', { + params: { + statuses: `[${JobStatus.RUNNING.value}, ${JobStatus.QUEUED.value}, ${JobStatus.INACTIVE.value}]`, + }, + }); + runningJobs.forEach(updateJob); + } catch { + // Token may not be ready yet; login / main bootstrap will refetch. + } } export default jobModule; diff --git a/client/platform/web-girder/utils/metadataIngestJobUi.ts b/client/platform/web-girder/utils/metadataIngestJobUi.ts new file mode 100644 index 00000000..5e2e06df --- /dev/null +++ b/client/platform/web-girder/utils/metadataIngestJobUi.ts @@ -0,0 +1,208 @@ +/** + * Client UX for metadata ingest jobs: launch dialog with job link, poll until + * complete, then status dialog with optional navigate-to-folder action. + */ +import type { PromptParams } from 'dive-common/vue-utilities/prompt-service'; +import type { DiveMetadataIngestJob } from 'platform/web-girder/api/divemetadata.service'; +import { waitForMetadataIngestJob } from 'platform/web-girder/api/divemetadata.service'; + +type PromptFn = (params: PromptParams) => Promise; + +export function metadataJobUrl(jobId: string): string { + return `/girder/#job/${jobId}`; +} + +export function metadataJobAbsoluteUrl(jobId: string): string { + if (typeof window === 'undefined') { + return metadataJobUrl(jobId); + } + return `${window.location.origin}${metadataJobUrl(jobId)}`; +} + +export function summarizeIngestResult(result: Record): string[] { + const lines: string[] = []; + if (typeof result.results === 'string' && result.results) { + lines.push(result.results); + } + // Bulk update compact summary from worker + if (result.results && typeof result.results === 'object' && !Array.isArray(result.results)) { + const bulk = result.results as { rowCount?: number; statusCounts?: Record }; + if (bulk.rowCount != null) { + const counts = bulk.statusCounts || {}; + const updated = (counts.success || 0) + (counts.partial_success || 0); + const notFound = counts.not_found || 0; + const errors = Object.entries(counts) + .filter(([status]) => !['success', 'partial_success', 'not_found'].includes(status)) + .reduce((sum, [, count]) => sum + count, 0); + lines.push( + `Updated ${updated} of ${bulk.rowCount} DIVEMetadata item${bulk.rowCount === 1 ? '' : 's'}.`, + ); + if (notFound > 0) { + lines.push(`${notFound} row${notFound === 1 ? '' : 's'} not found.`); + } + if (errors > 0) { + lines.push(`${errors} row${errors === 1 ? '' : 's'} failed with errors.`); + } + } else if (bulk.statusCounts) { + Object.entries(bulk.statusCounts).forEach(([status, count]) => { + lines.push(` ${status}: ${count}`); + }); + } + } + if (typeof result.metadataFoldersFound === 'number') { + lines.push(`DIVE Metadata folders found: ${result.metadataFoldersFound}`); + } + if (typeof result.added === 'number') { + lines.push(`Added: ${result.added}`); + } + if (typeof result.existing === 'number') { + lines.push(`Existing / skipped: ${result.existing}`); + } + if (typeof result.datasetCount === 'number') { + lines.push(`Datasets scanned: ${result.datasetCount}`); + } + if (typeof result.rowCount === 'number') { + lines.push(`Rows processed: ${result.rowCount}`); + } + if (typeof result.dataFileName === 'string' && result.dataFileName) { + lines.push(`Source file: ${result.dataFileName}`); + } + if (Array.isArray(result.created)) { + lines.push(`Metadata folders created/updated: ${result.created.length}`); + } + if (Array.isArray(result.existing)) { + lines.push(`Existing metadata entries: ${result.existing.length}`); + } + const { errors } = result; + if (Array.isArray(errors) && errors.length) { + lines.push(`Errors: ${errors.length}`); + errors.slice(0, 3).forEach((err) => { + lines.push(` - ${String(err)}`); + }); + if (errors.length > 3) { + lines.push(` … and ${errors.length - 3} more`); + } + } + if (!lines.length) { + lines.push('Metadata processing completed successfully.'); + } + return lines; +} + +export function resolveMetadataFolderId(result: Record): string | null { + if (typeof result.folderId === 'string' && result.folderId) { + return result.folderId; + } + if (typeof result.metadataFolderId === 'string' && result.metadataFolderId) { + return result.metadataFolderId; + } + const { created } = result; + if (Array.isArray(created)) { + const hit = created.find( + (entry) => entry && typeof entry === 'object' + && typeof (entry as { metadataFolderId?: string }).metadataFolderId === 'string', + ) as { metadataFolderId?: string } | undefined; + if (hit?.metadataFolderId) { + return hit.metadataFolderId; + } + } + const { existing } = result; + if (Array.isArray(existing)) { + const hit = existing.find( + (entry) => entry && typeof entry === 'object' + && typeof (entry as { metadataFolderId?: string }).metadataFolderId === 'string', + ) as { metadataFolderId?: string } | undefined; + if (hit?.metadataFolderId) { + return hit.metadataFolderId; + } + } + return null; +} + +export interface NotifyMetadataIngestJobOptions { + job: DiveMetadataIngestJob; + prompt: PromptFn; + /** Vue router with a push method (optional; used for Open Metadata). */ + router?: { push: (location: { name: string; params: { id: string } }) => unknown }; + /** Used when the job result does not include a folder id (e.g. bulk import). */ + fallbackFolderId?: string; + /** Called after a successful job (before or after the completion dialog). */ + onSuccess?: (result: Record, folderId: string | null) => void; +} + +/** + * Show launch dialog with job link, watch until terminal status, then show + * completion / failure dialog with optional navigation to the metadata folder. + */ +export async function notifyAndWatchMetadataIngestJob( + options: NotifyMetadataIngestJobOptions, +): Promise | null> { + const { + job, prompt, router, fallbackFolderId, onSuccess, + } = options; + const jobUrl = metadataJobUrl(job._id); + const jobAbs = metadataJobAbsoluteUrl(job._id); + + const resultPromise = waitForMetadataIngestJob(job._id); + + const openJob = await prompt({ + title: 'Metadata processing started', + text: [ + 'Metadata processing has been launched as a background job.', + `Title: ${job.title || 'DIVE Metadata'}`, + '', + 'Open the job page to follow progress and logs:', + jobAbs, + ], + confirm: true, + positiveButton: 'Open Job', + negativeButton: 'Continue', + maxWidth: '560px', + }); + if (openJob) { + window.open(jobUrl, '_blank'); + } + + try { + const result = await resultPromise; + const folderId = resolveMetadataFolderId(result) || fallbackFolderId || null; + const text = [ + ...summarizeIngestResult(result), + ]; + if (folderId) { + text.push('', 'You can open the metadata folder to review the results.'); + } + const goToFolder = await prompt({ + title: 'Metadata processing complete', + text, + confirm: !!folderId, + positiveButton: folderId ? 'Open Metadata' : 'OK', + negativeButton: folderId ? 'Dismiss' : 'Cancel', + maxWidth: '560px', + }); + onSuccess?.(result, folderId); + if (goToFolder && folderId && router) { + await router.push({ name: 'metadata', params: { id: folderId } }); + } + return result; + } catch (err) { + const message = err instanceof Error ? err.message : String(err); + const openFailedJob = await prompt({ + title: 'Metadata processing failed', + text: [ + message, + '', + 'Open the job page for full logs:', + jobAbs, + ], + confirm: true, + positiveButton: 'Open Job', + negativeButton: 'Dismiss', + maxWidth: '560px', + }); + if (openFailedJob) { + window.open(jobUrl, '_blank'); + } + return null; + } +} diff --git a/client/platform/web-girder/views/CreateDIVEMetadata.vue b/client/platform/web-girder/views/CreateDIVEMetadata.vue index e62360df..d1c496eb 100644 --- a/client/platform/web-girder/views/CreateDIVEMetadata.vue +++ b/client/platform/web-girder/views/CreateDIVEMetadata.vue @@ -13,6 +13,7 @@ import { createDiveMetadataRecursive, } from 'platform/web-girder/api/divemetadata.service'; import { usePrompt } from 'dive-common/vue-utilities/prompt-service'; +import { notifyAndWatchMetadataIngestJob } from 'platform/web-girder/utils/metadataIngestJobUi'; import type { GirderModel } from 'vue-girder-slicer-cli-ui/dist/girderTypes'; import eventBus from '../eventBus'; @@ -48,8 +49,8 @@ export default defineComponent({ setup(props) { const router = useRouter(); - const girderRest = useGirderRest(); const { prompt } = usePrompt(); + const girderRest = useGirderRest(); const source = ref(null as GirderModel | { _id: string; _modelType: string; name: string } | null); const open = ref(false); const categoricalLimit = ref(50); @@ -122,57 +123,43 @@ export default defineComponent({ throw new Error('no source resource'); } if (useRecursiveApi.value) { - const result = await createDiveMetadataRecursive( + const { data: job } = await createDiveMetadataRecursive( props.datasetId, isCollection.value ? 'collection' : 'folder', effectiveScope.value, newName.value, categoricalLimit.value, ); - const { created, existing, errors } = result.data; - const hasWork = created.length > 0 - || existing.some((entry) => entry.metadataFolderId); - if (!hasWork) { - throw new Error( - errors.join('; ') - || 'No metadata folders were created or updated', - ); - } - if (effectiveScope.value === 'subfolders') { - open.value = false; - eventBus.$emit('refresh-data-browser'); - return; - } - const first = created.find((entry) => entry.metadataFolderId) - || existing.find((entry) => entry.metadataFolderId); open.value = false; - router.push({ name: 'metadata', params: { id: first!.metadataFolderId! } }); + await notifyAndWatchMetadataIngestJob({ + job, + prompt, + router, + onSuccess: () => { + eventBus.$emit('refresh-data-browser'); + }, + }); return; } if (!locationIsFolder.value) { throw new Error('Choose a destination folder'); } - const newDataset = await createDiveMetadataFolder( + const { data: job } = await createDiveMetadataFolder( location.value._id, newName.value, props.datasetId, categoricalLimit.value, combineMetadataFolders.value, ); - const { folderId } = newDataset.data; open.value = false; - if (combineMetadataFolders.value) { - await prompt({ - title: 'DIVE Metadata folders combined', - text: [ - `DIVE Metadata folders found: ${newDataset.data.metadataFoldersFound ?? 0}`, - `Datasets added: ${newDataset.data.added ?? 0}`, - `Datasets skipped (already present): ${newDataset.data.existing ?? 0}`, - ], - positiveButton: 'OK', - }); - } - router.push({ name: 'metadata', params: { id: folderId } }); + await notifyAndWatchMetadataIngestJob({ + job, + prompt, + router, + onSuccess: () => { + eventBus.$emit('refresh-data-browser'); + }, + }); }); return { diff --git a/client/platform/web-girder/views/DIVEMetadata/DIVEMetadataImport.vue b/client/platform/web-girder/views/DIVEMetadata/DIVEMetadataImport.vue index ae2843d3..dc479c86 100644 --- a/client/platform/web-girder/views/DIVEMetadata/DIVEMetadataImport.vue +++ b/client/platform/web-girder/views/DIVEMetadata/DIVEMetadataImport.vue @@ -3,10 +3,9 @@ import { defineComponent, ref } from 'vue'; import { useApi } from 'dive-common/apispec'; import { usePrompt } from 'dive-common/vue-utilities/prompt-service'; import { getResponseError } from 'vue-media-annotator/utils'; -import { - BulkMetadataImportSummary, - importMetadataFile, -} from 'platform/web-girder/api/divemetadata.service'; +import { importMetadataFile } from 'platform/web-girder/api/divemetadata.service'; +import { notifyAndWatchMetadataIngestJob } from 'platform/web-girder/utils/metadataIngestJobUi'; +import { useRouter } from 'vue-router/composables'; export default defineComponent({ name: 'DIVEMetadataImport', @@ -36,23 +35,10 @@ export default defineComponent({ setup(props, { emit }) { const { openFromDisk } = useApi(); const { prompt } = usePrompt(); + const router = useRouter(); const processing = ref(false); const menuOpen = ref(false); const replace = ref(false); - - const importSummaryText = (summary: BulkMetadataImportSummary) => { - const lines = [ - `Updated ${summary.updatedCount} of ${summary.totalCount} DIVEMetadata item${summary.totalCount === 1 ? '' : 's'}.`, - ]; - if (summary.notFoundCount > 0) { - lines.push(`${summary.notFoundCount} row${summary.notFoundCount === 1 ? '' : 's'} not found.`); - } - if (summary.errorCount > 0) { - lines.push(`${summary.errorCount} row${summary.errorCount === 1 ? '' : 's'} failed with errors.`); - } - return lines; - }; - const openUpload = async () => { try { const ret = await openFromDisk('annotation'); @@ -60,20 +46,31 @@ export default defineComponent({ menuOpen.value = false; const path = ret.filePaths[0]; processing.value = true; - const importResult = await importMetadataFile( - props.metadataRoot, - path, - ret.fileList?.length ? ret.fileList[0] : undefined, - replace.value, - ); - if (importResult) { - emit('updated'); - await prompt({ - title: 'Import Complete', - text: importSummaryText(importResult), - positiveButton: 'OK', + const job = ret.fileList?.length + ? await importMetadataFile( + props.metadataRoot, + path, + ret.fileList[0], + replace.value, + ) + : await importMetadataFile( + props.metadataRoot, + path, + undefined, + replace.value, + ); + if (job) { + await notifyAndWatchMetadataIngestJob({ + job, + prompt, + router, + fallbackFolderId: props.metadataRoot, + onSuccess: () => { + emit('updated'); + }, }); } + processing.value = false; } // eslint-disable-next-line @typescript-eslint/no-explicit-any } catch (error: any) { @@ -83,7 +80,6 @@ export default defineComponent({ text, positiveButton: 'OK', }); - } finally { processing.value = false; } }; diff --git a/client/platform/web-girder/views/DIVEMetadata/DIVEMetadataIndexFolder.vue b/client/platform/web-girder/views/DIVEMetadata/DIVEMetadataIndexFolder.vue index 84677498..3c738d0b 100644 --- a/client/platform/web-girder/views/DIVEMetadata/DIVEMetadataIndexFolder.vue +++ b/client/platform/web-girder/views/DIVEMetadata/DIVEMetadataIndexFolder.vue @@ -1,6 +1,6 @@